diff options
Diffstat (limited to 'libdw/libdwP.h')
| -rw-r--r-- | libdw/libdwP.h | 648 |
1 files changed, 607 insertions, 41 deletions
diff --git a/libdw/libdwP.h b/libdw/libdwP.h index 6ad322c1..eebb7d12 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -1,7 +1,6 @@ -/* Internal definitions for libdwarf. - Copyright (C) 2002-2011, 2013-2016 Red Hat, Inc. +/* Internal definitions for libdw. + Copyright (C) 2002-2011, 2013-2018 Red Hat, Inc. This file is part of elfutils. - Written by Ulrich Drepper <[email protected]>, 2002. This file is free software; you can redistribute it and/or modify it under the terms of either @@ -74,14 +73,19 @@ enum IDX_debug_types, IDX_debug_abbrev, IDX_debug_aranges, + IDX_debug_addr, IDX_debug_line, + IDX_debug_line_str, IDX_debug_frame, IDX_debug_loc, + IDX_debug_loclists, IDX_debug_pubnames, IDX_debug_str, + IDX_debug_str_offsets, IDX_debug_macinfo, IDX_debug_macro, IDX_debug_ranges, + IDX_debug_rnglists, IDX_gnu_debugaltlink, IDX_last }; @@ -108,6 +112,9 @@ enum DWARF_E_NO_ENTRY, DWARF_E_INVALID_DWARF, DWARF_E_NO_STRING, + DWARF_E_NO_DEBUG_STR, + DWARF_E_NO_DEBUG_LINE_STR, + DWARF_E_NO_STR_OFFSETS, DWARF_E_NO_ADDR, DWARF_E_NO_CONSTANT, DWARF_E_NO_REFERENCE, @@ -118,7 +125,9 @@ enum DWARF_E_VERSION, DWARF_E_INVALID_DIR_IDX, DWARF_E_ADDR_OUTOFRANGE, - DWARF_E_NO_LOCLIST, + DWARF_E_NO_DEBUG_LOC, + DWARF_E_NO_DEBUG_LOCLISTS, + DWARF_E_NO_LOC_VALUE, DWARF_E_NO_BLOCK, DWARF_E_INVALID_LINE_IDX, DWARF_E_INVALID_ARANGE_IDX, @@ -126,11 +135,13 @@ enum DWARF_E_NO_FLAG, DWARF_E_INVALID_OFFSET, DWARF_E_NO_DEBUG_RANGES, + DWARF_E_NO_DEBUG_RNGLISTS, DWARF_E_INVALID_CFI, DWARF_E_NO_ALT_DEBUGLINK, DWARF_E_INVALID_OPCODE, DWARF_E_NOT_CUDIE, DWARF_E_UNKNOWN_LANGUAGE, + DWARF_E_NO_DEBUG_ADDR, }; @@ -142,6 +153,10 @@ struct Dwarf /* The underlying ELF file. */ Elf *elf; + /* The (absolute) path to the ELF dir, if known. To help locating + alt and dwo files. */ + char *debugdir; + /* dwz alternate DWARF file. */ Dwarf *alt_dwarf; @@ -154,6 +169,10 @@ struct Dwarf /* If true, we allocated the ELF descriptor ourselves. */ bool free_elf; + /* If >= 0, we allocated the alt_dwarf ourselves and must end it and + close this file descriptor. */ + int alt_fd; + /* Information for traversing the .debug_pubnames section. This is an array and separately allocated with malloc. */ struct pubnames_s @@ -174,6 +193,9 @@ struct Dwarf Dwarf_Off next_tu_offset; Dwarf_Sig8_Hash sig8_hash; + /* Search tree for split Dwarf associated with CUs in this debug. */ + void *split_tree; + /* Search tree for .debug_macro operator tables. */ void *macro_ops; @@ -187,8 +209,14 @@ struct Dwarf struct Dwarf_CFI_s *cfi; /* Fake loc CU. Used when synthesizing attributes for Dwarf_Ops that - came from a location list entry in dwarf_getlocation_attr. */ + came from a location list entry in dwarf_getlocation_attr. + Depending on version this is the .debug_loc or .debug_loclists + section (could be both if mixing CUs with different DWARF versions). */ struct Dwarf_CU *fake_loc_cu; + struct Dwarf_CU *fake_loclists_cu; + + /* Similar for addrx/constx, which will come from .debug_addr section. */ + struct Dwarf_CU *fake_addr_cu; /* Internal memory handling. This is basically a simplified reimplementation of obstacks. Unfortunately the standard obstack @@ -212,13 +240,12 @@ struct Dwarf /* Abbreviation representation. */ struct Dwarf_Abbrev { - Dwarf_Off offset; - unsigned char *attrp; - unsigned int attrcnt; - unsigned int code; - unsigned int tag; - bool has_children; -}; + Dwarf_Off offset; /* Offset to start of abbrev into .debug_abbrev. */ + unsigned char *attrp; /* Pointer to start of attribute name/form pairs. */ + bool has_children : 1; /* Whether or not the DIE has children. */ + unsigned int code : 31; /* The (unique) abbrev code. */ + unsigned int tag; /* The tag of the DIE. */ +} attribute_packed; #include "dwarf_abbrev_hash.h" @@ -293,9 +320,23 @@ struct Dwarf_CU uint8_t offset_size; uint16_t version; - /* Zero if this is a normal CU. Nonzero if it is a type unit. */ - size_t type_offset; - uint64_t type_sig8; + size_t sec_idx; /* Normally .debug_info, could be .debug_type or "fake". */ + + /* The unit type if version >= 5. Otherwise 0 for normal CUs (from + .debug_info) or 1 for v4 type units (from .debug_types). */ + uint8_t unit_type; + + /* Zero if the unit type doesn't support a die/type offset and/or id/sig. + Nonzero if it is a v4 type unit or for DWARFv5 units depending on + unit_type. */ + size_t subdie_offset; + uint64_t unit_id8; + + /* If this is a skeleton unit this points to the split compile unit. + Or the other way around if this is a split compile unit. Set to -1 + if not yet searched. Always use __libdw_find_split_unit to access + this field. */ + struct Dwarf_CU *split; /* Hash table for the abbreviations. */ Dwarf_Abbrev_Hash abbrev_hash; @@ -313,13 +354,57 @@ struct Dwarf_CU /* Known location lists. */ void *locs; + /* Base address for use with ranges and locs. + Don't access directly, call __libdw_cu_base_address. */ + Dwarf_Addr base_address; + + /* The offset into the .debug_addr section where index zero begins. + Don't access directly, call __libdw_cu_addr_base. */ + Dwarf_Off addr_base; + + /* The offset into the .debug_str_offsets section where index zero begins. + Don't access directly, call __libdw_cu_str_off_base. */ + Dwarf_Off str_off_base; + + /* The offset into the .debug_ranges section to use for GNU + DebugFission split units. Don't access directly, call + __libdw_cu_ranges_base. */ + Dwarf_Off ranges_base; + + /* The start of the offset table in .debug_loclists. + Don't access directly, call __libdw_cu_locs_base. */ + Dwarf_Off locs_base; + /* Memory boundaries of this CU. */ void *startp; void *endp; }; -/* Compute the offset of a CU's first DIE from its offset. This - is either: +#define ISV4TU(cu) ((cu)->version == 4 && (cu)->sec_idx == IDX_debug_types) + +/* Compute the offset of a CU's first DIE from the CU offset. + CU must be a valid/known version/unit_type. */ +static inline Dwarf_Off +__libdw_first_die_from_cu_start (Dwarf_Off cu_start, + uint8_t offset_size, + uint16_t version, + uint8_t unit_type) +{ +/* + assert (offset_size == 4 || offset_size == 8); + assert (version >= 2 && version <= 5); + assert (unit_type == DW_UT_compile + || unit_type == DW_UT_partial + || unit_type == DW_UT_skeleton + || unit_type == DW_UT_split_compile + || unit_type == DW_UT_type + || unit_type == DW_UT_split_type); +*/ + + Dwarf_Off off = cu_start; + if (version < 5) + { + /* LEN VER OFFSET ADDR 4-bytes + 2-bytes + 4-bytes + 1-byte for 32-bit dwarf 12-bytes + 2-bytes + 8-bytes + 1-byte for 64-bit dwarf @@ -328,22 +413,61 @@ struct Dwarf_CU 12-bytes + 2-bytes + 8-bytes + 1-byte + 8-bytes + 8-bytes for 64-bit Note the trick in the computation. If the offset_size is 4 - the '- 4' term changes the '3 *' into a '2 *'. If the - offset_size is 8 it accounts for the 4-byte escape value + the '- 4' term changes the '3 *' (or '4 *') into a '2 *' (or '3 *). + If the offset_size is 8 it accounts for the 4-byte escape value used at the start of the length. */ -#define DIE_OFFSET_FROM_CU_OFFSET(cu_offset, offset_size, type_unit) \ - ((type_unit) ? ((cu_offset) + 4 * (offset_size) - 4 + 3 + 8) \ - : ((cu_offset) + 3 * (offset_size) - 4 + 3)) + if (unit_type != DW_UT_type) + off += 3 * offset_size - 4 + 3; + else + off += 4 * offset_size - 4 + 3 + 8; + } + else + { + /* + LEN VER TYPE ADDR OFFSET SIGNATURE TYPE-OFFSET + 4-bytes + 2-bytes + 1-byte + 1-byte + 4-bytes + 8-bytes + 4-bytes 32-bit + 12-bytes + 2-bytes + 1-byte + 1-byte + 8-bytes + 8-bytes + 8-bytes 64-bit + Both signature and type offset are optional. + + Note same 4/8 offset size trick as above. + We explicitly ignore unknow unit types (see asserts above). */ + off += 3 * offset_size - 4 + 4; + if (unit_type == DW_UT_skeleton || unit_type == DW_UT_split_compile + || unit_type == DW_UT_type || unit_type == DW_UT_split_type) + { + off += 8; + if (unit_type == DW_UT_type || unit_type == DW_UT_split_type) + off += offset_size; + } + } + + return off; +} + +static inline Dwarf_Off +__libdw_first_die_off_from_cu (struct Dwarf_CU *cu) +{ + return __libdw_first_die_from_cu_start (cu->start, + cu->offset_size, + cu->version, + cu->unit_type); +} #define CUDIE(fromcu) \ ((Dwarf_Die) \ { \ .cu = (fromcu), \ - .addr = ((char *) fromcu->dbg->sectiondata[cu_sec_idx (fromcu)]->d_buf \ - + DIE_OFFSET_FROM_CU_OFFSET ((fromcu)->start, \ - (fromcu)->offset_size, \ - (fromcu)->type_offset != 0)) \ - }) \ + .addr = ((char *) (fromcu)->dbg->sectiondata[cu_sec_idx (fromcu)]->d_buf \ + + __libdw_first_die_off_from_cu (fromcu)) \ + }) + +#define SUBDIE(fromcu) \ + ((Dwarf_Die) \ + { \ + .cu = (fromcu), \ + .addr = ((char *) (fromcu)->dbg->sectiondata[cu_sec_idx (fromcu)]->d_buf \ + + (fromcu)->start + (fromcu)->subdie_offset) \ + }) /* Prototype of a single .debug_macro operator. */ @@ -399,6 +523,44 @@ libdw_macro_nforms (Dwarf_Macro *macro) return macro->table->table[macro->table->opcodes[macro->opcode - 1]].nforms; } +/* Returns true for any allowed FORM in the opcode_operands_table as + mentioned in the DWARF5 spec (6.3.1 Macro Information Header). + Or those mentioned in DWARF5 spec (6.2.4.2 Vendor-defined Content + Descriptions) for the directory/file table (plus DW_FORM_strp_sup). */ +static inline bool +libdw_valid_user_form (int form) +{ + switch (form) + { + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: + case DW_FORM_data16: + case DW_FORM_flag: + case DW_FORM_line_strp: + case DW_FORM_sdata: + case DW_FORM_sec_offset: + case DW_FORM_string: + case DW_FORM_strp: + case DW_FORM_strp_sup: + case DW_FORM_strx: + case DW_FORM_strx1: + case DW_FORM_strx2: + case DW_FORM_strx3: + case DW_FORM_strx4: + case DW_FORM_udata: + return true; + default: + return false; + } +} + + /* We have to include the file at this point because the inline functions access internals of the Dwarf structure. */ #include "memory-access.h" @@ -434,7 +596,19 @@ extern void *__libdw_allocate (Dwarf *dbg, size_t minsize, size_t align) __attribute__ ((__malloc__)) __nonnull_attribute__ (1); /* Default OOM handler. */ -extern void __libdw_oom (void) __attribute ((noreturn, visibility ("hidden"))); +extern void __libdw_oom (void) __attribute ((noreturn)) attribute_hidden; + +/* Read next unit (or v4 debug type) and return next offset. Doesn't + create an actual Dwarf_CU just provides necessary header fields. */ +extern int +internal_function +__libdw_next_unit (Dwarf *dbg, bool v4_debug_types, Dwarf_Off off, + Dwarf_Off *next_off, size_t *header_sizep, + Dwarf_Half *versionp, uint8_t *unit_typep, + Dwarf_Off *abbrev_offsetp, uint8_t *address_sizep, + uint8_t *offset_sizep, uint64_t *unit_id8p, + Dwarf_Off *subdie_offsetp) + __nonnull_attribute__ (4) internal_function; /* Allocate the internal data for a unit not seen before. */ extern struct Dwarf_CU *__libdw_intern_next_unit (Dwarf *dbg, bool debug_types) @@ -444,6 +618,18 @@ extern struct Dwarf_CU *__libdw_intern_next_unit (Dwarf *dbg, bool debug_types) extern struct Dwarf_CU *__libdw_findcu (Dwarf *dbg, Dwarf_Off offset, bool tu) __nonnull_attribute__ (1) internal_function; +/* Find CU for given DIE address. */ +extern struct Dwarf_CU *__libdw_findcu_addr (Dwarf *dbg, void *addr) + __nonnull_attribute__ (1) internal_function; + +/* Find split Dwarf for given DIE address. */ +extern struct Dwarf *__libdw_find_split_dbg_addr (Dwarf *dbg, void *addr) + __nonnull_attribute__ (1) internal_function; + +/* Find the split (or skeleton) unit. */ +extern struct Dwarf_CU *__libdw_find_split_unit (Dwarf_CU *cu) + internal_function; + /* Get abbreviation with given code. */ extern Dwarf_Abbrev *__libdw_findabbrev (struct Dwarf_CU *cu, unsigned int code) @@ -467,6 +653,9 @@ __libdw_dieabbrev (Dwarf_Die *die, const unsigned char **readp) /* Get the abbreviation code. */ unsigned int code; const unsigned char *addr = die->addr; + if (unlikely (die->cu == NULL + || addr >= (const unsigned char *) die->cu->endp)) + return die->abbrev = DWARF_END_ABBREV; get_uleb128 (code, addr, die->cu->endp); if (readp != NULL) *readp = addr; @@ -484,7 +673,7 @@ extern size_t __libdw_form_val_compute_len (struct Dwarf_CU *cu, const unsigned char *valp) __nonnull_attribute__ (1, 3) internal_function; -/* Find the length of a form attribute. */ +/* Find the length of a form attribute in DIE/info data. */ static inline size_t __nonnull_attribute__ (1, 3) __libdw_form_val_len (struct Dwarf_CU *cu, unsigned int form, @@ -495,10 +684,24 @@ __libdw_form_val_len (struct Dwarf_CU *cu, unsigned int form, static const uint8_t form_lengths[] = { [DW_FORM_flag_present] = 0x80, - [DW_FORM_data1] = 1, [DW_FORM_ref1] = 1, [DW_FORM_flag] = 1, + [DW_FORM_implicit_const] = 0x80, /* Value is in abbrev, not in info. */ + + [DW_FORM_flag] = 1, + [DW_FORM_data1] = 1, [DW_FORM_ref1] = 1, + [DW_FORM_addrx1] = 1, [DW_FORM_strx1] = 1, + [DW_FORM_data2] = 2, [DW_FORM_ref2] = 2, - [DW_FORM_data4] = 4, [DW_FORM_ref4] = 4, - [DW_FORM_data8] = 8, [DW_FORM_ref8] = 8, [DW_FORM_ref_sig8] = 8, + [DW_FORM_addrx2] = 2, [DW_FORM_strx2] = 2, + + [DW_FORM_addrx3] = 3, [DW_FORM_strx3] = 3, + + [DW_FORM_data4] = 4, [DW_FORM_ref4] = 4, [DW_FORM_ref_sup4] = 4, + [DW_FORM_addrx4] = 4, [DW_FORM_strx4] = 4, + + [DW_FORM_ref_sig8] = 8, + [DW_FORM_data8] = 8, [DW_FORM_ref8] = 8, [DW_FORM_ref_sup8] = 8, + + [DW_FORM_data16] = 16, }; /* Return immediately for forms with fixed lengths. */ @@ -626,7 +829,8 @@ __libdw_offset_in_section (Dwarf *dbg, int sec_index, if (data == NULL) return -1; if (unlikely (offset > data->d_size) - || unlikely (data->d_size - offset < size)) + || unlikely (data->d_size < size) + || unlikely (offset > data->d_size - size)) { __libdw_seterrno (DWARF_E_INVALID_OFFSET); return -1; @@ -643,7 +847,8 @@ __libdw_in_section (Dwarf *dbg, int sec_index, if (data == NULL) return false; if (unlikely (addr < data->d_buf) - || unlikely (data->d_size - (addr - data->d_buf) < size)) + || unlikely (data->d_size < size) + || unlikely ((size_t)(addr - data->d_buf) > data->d_size - size)) { __libdw_seterrno (DWARF_E_INVALID_OFFSET); return false; @@ -714,13 +919,13 @@ __libdw_read_offset (Dwarf *dbg, Dwarf *dbg_ret, static inline size_t cu_sec_idx (struct Dwarf_CU *cu) { - return cu->type_offset == 0 ? IDX_debug_info : IDX_debug_types; + return cu->sec_idx; } static inline bool is_cudie (Dwarf_Die *cudie) { - return CUDIE (cudie->cu).addr == cudie->addr; + return cudie->cu != NULL && CUDIE (cudie->cu).addr == cudie->addr; } /* Read up begin/end pair and increment read pointer. @@ -728,15 +933,18 @@ is_cudie (Dwarf_Die *cudie) - If it's base address selection record, set up *BASEP and return 1. - If it's end of rangelist, don't set anything and return 2 - If an error occurs, don't set anything and return <0. */ -int __libdw_read_begin_end_pair_inc (Dwarf *dbg, int sec_index, - unsigned char **addr, int width, +int __libdw_read_begin_end_pair_inc (Dwarf_CU *cu, int sec_index, + const unsigned char **readp, + const unsigned char *readend, + int width, Dwarf_Addr *beginp, Dwarf_Addr *endp, Dwarf_Addr *basep) internal_function; -unsigned char * __libdw_formptr (Dwarf_Attribute *attr, int sec_index, - int err_nodata, unsigned char **endpp, - Dwarf_Off *offsetp) +const unsigned char * __libdw_formptr (Dwarf_Attribute *attr, int sec_index, + int err_nodata, + const unsigned char **endpp, + Dwarf_Off *offsetp) internal_function; /* Fills in the given attribute to point at an empty location expression. */ @@ -757,6 +965,363 @@ int __libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset, /* Load and return value of DW_AT_comp_dir from CUDIE. */ const char *__libdw_getcompdir (Dwarf_Die *cudie); +/* Get the base address for the CU, fetches it when not yet set. + This is used as initial base address for ranges and loclists. */ +Dwarf_Addr __libdw_cu_base_address (Dwarf_CU *cu); + +/* Get the address base for the CU, fetches it when not yet set. */ +static inline Dwarf_Off +__libdw_cu_addr_base (Dwarf_CU *cu) +{ + if (cu->addr_base == (Dwarf_Off) -1) + { + Dwarf_Die cu_die = CUDIE(cu); + Dwarf_Attribute attr; + Dwarf_Off offset = 0; + if (dwarf_attr (&cu_die, DW_AT_GNU_addr_base, &attr) != NULL + || dwarf_attr (&cu_die, DW_AT_addr_base, &attr) != NULL) + { + Dwarf_Word off; + if (dwarf_formudata (&attr, &off) == 0) + offset = off; + } + cu->addr_base = offset; + } + + return cu->addr_base; +} + +/* Gets the .debug_str_offsets base offset to use. static inline to + be shared between libdw and eu-readelf. */ +static inline Dwarf_Off +str_offsets_base_off (Dwarf *dbg, Dwarf_CU *cu) +{ + /* If we don't have a CU, then find and use the first one in the + debug file (when we support .dwp files, we must actually find the + one matching our "caller" - aka macro or line). If we (now) have + a cu and str_offsets_base attribute, just use that. Otherwise + use the first offset. But we might have to parse the header + first, but only if this is version 5. Assume if all else fails, + this is version 4, without header. */ + + if (cu == NULL && dbg != NULL) + { + Dwarf_CU *first_cu; + if (dwarf_get_units (dbg, NULL, &first_cu, + NULL, NULL, NULL, NULL) == 0) + cu = first_cu; + } + + if (cu != NULL) + { + if (cu->str_off_base == (Dwarf_Off) -1) + { + Dwarf_Die cu_die = CUDIE(cu); + Dwarf_Attribute attr; + if (dwarf_attr (&cu_die, DW_AT_str_offsets_base, &attr) != NULL) + { + Dwarf_Word off; + if (dwarf_formudata (&attr, &off) == 0) + { + cu->str_off_base = off; + return cu->str_off_base; + } + } + /* For older DWARF simply assume zero (no header). */ + if (cu->version < 5) + { + cu->str_off_base = 0; + return cu->str_off_base; + } + + if (dbg == NULL) + dbg = cu->dbg; + } + else + return cu->str_off_base; + } + + /* No str_offsets_base attribute, we have to assume "zero". + But there could be a header first. */ + Dwarf_Off off = 0; + if (dbg == NULL) + goto no_header; + + Elf_Data *data = dbg->sectiondata[IDX_debug_str_offsets]; + if (data == NULL) + goto no_header; + + const unsigned char *start; + const unsigned char *readp; + const unsigned char *readendp; + start = readp = (const unsigned char *) data->d_buf; + readendp = (const unsigned char *) data->d_buf + data->d_size; + + uint64_t unit_length; + uint16_t version; + + unit_length = read_4ubyte_unaligned_inc (dbg, readp); + if (unlikely (unit_length == 0xffffffff)) + { + if (unlikely (readendp - readp < 8)) + goto no_header; + unit_length = read_8ubyte_unaligned_inc (dbg, readp); + /* In theory the offset size could be different + between CU and str_offsets unit. But we just + ignore that here. */ + } + + /* We need at least 2-bytes (version) + 2-bytes (padding) = + 4 bytes to complete the header. And this unit cannot go + beyond the section data. */ + if (readendp - readp < 4 + || unit_length < 4 + || (uint64_t) (readendp - readp) < unit_length) + goto no_header; + + version = read_2ubyte_unaligned_inc (dbg, readp); + if (version != 5) + goto no_header; + /* padding */ + read_2ubyte_unaligned_inc (dbg, readp); + + off = (Dwarf_Off) (readp - start); + + no_header: + if (cu != NULL) + cu->str_off_base = off; + + return off; +} + + +/* Get the string offsets base for the CU, fetches it when not yet set. */ +static inline Dwarf_Off __libdw_cu_str_off_base (Dwarf_CU *cu) +{ + return str_offsets_base_off (NULL, cu); +} + + +/* Either a direct offset into .debug_ranges for version < 5, or the + start of the offset table in .debug_rnglists for version > 5. */ +static inline Dwarf_Off +__libdw_cu_ranges_base (Dwarf_CU *cu) +{ + if (cu->ranges_base == (Dwarf_Off) -1) + { + Dwarf_Off offset = 0; + Dwarf_Die cu_die = CUDIE(cu); + Dwarf_Attribute attr; + if (cu->version < 5) + { + if (dwarf_attr (&cu_die, DW_AT_GNU_ranges_base, &attr) != NULL) + { + Dwarf_Word off; + if (dwarf_formudata (&attr, &off) == 0) + offset = off; + } + } + else + { + if (dwarf_attr (&cu_die, DW_AT_rnglists_base, &attr) != NULL) + { + Dwarf_Word off; + if (dwarf_formudata (&attr, &off) == 0) + offset = off; + } + + /* There wasn't an rnglists_base, if the Dwarf does have a + .debug_rnglists section, then it might be we need the + base after the first header. */ + Elf_Data *data = cu->dbg->sectiondata[IDX_debug_rnglists]; + if (offset == 0 && data != NULL) + { + Dwarf *dbg = cu->dbg; + const unsigned char *readp = data->d_buf; + const unsigned char *const dataend + = (unsigned char *) data->d_buf + data->d_size; + + uint64_t unit_length = read_4ubyte_unaligned_inc (dbg, readp); + unsigned int offset_size = 4; + if (unlikely (unit_length == 0xffffffff)) + { + if (unlikely (readp > dataend - 8)) + goto no_header; + + unit_length = read_8ubyte_unaligned_inc (dbg, readp); + offset_size = 8; + } + + if (readp > dataend - 8 + || unit_length < 8 + || unit_length > (uint64_t) (dataend - readp)) + goto no_header; + + uint16_t version = read_2ubyte_unaligned_inc (dbg, readp); + if (version != 5) + goto no_header; + + uint8_t address_size = *readp++; + if (address_size != 4 && address_size != 8) + goto no_header; + + uint8_t segment_size = *readp++; + if (segment_size != 0) + goto no_header; + + uint32_t offset_entry_count; + offset_entry_count = read_4ubyte_unaligned_inc (dbg, readp); + + const unsigned char *offset_array_start = readp; + if (offset_entry_count <= 0) + goto no_header; + + uint64_t needed = offset_entry_count * offset_size; + if (unit_length - 8 < needed) + goto no_header; + + offset = (Dwarf_Off) (offset_array_start + - (unsigned char *) data->d_buf); + } + } + no_header: + cu->ranges_base = offset; + } + + return cu->ranges_base; +} + + +/* The start of the offset table in .debug_loclists for DWARF5. */ +static inline Dwarf_Off +__libdw_cu_locs_base (Dwarf_CU *cu) +{ + if (cu->locs_base == (Dwarf_Off) -1) + { + Dwarf_Off offset = 0; + Dwarf_Die cu_die = CUDIE(cu); + Dwarf_Attribute attr; + if (dwarf_attr (&cu_die, DW_AT_loclists_base, &attr) != NULL) + { + Dwarf_Word off; + if (dwarf_formudata (&attr, &off) == 0) + offset = off; + } + + /* There wasn't an loclists_base, if the Dwarf does have a + .debug_loclists section, then it might be we need the + base after the first header. */ + Elf_Data *data = cu->dbg->sectiondata[IDX_debug_loclists]; + if (offset == 0 && data != NULL) + { + Dwarf *dbg = cu->dbg; + const unsigned char *readp = data->d_buf; + const unsigned char *const dataend + = (unsigned char *) data->d_buf + data->d_size; + + uint64_t unit_length = read_4ubyte_unaligned_inc (dbg, readp); + unsigned int offset_size = 4; + if (unlikely (unit_length == 0xffffffff)) + { + if (unlikely (readp > dataend - 8)) + goto no_header; + + unit_length = read_8ubyte_unaligned_inc (dbg, readp); + offset_size = 8; + } + + if (readp > dataend - 8 + || unit_length < 8 + || unit_length > (uint64_t) (dataend - readp)) + goto no_header; + + uint16_t version = read_2ubyte_unaligned_inc (dbg, readp); + if (version != 5) + goto no_header; + + uint8_t address_size = *readp++; + if (address_size != 4 && address_size != 8) + goto no_header; + + uint8_t segment_size = *readp++; + if (segment_size != 0) + goto no_header; + + uint32_t offset_entry_count; + offset_entry_count = read_4ubyte_unaligned_inc (dbg, readp); + + const unsigned char *offset_array_start = readp; + if (offset_entry_count <= 0) + goto no_header; + + uint64_t needed = offset_entry_count * offset_size; + if (unit_length - 8 < needed) + goto no_header; + + offset = (Dwarf_Off) (offset_array_start + - (unsigned char *) data->d_buf); + } + + no_header: + cu->locs_base = offset; + } + + return cu->locs_base; +} + +/* Helper function for tsearch/tfind split_tree Dwarf. */ +int __libdw_finddbg_cb (const void *arg1, const void *arg2); + +/* Link skeleton and split compile units. */ +static inline void +__libdw_link_skel_split (Dwarf_CU *skel, Dwarf_CU *split) +{ + skel->split = split; + split->split = skel; + + /* Get .debug_addr and addr_base greedy. + We also need it for the fake addr cu. + There is only one per split debug. */ + Dwarf *dbg = skel->dbg; + Dwarf *sdbg = split->dbg; + if (sdbg->sectiondata[IDX_debug_addr] == NULL + && dbg->sectiondata[IDX_debug_addr] != NULL) + { + sdbg->sectiondata[IDX_debug_addr] + = dbg->sectiondata[IDX_debug_addr]; + split->addr_base = __libdw_cu_addr_base (skel); + sdbg->fake_addr_cu = dbg->fake_addr_cu; + } +} + + +/* Given an address index for a CU return the address. + Returns -1 and sets libdw_errno if an error occurs. */ +int __libdw_addrx (Dwarf_CU *cu, Dwarf_Word idx, Dwarf_Addr *addr); + + +/* Helper function to set debugdir field in Dwarf, used from dwarf_begin_elf + and libdwfl process_file. */ +char * __libdw_debugdir (int fd); + + +/* Given the directory of a debug file, an absolute or relative dir + to look in, and file returns a full path. + + If the file is absolute (starts with a /) a copy of file is returned. + the file isn't absolute, but dir is absolute, then a path that is + the concatenation of dir and file is returned. If neither file, + nor dir is absolute, the path will be constructed using dir (if not + NULL) and file relative to the debugdir (if valid). + + The debugdir and the dir may be NULL (in which case they aren't used). + If file is NULL, or no full path can be constructed NULL is returned. + + The caller is responsible for freeing the result if not NULL. */ +char * __libdw_filepath (const char *debugdir, const char *dir, + const char *file) + internal_function; + /* Aliases to avoid PLTs. */ INTDECL (dwarf_aggregate_size) @@ -777,6 +1342,7 @@ INTDECL (dwarf_formref_die) INTDECL (dwarf_formsdata) INTDECL (dwarf_formstring) INTDECL (dwarf_formudata) +INTDECL (dwarf_getabbrevattr_data) INTDECL (dwarf_getalt) INTDECL (dwarf_getarange_addr) INTDECL (dwarf_getarangeinfo) |
