diff options
author | Mark Wielaard <mark@klomp.org> | 2018-05-20 23:30:01 +0200 |
---|---|---|
committer | Mark Wielaard <mark@klomp.org> | 2018-05-25 15:07:58 +0200 |
commit | 6e3d2521a2b5a3b436901f52cfb9785887a7c961 (patch) | |
tree | 649d9c157ac680e9f99b61df772e429883602bf5 /libdw | |
parent | 184fd30d1a453dc165ffc187b22dec6d196522ad (diff) | |
download | elfutils-6e3d2521a2b5a3b436901f52cfb9785887a7c961.tar.gz |
libdw: Support DW_OP_addrx/constx and split DWARF addrx/constx support.
DW_OP_addrx/constx and GNU DebugFission DW_OP_GNU_addr/const_index take
as argument an index into the .debug_addr section for the associated CU.
This index gets resolved through dwarf_getlocation_attr. A new fake addr
CU is created per Dwarf for use with this new attribute. For split DWARF
files, the IDX_debug_addr gets replaced with the skeleton section and the
addr base is resolved immediately when constructing the split DWARF CU.
Move __libdw_cu_addr_base to libdwP.h to share with eu-readelf. Also
make it possible to resolve addrx[1234]/GNU_addr_index also as constant
indexes to (also) show when displaying these attributes in eu-readelf.
A new varlocs tests is added to test the resolving for both the DWARF4
and DWARF5 DW_OP variants. And now that addrx forms are resolved in
split DWARF files add the new DIEs with "single ranges" (those DIEs that
have a lowpc/highpc attribute pair) to run-all-dwarf-ranges.sh.
Signed-off-by: Mark Wielaard <mark@klomp.org>
Diffstat (limited to 'libdw')
-rw-r--r-- | libdw/ChangeLog | 19 | ||||
-rw-r--r-- | libdw/dwarf_begin_elf.c | 27 | ||||
-rw-r--r-- | libdw/dwarf_end.c | 12 | ||||
-rw-r--r-- | libdw/dwarf_formaddr.c | 18 | ||||
-rw-r--r-- | libdw/dwarf_formudata.c | 33 | ||||
-rw-r--r-- | libdw/dwarf_getlocation_attr.c | 31 | ||||
-rw-r--r-- | libdw/libdwP.h | 24 | ||||
-rw-r--r-- | libdw/libdw_find_split_unit.c | 14 |
8 files changed, 158 insertions, 20 deletions
diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 08c8f7be..f5689632 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,22 @@ +2018-05-21 Mark Wielaard <mark@klomp.org> + + * dwarf_begin_elf.c (valid_p): Add a fake_addr_cu to the result. + * dwarf_end.c (cu_free): Disconnect the fake_addr_cu from the split + dwarf if shared with skeleton. + (dwarf_end): release fake_addr_cu. + * dwarf_formaddr.c (__libdw_cu_addr_base): Move to... + * libdwP.h (__libdw_cu_addr_base): ... here. + (struct Dwarf): Add fake_addr_cu field. + * dwarf_formudata.c (dwarf_formudata): Handle + DW_FORM_GNU_addr_index and DW_FORM_addrx[1234]. + * dwarf_getlocation_attr.c (addr_valp): New static function. + (dwarf_getlocation_attr): Create attribute for values of + DW_OP_GNU_const_index, DW_OP_constx and DW_OP_GNU_addr_index and + DW_OP_addrx. + * libdw_find_split_unit.c (__libdw_find_split_unit): Connect + IDX_debug_addr sectiondata and fake_addr_cu between split and + skeleton. + 2018-05-20 Mark Wielaard <mark@klomp.org> * dwarf_cu_info.c: New file. diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c index 0e435c57..5d8e79e3 100644 --- a/libdw/dwarf_begin_elf.c +++ b/libdw/dwarf_begin_elf.c @@ -246,6 +246,33 @@ valid_p (Dwarf *result) } } + /* 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 + inside the .debug_addr section, if it exists. */ + if (result != NULL && result->sectiondata[IDX_debug_addr] != NULL) + { + result->fake_addr_cu = (Dwarf_CU *) calloc (1, sizeof (Dwarf_CU)); + if (unlikely (result->fake_addr_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_addr_cu->sec_idx = IDX_debug_addr; + result->fake_addr_cu->dbg = result; + result->fake_addr_cu->startp + = result->sectiondata[IDX_debug_addr]->d_buf; + result->fake_addr_cu->endp + = (result->sectiondata[IDX_debug_addr]->d_buf + + result->sectiondata[IDX_debug_addr]->d_size); + } + } + if (result != NULL) result->debugdir = __libdw_debugdir (result->elf->fildes); diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c index 4702f1b1..19546741 100644 --- a/libdw/dwarf_end.c +++ b/libdw/dwarf_end.c @@ -59,7 +59,12 @@ cu_free (void *arg) /* Free split dwarf one way (from skeleton to split). */ if (p->unit_type == DW_UT_skeleton && p->split != NULL && p->split != (void *)-1) - INTUSE(dwarf_end) (p->split->dbg); + { + /* The fake_addr_cu might be shared, only release one. */ + if (p->dbg->fake_addr_cu == p->split->dbg->fake_addr_cu) + p->split->dbg->fake_addr_cu = NULL; + INTUSE(dwarf_end) (p->split->dbg); + } } @@ -108,6 +113,11 @@ dwarf_end (Dwarf *dwarf) cu_free (dwarf->fake_loc_cu); free (dwarf->fake_loc_cu); } + if (dwarf->fake_addr_cu != NULL) + { + cu_free (dwarf->fake_addr_cu); + free (dwarf->fake_addr_cu); + } /* Did we find and allocate the alt Dwarf ourselves? */ if (dwarf->alt_fd != -1) diff --git a/libdw/dwarf_formaddr.c b/libdw/dwarf_formaddr.c index c917deac..3c89a5d2 100644 --- a/libdw/dwarf_formaddr.c +++ b/libdw/dwarf_formaddr.c @@ -136,21 +136,3 @@ dwarf_formaddr (Dwarf_Attribute *attr, Dwarf_Addr *return_addr) 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_GNU_addr_base, &attr) != NULL - || 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 316ad865..d56e7dc1 100644 --- a/libdw/dwarf_formudata.c +++ b/libdw/dwarf_formudata.c @@ -288,6 +288,39 @@ dwarf_formudata (Dwarf_Attribute *attr, Dwarf_Word *return_uval) get_sleb128_unchecked (*return_uval, datap); break; + /* These are indexes into the .debug_addr section, normally resolved + with dwarf_formaddr. Here treat as constants. */ + case DW_FORM_GNU_addr_index: + case DW_FORM_addrx: + if (datap >= endp) + goto invalid; + get_uleb128 (*return_uval, datap, endp); + break; + + case DW_FORM_addrx1: + if (datap >= endp - 1) + goto invalid; + *return_uval = *datap; + break; + + case DW_FORM_addrx2: + if (datap >= endp - 2) + goto invalid; + *return_uval = read_2ubyte_unaligned (attr->cu->dbg, datap); + break; + + case DW_FORM_addrx3: + if (datap >= endp - 3) + goto invalid; + *return_uval = read_3ubyte_unaligned (attr->cu->dbg, datap); + break; + + case DW_FORM_addrx4: + if (datap >= endp - 4) + goto invalid; + *return_uval = read_4ubyte_unaligned (attr->cu->dbg, datap); + break; + default: __libdw_seterrno (DWARF_E_NO_CONSTANT); return -1; diff --git a/libdw/dwarf_getlocation_attr.c b/libdw/dwarf_getlocation_attr.c index 162330f6..62ef47ab 100644 --- a/libdw/dwarf_getlocation_attr.c +++ b/libdw/dwarf_getlocation_attr.c @@ -52,6 +52,18 @@ attr_form_cu (Dwarf_Attribute *attr) } } +static unsigned char * +addr_valp (Dwarf_CU *cu, Dwarf_Word index) +{ + Elf_Data *debug_addr = cu->dbg->sectiondata[IDX_debug_addr]; + Dwarf_Word offset = __libdw_cu_addr_base (cu) + (index * cu->address_size); + if (debug_addr == NULL) + /* This is really an error, will trigger with dwarf_formaddr. */ + return (unsigned char *) offset; + + return (unsigned char *) debug_addr->d_buf + offset; +} + int dwarf_getlocation_attr (Dwarf_Attribute *attr, const Dwarf_Op *op, Dwarf_Attribute *result) { @@ -83,6 +95,25 @@ dwarf_getlocation_attr (Dwarf_Attribute *attr, const Dwarf_Op *op, Dwarf_Attribu result->cu = attr_form_cu (attr); break; + case DW_OP_GNU_const_index: + case DW_OP_constx: + result->code = DW_AT_const_value; + if (attr->cu->address_size == 4) + result->form = DW_FORM_data4; + else + result->form = DW_FORM_data8; + result->valp = addr_valp (attr->cu, op->number); + result->cu = attr->cu->dbg->fake_addr_cu; + break; + + case DW_OP_GNU_addr_index: + case DW_OP_addrx: + result->code = DW_AT_low_pc; + result->form = DW_FORM_addr; + result->valp = addr_valp (attr->cu, op->number); + result->cu = attr->cu->dbg->fake_addr_cu; + break; + case DW_OP_call2: case DW_OP_call4: case DW_OP_call_ref: diff --git a/libdw/libdwP.h b/libdw/libdwP.h index 2b5b5ead..82ee5d03 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -204,6 +204,9 @@ struct Dwarf came from a location list entry in dwarf_getlocation_attr. */ struct Dwarf_CU *fake_loc_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 implementation is not usable in libraries. */ @@ -947,7 +950,26 @@ const char *__libdw_getcompdir (Dwarf_Die *cudie); Dwarf_Addr __libdw_cu_base_address (Dwarf_CU *cu); /* Get the address base for the CU, fetches it when not yet set. */ -Dwarf_Off __libdw_cu_addr_base (Dwarf_CU *cu); +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. */ diff --git a/libdw/libdw_find_split_unit.c b/libdw/libdw_find_split_unit.c index bd48b9e5..78c9a2a5 100644 --- a/libdw/libdw_find_split_unit.c +++ b/libdw/libdw_find_split_unit.c @@ -88,6 +88,20 @@ __libdw_find_split_unit (Dwarf_CU *cu) cu->split = split; split->split = cu; + /* 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 = cu->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 (cu); + sdbg->fake_addr_cu = dbg->fake_addr_cu; + } + /* We have everything we need from this ELF file. And we are going to close the fd to not run out of file |