diff options
author | Mark Wielaard <mark@klomp.org> | 2018-04-07 23:48:27 +0200 |
---|---|---|
committer | Mark Wielaard <mark@klomp.org> | 2018-05-30 00:16:15 +0200 |
commit | 4dc31fde711c69fed6acfe18e7e57dfd5665cebb (patch) | |
tree | 1f8cac4d4e3a2da4314cf37410dd94cd418d47d8 /libdw | |
parent | 7cfe2c16f9ddaa7478a2d97dd893c6b89080020a (diff) | |
download | elfutils-4dc31fde711c69fed6acfe18e7e57dfd5665cebb.tar.gz |
libdw: Handle .debug_loclists in dwarf_getlocation.
Handle all new DW_LLE opcodes in .debug_loclists in dwarf_getlocation.
__libdw_read_begin_end_pair_inc now also handles a default location
(which is simply the range [0,-1]). Since expression blocks can now
also come from the .debug_loclists section add a new fake_loclists_cu
necessary for checking bounds while parsing expression blocks.
Adapt varlocs test to handle debug-only files.
Test testfileranges5.debug and testfilesplitranges5.debug with it.
Signed-off-by: Mark Wielaard <mark@klomp.org>
Diffstat (limited to 'libdw')
-rw-r--r-- | libdw/ChangeLog | 17 | ||||
-rw-r--r-- | libdw/dwarf_begin_elf.c | 27 | ||||
-rw-r--r-- | libdw/dwarf_end.c | 5 | ||||
-rw-r--r-- | libdw/dwarf_getlocation.c | 99 | ||||
-rw-r--r-- | libdw/dwarf_getlocation_attr.c | 6 | ||||
-rw-r--r-- | libdw/dwarf_ranges.c | 114 | ||||
-rw-r--r-- | libdw/libdwP.h | 87 | ||||
-rw-r--r-- | libdw/libdw_findcu.c | 1 |
8 files changed, 340 insertions, 16 deletions
diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 0db49bf8..22712f18 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,20 @@ +2018-04-07 Mark Wielaard <mark@klomp.org> + + * libdwP.h (struct Dwarf_CU): Add locs_base. + (__libdw_cu_locs_base): New static inline function. + * libdw_findcu.c (__libdw_intern_next_unit): Initialize locs_base. + * dwarf_begin_elf.c (valid_p): Create fake_loclists_cu if necessary. + * dwarf_end.c (dwarf_end): Clean up fake_loclists_cu. + * dwarf_getlocation.c (initial_offset): Handle .debug_loclists. + (getlocations_addr): Likewise. + (dwarf_getlocation_addr): Likewise. + * dwarf_getlocation_attr.c (attr_form_cu): Use fake_loclists_cu for + DWARF5. + (initial_offset): Handle DW_FORM_loclistx. + * dwarf_ranges.c (__libdw_read_begin_end_pair_inc): Handle + .debug_loclists. + * libdwP.h (struct Dwarf): Add fake_loclists_cu. + 2018-04-12 Mark Wielaard <mark@klomp.org> * dwarf.h: Add DWARF5 location list entry DW_LLE encodings. diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c index af5096fb..513af2b1 100644 --- a/libdw/dwarf_begin_elf.c +++ b/libdw/dwarf_begin_elf.c @@ -226,6 +226,9 @@ valid_p (Dwarf *result) result = NULL; } + /* For dwarf_location_attr () we need a "fake" CU to indicate + where the "fake" attribute data comes from. This is a block + inside the .debug_loc or .debug_loclists section. */ if (result != NULL && result->sectiondata[IDX_debug_loc] != NULL) { result->fake_loc_cu = (Dwarf_CU *) calloc (1, sizeof (Dwarf_CU)); @@ -248,6 +251,29 @@ valid_p (Dwarf *result) } } + if (result != NULL && result->sectiondata[IDX_debug_loclists] != NULL) + { + result->fake_loclists_cu = (Dwarf_CU *) calloc (1, sizeof (Dwarf_CU)); + if (unlikely (result->fake_loclists_cu == NULL)) + { + Dwarf_Sig8_Hash_free (&result->sig8_hash); + __libdw_seterrno (DWARF_E_NOMEM); + free (result->fake_loc_cu); + free (result); + result = NULL; + } + else + { + result->fake_loclists_cu->sec_idx = IDX_debug_loclists; + result->fake_loclists_cu->dbg = result; + result->fake_loclists_cu->startp + = result->sectiondata[IDX_debug_loclists]->d_buf; + result->fake_loclists_cu->endp + = (result->sectiondata[IDX_debug_loclists]->d_buf + + result->sectiondata[IDX_debug_loclists]->d_size); + } + } + /* For DW_OP_constx/GNU_const_index and DW_OP_addrx/GNU_addr_index the dwarf_location_attr () will need a "fake" address CU to indicate where the attribute data comes from. This is a just @@ -260,6 +286,7 @@ valid_p (Dwarf *result) Dwarf_Sig8_Hash_free (&result->sig8_hash); __libdw_seterrno (DWARF_E_NOMEM); free (result->fake_loc_cu); + free (result->fake_loclists_cu); free (result); result = NULL; } diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c index 19546741..23a50a0b 100644 --- a/libdw/dwarf_end.c +++ b/libdw/dwarf_end.c @@ -113,6 +113,11 @@ dwarf_end (Dwarf *dwarf) cu_free (dwarf->fake_loc_cu); free (dwarf->fake_loc_cu); } + if (dwarf->fake_loclists_cu != NULL) + { + cu_free (dwarf->fake_loclists_cu); + free (dwarf->fake_loclists_cu); + } if (dwarf->fake_addr_cu != NULL) { cu_free (dwarf->fake_addr_cu); diff --git a/libdw/dwarf_getlocation.c b/libdw/dwarf_getlocation.c index d4b8effe..d293e75d 100644 --- a/libdw/dwarf_getlocation.c +++ b/libdw/dwarf_getlocation.c @@ -696,13 +696,77 @@ __libdw_cu_base_address (Dwarf_CU *cu) static int initial_offset (Dwarf_Attribute *attr, ptrdiff_t *offset) { - size_t secidx = IDX_debug_loc; + size_t secidx = (attr->cu->version < 5 + ? IDX_debug_loc : IDX_debug_loclists); Dwarf_Word start_offset; - if (__libdw_formptr (attr, secidx, - DWARF_E_NO_DEBUG_LOC, - NULL, &start_offset) == NULL) - return -1; + if (attr->form == DW_FORM_loclistx) + { + Dwarf_Word idx; + Dwarf_CU *cu = attr->cu; + const unsigned char *datap = attr->valp; + const unsigned char *endp = cu->endp; + if (datap >= endp) + { + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } + get_uleb128 (idx, datap, endp); + + Elf_Data *data = cu->dbg->sectiondata[secidx]; + if (data == NULL && cu->unit_type == DW_UT_split_compile) + { + cu = __libdw_find_split_unit (cu); + if (cu != NULL) + data = cu->dbg->sectiondata[secidx]; + } + + if (data == NULL) + { + __libdw_seterrno (secidx == IDX_debug_loc + ? DWARF_E_NO_DEBUG_LOC + : DWARF_E_NO_DEBUG_LOCLISTS); + return -1; + } + + Dwarf_Off loc_base_off = __libdw_cu_locs_base (cu); + + /* The section should at least contain room for one offset. */ + size_t sec_size = cu->dbg->sectiondata[secidx]->d_size; + size_t offset_size = cu->offset_size; + if (offset_size > sec_size) + { + invalid_offset: + __libdw_seterrno (DWARF_E_INVALID_OFFSET); + return -1; + } + + /* And the base offset should be at least inside the section. */ + if (loc_base_off > (sec_size - offset_size)) + goto invalid_offset; + + size_t max_idx = (sec_size - offset_size - loc_base_off) / offset_size; + if (idx > max_idx) + goto invalid_offset; + + datap = (cu->dbg->sectiondata[secidx]->d_buf + + loc_base_off + (idx * offset_size)); + if (offset_size == 4) + start_offset = read_4ubyte_unaligned (cu->dbg, datap); + else + start_offset = read_8ubyte_unaligned (cu->dbg, datap); + + start_offset += loc_base_off; + } + else + { + if (__libdw_formptr (attr, secidx, + (secidx == IDX_debug_loc + ? DWARF_E_NO_DEBUG_LOC + : DWARF_E_NO_DEBUG_LOCLISTS), + NULL, &start_offset) == NULL) + return -1; + } *offset = start_offset; return 0; @@ -716,7 +780,7 @@ getlocations_addr (Dwarf_Attribute *attr, ptrdiff_t offset, { Dwarf_CU *cu = attr->cu; Dwarf *dbg = cu->dbg; - size_t secidx = IDX_debug_loc; + size_t secidx = cu->version < 5 ? IDX_debug_loc : IDX_debug_loclists; const unsigned char *readp = locs->d_buf + offset; const unsigned char *readendp = locs->d_buf + locs->d_size; @@ -741,13 +805,22 @@ getlocations_addr (Dwarf_Attribute *attr, ptrdiff_t offset, /* We have a location expression. */ Dwarf_Block block; - if (readendp - readp < 2) + if (secidx == IDX_debug_loc) { - invalid: - __libdw_seterrno (DWARF_E_INVALID_DWARF); - return -1; + if (readendp - readp < 2) + { + invalid: + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } + block.length = read_2ubyte_unaligned_inc (dbg, readp); + } + else + { + if (readendp - readp < 1) + goto invalid; + get_uleb128 (block.length, readp, readendp); } - block.length = read_2ubyte_unaligned_inc (dbg, readp); block.data = (unsigned char *) readp; if (readendp - readp < (ptrdiff_t) block.length) goto invalid; @@ -815,7 +888,7 @@ dwarf_getlocation_addr (Dwarf_Attribute *attr, Dwarf_Addr address, if (initial_offset (attr, &off) != 0) return -1; - size_t secidx = IDX_debug_loc; + size_t secidx = attr->cu->version < 5 ? IDX_debug_loc : IDX_debug_loclists; const Elf_Data *d = attr->cu->dbg->sectiondata[secidx]; while (got < maxlocs @@ -896,7 +969,7 @@ dwarf_getlocations (Dwarf_Attribute *attr, ptrdiff_t offset, Dwarf_Addr *basep, return -1; } - size_t secidx = IDX_debug_loc; + size_t secidx = attr->cu->version < 5 ? IDX_debug_loc : IDX_debug_loclists; const Elf_Data *d = attr->cu->dbg->sectiondata[secidx]; return getlocations_addr (attr, offset, basep, startp, endp, diff --git a/libdw/dwarf_getlocation_attr.c b/libdw/dwarf_getlocation_attr.c index 9d7fd4b5..875fc5d7 100644 --- a/libdw/dwarf_getlocation_attr.c +++ b/libdw/dwarf_getlocation_attr.c @@ -38,7 +38,7 @@ attr_form_cu (Dwarf_Attribute *attr) { /* If the attribute has block/expr form the data comes from the .debug_info from the same cu as the attr. Otherwise it comes from - the .debug_loc data section. */ + the .debug_loc or .debug_loclists data section. */ switch (attr->form) { case DW_FORM_block1: @@ -48,7 +48,9 @@ attr_form_cu (Dwarf_Attribute *attr) case DW_FORM_exprloc: return attr->cu; default: - return attr->cu->dbg->fake_loc_cu; + return (attr->cu->version < 5 + ? attr->cu->dbg->fake_loc_cu + : attr->cu->dbg->fake_loclists_cu); } } diff --git a/libdw/dwarf_ranges.c b/libdw/dwarf_ranges.c index fa65e5c6..0f3ee6b5 100644 --- a/libdw/dwarf_ranges.c +++ b/libdw/dwarf_ranges.c @@ -36,6 +36,7 @@ /* Read up begin/end pair and increment read pointer. - If it's normal range record, set up `*beginp' and `*endp' and return 0. + - If it's a default location, set `*beginp' (0), `*endp' (-1) and return 0. - 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 -1. */ @@ -197,6 +198,119 @@ __libdw_read_begin_end_pair_inc (Dwarf_CU *cu, int sec_index, goto invalid; } } + else if (sec_index == IDX_debug_loclists) + { + const unsigned char *addr = *addrp; + if (addrend - addr < 1) + goto invalid; + + const char code = *addr++; + uint64_t begin = 0, end = 0, base = *basep, addr_idx; + switch (code) + { + case DW_LLE_end_of_list: + *addrp = addr; + return 2; + + case DW_LLE_base_addressx: + if (addrend - addr < 1) + goto invalid; + get_uleb128 (addr_idx, addr, addrend); + if (__libdw_addrx (cu, addr_idx, &base) != 0) + return -1; + + *basep = base; + *addrp = addr; + return 1; + + case DW_LLE_startx_endx: + if (addrend - addr < 1) + goto invalid; + get_uleb128 (addr_idx, addr, addrend); + if (__libdw_addrx (cu, addr_idx, &begin) != 0) + return -1; + if (addrend - addr < 1) + goto invalid; + get_uleb128 (addr_idx, addr, addrend); + if (__libdw_addrx (cu, addr_idx, &end) != 0) + return -1; + + *beginp = begin; + *endp = end; + *addrp = addr; + return 0; + + case DW_LLE_startx_length: + if (addrend - addr < 1) + goto invalid; + get_uleb128 (addr_idx, addr, addrend); + if (__libdw_addrx (cu, addr_idx, &begin) != 0) + return -1; + if (addrend - addr < 1) + goto invalid; + get_uleb128 (end, addr, addrend); + + *beginp = begin; + *endp = begin + end; + *addrp = addr; + return 0; + + case DW_LLE_offset_pair: + if (addrend - addr < 1) + goto invalid; + get_uleb128 (begin, addr, addrend); + if (addrend - addr < 1) + goto invalid; + get_uleb128 (end, addr, addrend); + + *beginp = begin + base; + *endp = end + base; + *addrp = addr; + return 0; + + case DW_LLE_default_location: + *beginp = 0; + *endp = (Dwarf_Addr) -1; + *addrp = addr; + return 0; + + case DW_LLE_base_address: + if (addrend - addr < width) + goto invalid; + __libdw_read_address_inc (dbg, sec_index, &addr, width, &base); + + *basep = base; + *addrp = addr; + return 1; + + case DW_LLE_start_end: + if (addrend - addr < 2 * width) + goto invalid; + __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin); + __libdw_read_address_inc (dbg, sec_index, &addr, width, &end); + + *beginp = begin; + *endp = end; + *addrp = addr; + return 0; + + case DW_LLE_start_length: + if (addrend - addr < width) + goto invalid; + __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin); + if (addrend - addr < 1) + goto invalid; + get_uleb128 (end, addr, addrend); + + *beginp = begin; + *endp = begin + end; + *addrp = addr; + return 0; + + default: + goto invalid; + } + } else { __libdw_seterrno (DWARF_E_INVALID_DWARF); diff --git a/libdw/libdwP.h b/libdw/libdwP.h index f99ea584..dd47009b 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -206,8 +206,11 @@ 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; @@ -365,6 +368,10 @@ struct Dwarf_CU __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; @@ -1177,6 +1184,84 @@ __libdw_cu_ranges_base (Dwarf_CU *cu) } +/* 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; +} + + /* Link skeleton and split compile units. */ static inline void __libdw_link_skel_split (Dwarf_CU *skel, Dwarf_CU *split) diff --git a/libdw/libdw_findcu.c b/libdw/libdw_findcu.c index 83c2eb14..9d231999 100644 --- a/libdw/libdw_findcu.c +++ b/libdw/libdw_findcu.c @@ -122,6 +122,7 @@ __libdw_intern_next_unit (Dwarf *dbg, bool debug_types) newp->addr_base = (Dwarf_Off) -1; newp->str_off_base = (Dwarf_Off) -1; newp->ranges_base = (Dwarf_Off) -1; + newp->locs_base = (Dwarf_Off) -1; newp->startp = data->d_buf + newp->start; newp->endp = data->d_buf + newp->end; |