From: Petr Machata Date: Mon, 4 May 2009 16:07:54 +0000 (+0200) Subject: Fix bugs, introduce __libdw_read_begin_end_pair_inc X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=90d7c39454468b91c0fd6514a4e8d821222d6adb;p=thirdparty%2Felfutils.git Fix bugs, introduce __libdw_read_begin_end_pair_inc --- diff --git a/libdw/dwarf_formblock.c b/libdw/dwarf_formblock.c index 486dc1983..51396d470 100644 --- a/libdw/dwarf_formblock.c +++ b/libdw/dwarf_formblock.c @@ -79,9 +79,7 @@ dwarf_formblock (attr, return_block) break; case DW_FORM_block4: - if (__libdw_read_length (attr->cu->dbg, IDX_debug_info, attr->valp, - 4, &return_block->length)) - return -1; + return_block->length = read_4ubyte_unaligned (attr->cu->dbg, attr->valp); return_block->data = attr->valp + 4; break; diff --git a/libdw/dwarf_formref.c b/libdw/dwarf_formref.c index 3595184cd..b8463b70b 100644 --- a/libdw/dwarf_formref.c +++ b/libdw/dwarf_formref.c @@ -79,11 +79,11 @@ __libdw_formref (attr, return_offset) break; case DW_FORM_ref4: + *return_offset = read_4ubyte_unaligned (attr->cu->dbg, attr->valp); + break; + case DW_FORM_ref8: - if (__libdw_read_offset (attr->cu->dbg, IDX_debug_info, attr->valp, - attr->form == DW_FORM_ref4 ? 4 : 8, - return_offset)) - return -1; + *return_offset = read_8ubyte_unaligned (attr->cu->dbg, attr->valp); break; case DW_FORM_ref_udata: diff --git a/libdw/dwarf_formref_die.c b/libdw/dwarf_formref_die.c index 128ee137d..a6ef7e48a 100644 --- a/libdw/dwarf_formref_die.c +++ b/libdw/dwarf_formref_die.c @@ -72,9 +72,9 @@ dwarf_formref_die (attr, die_mem) ? attr->cu->address_size : attr->cu->offset_size); - if (__libdw_read_address (attr->cu->dbg, - IDX_debug_info, attr->valp, - ref_size, &offset)) + if (__libdw_read_offset (attr->cu->dbg, + IDX_debug_info, attr->valp, + ref_size, &offset, IDX_debug_info)) return NULL; } else diff --git a/libdw/dwarf_formstring.c b/libdw/dwarf_formstring.c index 63eca9b1d..f960f51d2 100644 --- a/libdw/dwarf_formstring.c +++ b/libdw/dwarf_formstring.c @@ -74,19 +74,15 @@ dwarf_formstring (attrp) if (unlikely (attrp->form != DW_FORM_strp) || dbg->sectiondata[IDX_debug_str] == NULL) { - invalid_error: __libdw_seterrno (DWARF_E_NO_STRING); return NULL; } uint64_t off; if (__libdw_read_offset (dbg, IDX_debug_info, attrp->valp, - attrp->cu->offset_size, &off)) + attrp->cu->offset_size, &off, IDX_debug_str)) return NULL; - if (off >= dbg->sectiondata[IDX_debug_str]->d_size) - goto invalid_error; - return (const char *) dbg->sectiondata[IDX_debug_str]->d_buf + off; } INTDEF(dwarf_formstring) diff --git a/libdw/dwarf_formudata.c b/libdw/dwarf_formudata.c index e2f1d023b..b346afb38 100644 --- a/libdw/dwarf_formudata.c +++ b/libdw/dwarf_formudata.c @@ -78,9 +78,9 @@ dwarf_formudata (attr, return_uval) case DW_FORM_data4: case DW_FORM_data8: - if (__libdw_read_offset (attr->cu->dbg, IDX_debug_info, attr->valp, - attr->form == DW_FORM_data4 ? 4 : 8, - return_uval)) + if (__libdw_read_address (attr->cu->dbg, IDX_debug_info, attr->valp, + attr->form == DW_FORM_data4 ? 4 : 8, + return_uval)) return -1; break; diff --git a/libdw/dwarf_getaranges.c b/libdw/dwarf_getaranges.c index 78086fb8a..7aeb293b7 100644 --- a/libdw/dwarf_getaranges.c +++ b/libdw/dwarf_getaranges.c @@ -151,13 +151,9 @@ dwarf_getaranges (dbg, aranges, naranges) Dwarf_Word offset; if (__libdw_read_offset_inc (dbg, IDX_debug_aranges, (unsigned char **)&readp, - length_bytes, &offset)) + length_bytes, &offset, IDX_debug_info)) return -1; - /* Sanity-check the offset. */ - if (offset + 4 > dbg->sectiondata[IDX_debug_info]->d_size) - goto invalid; - unsigned int address_size = *readp++; if (address_size != 4 && address_size != 8) goto invalid; @@ -177,12 +173,14 @@ dwarf_getaranges (dbg, aranges, naranges) if (__libdw_read_address_inc (dbg, IDX_debug_aranges, (unsigned char **)&readp, - address_size, &range_address) - || __libdw_read_length_inc (dbg, IDX_debug_aranges, - (unsigned char **)&readp, - address_size, &range_length)) + address_size, &range_address)) return -1; + if (address_size == 4) + range_length = read_4ubyte_unaligned_inc (dbg, readp); + else + range_length = read_8ubyte_unaligned_inc (dbg, readp); + /* Two zero values mark the end. */ if (range_address == 0 && range_length == 0) break; diff --git a/libdw/dwarf_getlocation.c b/libdw/dwarf_getlocation.c index f9573877c..95ad5259c 100644 --- a/libdw/dwarf_getlocation.c +++ b/libdw/dwarf_getlocation.c @@ -405,26 +405,17 @@ dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs) Dwarf_Addr begin; Dwarf_Addr end; - Dwarf_Addr escape = ADDR_ESCAPE (attr->cu->address_size); - - if (__libdw_read_address_inc (attr->cu->dbg, - IDX_debug_line, &readp, - attr->cu->address_size, &begin) - || __libdw_read_address_inc (attr->cu->dbg, - IDX_debug_line, &readp, - attr->cu->address_size, &end)) - goto invalid; - - if (begin == escape) - { - base = end; - if (unlikely (base == escape)) - goto invalid; - continue; - } - if (begin == 0 && end == 0) /* End of list entry. */ + int status + = __libdw_read_begin_end_pair_inc (attr->cu->dbg, IDX_debug_loc, + &readp, attr->cu->address_size, + &begin, &end, &base); + if (status == 2) /* End of list entry. */ break; + else if (status == 1) /* Base address selected. */ + continue; + else if (status < 0) + return status; if ((unsigned char *) d->d_buf + d->d_size - readp < 2) goto invalid; @@ -470,7 +461,7 @@ dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs) if (llbufs != NULL && unlikely (getlocation (attr->cu, &block, &llbufs[got], &listlens[got], - IDX_debug_line) != 0)) + IDX_debug_loc) != 0)) return -1; ++got; } diff --git a/libdw/dwarf_getpubnames.c b/libdw/dwarf_getpubnames.c index f8f34c50f..c16f68384 100644 --- a/libdw/dwarf_getpubnames.c +++ b/libdw/dwarf_getpubnames.c @@ -124,16 +124,14 @@ get_offsets (Dwarf *dbg) } /* Get the CU offset. */ - if (__libdw_read_address (dbg, IDX_debug_pubnames, readp + 2, len_bytes, - &mem[cnt].cu_offset)) + if (__libdw_read_offset (dbg, IDX_debug_pubnames, readp + 2, len_bytes, + &mem[cnt].cu_offset, IDX_debug_info)) /* Error has been already set in reader. */ goto err_return; /* Determine the size of the CU header. */ if (unlikely (dbg->sectiondata[IDX_debug_info] == NULL - || dbg->sectiondata[IDX_debug_info]->d_buf == NULL - || (mem[cnt].cu_offset + 3 - >= dbg->sectiondata[IDX_debug_info]->d_size))) + || dbg->sectiondata[IDX_debug_info]->d_buf == NULL)) goto invalid_dwarf; unsigned char *infop @@ -222,10 +220,10 @@ dwarf_getpubnames (dbg, callback, arg, offset) while (1) { /* READP points to the next offset/name pair. */ - if (__libdw_read_address_inc (dbg, IDX_debug_pubnames, &readp, - dbg->pubnames_sets[cnt].address_len, - &gl.die_offset)) - return -1l; + if (dbg->pubnames_sets[cnt].address_len == 4) + gl.die_offset = read_4ubyte_unaligned_inc (dbg, readp); + else + gl.die_offset = read_8ubyte_unaligned_inc (dbg, readp); /* If the offset is zero we reached the end of the set. */ if (gl.die_offset == 0) diff --git a/libdw/dwarf_nextcu.c b/libdw/dwarf_nextcu.c index abffe7ec0..91cd3f2ed 100644 --- a/libdw/dwarf_nextcu.c +++ b/libdw/dwarf_nextcu.c @@ -84,7 +84,7 @@ dwarf_nextcu (dwarf, off, next_off, header_sizep, abbrev_offsetp, /* This points into the .debug_info section to the beginning of the CU entry. */ - unsigned char *data = (unsigned char *) dwarf->sectiondata[IDX_debug_info]->d_buf; + unsigned char *data = dwarf->sectiondata[IDX_debug_info]->d_buf; unsigned char *bytes = data + off; /* The format of the CU header is described in dwarf2p1 7.5.1: @@ -146,7 +146,7 @@ dwarf_nextcu (dwarf, off, next_off, header_sizep, abbrev_offsetp, depends on whether this is a 32-bit or 64-bit DWARF definition. */ uint64_t abbrev_offset; if (__libdw_read_offset_inc (dwarf, IDX_debug_info, &bytes, - offset_size, &abbrev_offset)) + offset_size, &abbrev_offset, IDX_debug_abbrev)) return -1; if (abbrev_offsetp != NULL) diff --git a/libdw/dwarf_ranges.c b/libdw/dwarf_ranges.c index 7dbfb9f54..ccb4f6b7c 100644 --- a/libdw/dwarf_ranges.c +++ b/libdw/dwarf_ranges.c @@ -55,6 +55,51 @@ #include #include +/* 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 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. */ +internal_function int +__libdw_read_begin_end_pair_inc (Dwarf *dbg, int sec_index, + unsigned char **addr, int width, + Dwarf_Addr *beginp, Dwarf_Addr *endp, + Dwarf_Addr *basep) +{ + Dwarf_Addr escape + = width == 8 ? (Elf64_Addr) -1 : (Elf64_Addr) (Elf32_Addr) -1; + Dwarf_Addr begin, end; + + bool begin_relocated + = !READ_AND_RELOCATE (__libdw_relocate_address, begin); + bool end_relocated + = !READ_AND_RELOCATE (__libdw_relocate_address, end); + + /* Unrelocated escape for begin means base address selection. */ + if (begin == escape && !begin_relocated) + { + if (unlikely (end == escape)) + { + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } + + if (basep != NULL) + *basep = end; + return 1; + } + + /* Unrelocated pair of zeroes means end of range list. */ + if (begin == 0 && end == 0 && !begin_relocated && !end_relocated) + return 2; + + /* Don't check for begin_relocated == end_relocated. Serve the data + to the client even though it may be buggy. */ + *beginp = begin; + *endp = end; + + return 0; +} ptrdiff_t dwarf_ranges (Dwarf_Die *die, ptrdiff_t offset, Dwarf_Addr *basep, @@ -142,23 +187,21 @@ dwarf_ranges (Dwarf_Die *die, ptrdiff_t offset, Dwarf_Addr *basep, Dwarf_Addr begin; Dwarf_Addr end; - if (__libdw_read_address_inc (die->cu->dbg, - IDX_debug_ranges, &readp, - die->cu->address_size, &begin) - || __libdw_read_address_inc (die->cu->dbg, - IDX_debug_ranges, &readp, - die->cu->address_size, &end)) - return -1l; - - if (begin == ADDR_ESCAPE (die->cu->address_size)) + + switch (__libdw_read_begin_end_pair_inc (die->cu->dbg, IDX_debug_ranges, + &readp, die->cu->address_size, + &begin, &end, basep)) { - *basep = end; + case 0: + break; + case 1: goto next; + case 2: + return 0; + default: + return -1l; } - if (begin == 0 && end == 0) /* End of list entry. */ - return 0; - /* We have an address range entry. */ *startp = *basep + begin; *endp = *basep + end; diff --git a/libdw/libdwP.h b/libdw/libdwP.h index 4a1e4b2bf..be0b99a8e 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -422,7 +422,7 @@ extern int __dwarf_errno_internal (void); /* Reader hooks. */ -static inline int +static inline bool __libdw_in_section (Dwarf *dbg, int sec_index, unsigned char *addr, int width) { @@ -432,12 +432,14 @@ __libdw_in_section (Dwarf *dbg, int sec_index, || (void *)addr + width > data->d_buf + data->d_size) { __libdw_seterrno (DWARF_E_INVALID_OFFSET); - return 1; + return false; } - return 0; + return true; } +/* Relocation hooks return -1 on error, 0 if there is no relocation + and 1 if a relocation was present.*/ int __libdw_relocate_address (Dwarf *dbg, int sec_index, uintptr_t addr, int width, Dwarf_Addr *val) @@ -448,60 +450,65 @@ int __libdw_relocate_offset (Dwarf *dbg, int width, Dwarf_Off *val) internal_function; -int __libdw_relocate_length (Dwarf *dbg, - int sec_index, uintptr_t addr, - int width, Dwarf_Addr *val) - internal_function; - -#define READ_AND_RELOCATE(RELOC_HOOK) \ - { \ - int status; \ - if ((status = __libdw_in_section (dbg, sec_index, *addr, width))) \ - return status; \ +#define READ_AND_RELOCATE(RELOC_HOOK, VAL) \ + ({ \ + if (!__libdw_in_section (dbg, sec_index, *addr, width)) \ + return -1; \ \ - uintptr_t addr0 = (uintptr_t)*addr; \ - __typeof (*ret) val; \ + uintptr_t addr0 = (uintptr_t) *addr; \ \ if (width == 4) \ - val = read_4ubyte_unaligned_inc (dbg, *addr); \ + VAL = read_4ubyte_unaligned_inc (dbg, *addr); \ else \ { \ assert (width == 8); \ - val = read_8ubyte_unaligned_inc (dbg, *addr); \ + VAL = read_8ubyte_unaligned_inc (dbg, *addr); \ } \ \ - if ((status = RELOC_HOOK (dbg, sec_index, addr0, width, &val))) \ + int status = RELOC_HOOK (dbg, sec_index, addr0, width, &VAL); \ + if (status == -1) \ return status; \ - \ - *ret = val; \ - return 0; \ - } + status; \ + }) static inline int __libdw_read_address_inc (Dwarf *dbg, int sec_index, unsigned char **addr, int width, Dwarf_Addr *ret) { - READ_AND_RELOCATE (__libdw_relocate_address) + Dwarf_Addr val; + READ_AND_RELOCATE (__libdw_relocate_address, val); + *ret = val; + return 0; } static inline int __libdw_read_offset_inc (Dwarf *dbg, int sec_index, unsigned char **addr, - int width, Dwarf_Off *ret) + int width, Dwarf_Off *ret, int sec_ret) { - READ_AND_RELOCATE (__libdw_relocate_offset) -} + Dwarf_Off val; + READ_AND_RELOCATE (__libdw_relocate_offset, val); -static inline int -__libdw_read_length_inc (Dwarf *dbg, - int sec_index, unsigned char **addr, - int width, Dwarf_Addr *ret) -{ - READ_AND_RELOCATE (__libdw_relocate_length) + Elf_Data *data = dbg->sectiondata[sec_ret]; + if (!__libdw_in_section (dbg, sec_ret, data->d_buf + val, width)) + return -1; + + *ret = val; + return 0; } -#undef READ_AND_RELOCATE +/* 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 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, + Dwarf_Addr *beginp, Dwarf_Addr *endp, + Dwarf_Addr *basep) + internal_function; static inline int __libdw_read_address (Dwarf *dbg, @@ -515,24 +522,12 @@ __libdw_read_address (Dwarf *dbg, static inline int __libdw_read_offset (Dwarf *dbg, int sec_index, const unsigned char *addr, - int width, Dwarf_Addr *ret) + int width, Dwarf_Off *ret, int sec_ret) { return __libdw_read_offset_inc (dbg, sec_index, (unsigned char **)&addr, - width, ret); + width, ret, sec_ret); } -static inline int -__libdw_read_length (Dwarf *dbg, - int sec_index, const unsigned char *addr, - int width, Dwarf_Word *ret) -{ - return __libdw_read_length_inc (dbg, sec_index, (unsigned char **)&addr, - width, ret); -} - -#define ADDR_ESCAPE(width) \ - (width == 8 ? (Elf64_Addr)-1 : (Elf64_Addr)(Elf32_Addr)-1) - /* Aliases to avoid PLTs. */ diff --git a/libdw/libdw_readhooks.c b/libdw/libdw_readhooks.c index 93c986429..404f12aa7 100644 --- a/libdw/libdw_readhooks.c +++ b/libdw/libdw_readhooks.c @@ -72,13 +72,3 @@ __libdw_relocate_offset (Dwarf *dbg __attribute__ ((unused)), { return 0; } - -internal_function int -__libdw_relocate_length (Dwarf *dbg __attribute__ ((unused)), - int sec_index __attribute__ ((unused)), - uintptr_t addr __attribute__ ((unused)), - int width __attribute__ ((unused)), - Dwarf_Addr *val __attribute__ ((unused))) -{ - return 0; -}