Recognize the new .debug_addr section. The CU will now hold a new address base offset in that section for that CU. dwarf_form_addr will decode DW_FORM_addrx[1234] and return addresses using that address base from the .debug_addr. A new internal function read_3ubyte_unaligned will try to read a 24-bit value depending on endianness of the underlying file.
Signed-off-by: Mark Wielaard <m...@klomp.org> --- libdw/ChangeLog | 14 ++++++ libdw/dwarf_begin_elf.c | 1 + libdw/dwarf_error.c | 3 +- libdw/dwarf_formaddr.c | 113 ++++++++++++++++++++++++++++++++++++++++++++---- libdw/dwarf_formudata.c | 8 ++++ libdw/libdwP.h | 9 ++++ libdw/memory-access.h | 50 ++++++++++++++++++++- src/ChangeLog | 4 ++ src/readelf.c | 5 +++ 9 files changed, 196 insertions(+), 11 deletions(-) diff --git a/libdw/ChangeLog b/libdw/ChangeLog index f552644..8423cb3 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,17 @@ +2018-03-22 Mark Wielaard <m...@klomp.org> + + * dwarf_begin_elf.c (dwarf_scnnames): Add IDX_debug_addr. + * dwarf_error.c (errmsgs): Add DWARF_E_NO_DEBUG_ADDR. + * dwarf_formaddr.c (dwarf_formaddr): Handle DW_FORM_addrx[1234]. + (__libdw_cu_addr_base): New function. + * dwarf_formudata.c (dwarf_formudata): Handle DW_AT_addr_base as + addrptr. + * libdwP.h: Add IDX_debug_addr and DWARF_E_NO_DEBUG_ADDR. + (struct Dwarf_CU): Add addr_base field. + (__libdw_cu_addr_base): New function definition. + * memory-access.h (file_byte_order): New static function. + (read_3ubyte_unaligned): New inline function. + 2018-03-06 Mark Wielaard <m...@klomp.org> * dwarf.h: Add DW_OP_implicit_pointer, DW_OP_addrx, DW_OP_constx, diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c index 6834ac5..1ffa6c9 100644 --- a/libdw/dwarf_begin_elf.c +++ b/libdw/dwarf_begin_elf.c @@ -52,6 +52,7 @@ static const char dwarf_scnnames[IDX_last][18] = [IDX_debug_info] = ".debug_info", [IDX_debug_types] = ".debug_types", [IDX_debug_abbrev] = ".debug_abbrev", + [IDX_debug_addr] = ".debug_addr", [IDX_debug_aranges] = ".debug_aranges", [IDX_debug_line] = ".debug_line", [IDX_debug_frame] = ".debug_frame", diff --git a/libdw/dwarf_error.c b/libdw/dwarf_error.c index 939ec04..212f32e 100644 --- a/libdw/dwarf_error.c +++ b/libdw/dwarf_error.c @@ -95,7 +95,8 @@ static const char *errmsgs[] = [DWARF_E_NO_ALT_DEBUGLINK] = N_("no alternative debug link found"), [DWARF_E_INVALID_OPCODE] = N_("invalid opcode"), [DWARF_E_NOT_CUDIE] = N_("not a CU (unit) DIE"), - [DWARF_E_UNKNOWN_LANGUAGE] = N_("unknown language code") + [DWARF_E_UNKNOWN_LANGUAGE] = N_("unknown language code"), + [DWARF_E_NO_DEBUG_ADDR] = N_(".debug_addr section missing"), }; #define nerrmsgs (sizeof (errmsgs) / sizeof (errmsgs[0])) diff --git a/libdw/dwarf_formaddr.c b/libdw/dwarf_formaddr.c index ddc4838..25e6970 100644 --- a/libdw/dwarf_formaddr.c +++ b/libdw/dwarf_formaddr.c @@ -1,7 +1,6 @@ /* Return address represented by attribute. - Copyright (C) 2003-2010 Red Hat, Inc. + Copyright (C) 2003-2010, 2018 Red Hat, Inc. This file is part of elfutils. - Written by Ulrich Drepper <drep...@redhat.com>, 2003. This file is free software; you can redistribute it and/or modify it under the terms of either @@ -41,17 +40,115 @@ dwarf_formaddr (Dwarf_Attribute *attr, Dwarf_Addr *return_addr) if (attr == NULL) return -1; - if (unlikely (attr->form != DW_FORM_addr)) + Dwarf_Word idx; + Dwarf_CU *cu = attr->cu; + Dwarf *dbg = cu->dbg; + const unsigned char *datap = attr->valp; + const unsigned char *endp = attr->cu->endp; + switch (attr->form) { - __libdw_seterrno (DWARF_E_NO_ADDR); - return -1; + /* There is one form that just encodes the whole address. */ + case DW_FORM_addr: + if (__libdw_read_address (dbg, cu_sec_idx (cu), datap, + cu->address_size, return_addr)) + return -1; + return 0; + + /* All others encode an index into the .debug_addr section where + the address can be found. */ + case DW_FORM_addrx: + if (datap >= endp) + { + invalid: + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } + get_uleb128 (idx, datap, endp); + break; + + case DW_FORM_addrx1: + if (datap >= endp - 1) + goto invalid; + idx = *datap; + break; + + case DW_FORM_addrx2: + if (datap >= endp - 2) + goto invalid; + idx = read_2ubyte_unaligned (dbg, datap); + break; + + case DW_FORM_addrx3: + if (datap >= endp - 3) + goto invalid; + idx = read_3ubyte_unaligned (dbg, datap); + break; + + case DW_FORM_addrx4: + if (datap >= endp - 4) + goto invalid; + idx = read_4ubyte_unaligned (dbg, datap); + break; + + default: + __libdw_seterrno (DWARF_E_NO_ADDR); + return -1; } - if (__libdw_read_address (attr->cu->dbg, - cu_sec_idx (attr->cu), attr->valp, - attr->cu->address_size, return_addr)) + /* So we got an index. Lets see if it is valid and we can get the actual + address. */ + + Dwarf_Off addr_off = __libdw_cu_addr_base (cu); + if (addr_off == (Dwarf_Off) -1) return -1; + if (dbg->sectiondata[IDX_debug_addr] == NULL) + { + __libdw_seterrno (DWARF_E_NO_DEBUG_ADDR); + return -1; + } + + /* The section should at least contain room for one address. */ + int address_size = cu->address_size; + if (cu->address_size > dbg->sectiondata[IDX_debug_addr]->d_size) + { + invalid_offset: + __libdw_seterrno (DWARF_E_INVALID_OFFSET); + return -1; + } + + if (addr_off > (dbg->sectiondata[IDX_debug_addr]->d_size + - address_size)) + goto invalid_offset; + + idx *= address_size; + if (idx > (dbg->sectiondata[IDX_debug_addr]->d_size + - address_size - addr_off)) + goto invalid_offset; + + datap = dbg->sectiondata[IDX_debug_addr]->d_buf + addr_off + idx; + if (address_size == 4) + *return_addr = read_4ubyte_unaligned (dbg, datap); + else + *return_addr = read_8ubyte_unaligned (dbg, datap); + return 0; } INTDEF(dwarf_formaddr) + +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; + if (dwarf_attr (&cu_die, DW_AT_addr_base, &attr) != NULL) + { + Dwarf_Word off; + if (dwarf_formudata (&attr, &off) == 0) + cu->addr_base = off; + } + } + + return cu->addr_base; +} diff --git a/libdw/dwarf_formudata.c b/libdw/dwarf_formudata.c index 95872d6..5d5fc63 100644 --- a/libdw/dwarf_formudata.c +++ b/libdw/dwarf_formudata.c @@ -182,6 +182,14 @@ dwarf_formudata (Dwarf_Attribute *attr, Dwarf_Word *return_uval) return -1; break; + case DW_AT_addr_base: + /* addrptr */ + if (__libdw_formptr (attr, IDX_debug_addr, + DWARF_E_NO_DEBUG_ADDR, NULL, + return_uval) == NULL) + return -1; + break; + default: /* sec_offset can only be used by one of the above attrs. */ if (attr->form == DW_FORM_sec_offset) diff --git a/libdw/libdwP.h b/libdw/libdwP.h index ad55558..ba50c35 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -74,6 +74,7 @@ enum IDX_debug_types, IDX_debug_abbrev, IDX_debug_aranges, + IDX_debug_addr, IDX_debug_line, IDX_debug_frame, IDX_debug_loc, @@ -131,6 +132,7 @@ enum DWARF_E_INVALID_OPCODE, DWARF_E_NOT_CUDIE, DWARF_E_UNKNOWN_LANGUAGE, + DWARF_E_NO_DEBUG_ADDR, }; @@ -324,6 +326,10 @@ struct Dwarf_CU /* Known location lists. */ void *locs; + /* The offset into the .debug_addr section where index zero begins. + Don't access directly, call __libdw_cu_addr_base. */ + Dwarf_Off addr_base; + /* Memory boundaries of this CU. */ void *startp; void *endp; @@ -865,6 +871,9 @@ 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 address base for the CU, fetches it when not yet set. */ +Dwarf_Off __libdw_cu_addr_base (Dwarf_CU *cu); + /* Given a file descriptor, dir and file returns a full path. If the file is absolute (starts with a /) a copy of file is returned. If the file isn't absolute, but dir is absolute, then a path that is diff --git a/libdw/memory-access.h b/libdw/memory-access.h index 5f96a14..22918cb 100644 --- a/libdw/memory-access.h +++ b/libdw/memory-access.h @@ -1,7 +1,6 @@ /* Unaligned memory access functionality. - Copyright (C) 2000-2014 Red Hat, Inc. + Copyright (C) 2000-2014, 2018 Red Hat, Inc. This file is part of elfutils. - Written by Ulrich Drepper <drep...@redhat.com>, 2001. This file is free software; you can redistribute it and/or modify it under the terms of either @@ -31,6 +30,7 @@ #define _MEMORY_ACCESS_H 1 #include <byteswap.h> +#include <endian.h> #include <limits.h> #include <stdint.h> @@ -315,6 +315,52 @@ read_8sbyte_unaligned_1 (bool other_byte_order, const void *p) Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 8); \ t_; }) +/* 3ubyte reads are only used for DW_FORM_addrx3 and DW_FORM_strx3. + And are probably very rare. They are not optimized. They are + handled as if reading a 4byte value with the first (for big endian) + or last (for little endian) byte zero. */ + +static inline int +file_byte_order (bool other_byte_order) +{ +#if __BYTE_ORDER == __LITTLE_ENDIAN + return other_byte_order ? __BIG_ENDIAN : __LITTLE_ENDIAN; +#else + return other_byte_order ? __LITTLE_ENDIAN : __BIG_ENDIAN; +#endif +} + +static inline uint32_t +read_3ubyte_unaligned (Dwarf *dbg, const unsigned char *p) +{ + union + { + uint32_t u4; + unsigned char c[4]; + } d; + bool other_byte_order = dbg->other_byte_order; + + if (file_byte_order (other_byte_order) == __BIG_ENDIAN) + { + d.c[0] = 0x00; + d.c[1] = p[0]; + d.c[2] = p[1]; + d.c[3] = p[2]; + } + else + { + d.c[0] = p[0]; + d.c[1] = p[1]; + d.c[2] = p[2]; + d.c[3] = 0x00; + } + + if (other_byte_order) + return bswap_32 (d.u4); + else + return d.u4; +} + #define read_addr_unaligned_inc(Nbytes, Dbg, Addr) \ (assert ((Nbytes) == 4 || (Nbytes) == 8), \ diff --git a/src/ChangeLog b/src/ChangeLog index 1ad6b3d..7b8084f 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,7 @@ +2018-03-22 Mark Wielaard <m...@klomp.org> + + * readelf.c (attr_callback): Handle DW_FORM_addrx[1234]. + 2018-03-27 Mark Wielaard <m...@klomp.org> * readelf.c (attr_callback): Print dwarf_dieoffset as %PRIx64, diff --git a/src/readelf.c b/src/readelf.c index 4e35b61..c1d6ac1 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -6064,6 +6064,11 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) switch (form) { case DW_FORM_addr: + case DW_FORM_addrx: + case DW_FORM_addrx1: + case DW_FORM_addrx2: + case DW_FORM_addrx3: + case DW_FORM_addrx4: if (!cbargs->silent) { Dwarf_Addr addr; -- 1.8.3.1