[PATCH] 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 --- libdw/ChangeLog | 19 libdw/dwarf_begin_elf.c | 27 + libdw/dwarf_end.c | 12 ++- libdw/dwarf_formaddr.c| 18 libdw/dwarf_formudata.c | 33 ++ libdw/dwarf_getlocation_attr.c| 31 ++ libdw/libdwP.h| 24 - libdw/libdw_find_split_unit.c | 14 +++ src/ChangeLog | 9 ++ src/readelf.c | 64 ++-- tests/ChangeLog | 14 +++ tests/Makefile.am | 2 + tests/addrx_constx-4.dwo.bz2 | Bin 0 -> 809 bytes tests/addrx_constx-5.dwo.bz2 | Bin 0 -> 824 bytes tests/run-all-dwarf-ranges.sh | 24 + tests/run-varlocs.sh | 208 ++ tests/testfile-addrx_constx-4.bz2 | Bin 0 -> 2851 bytes tests/testfile-addrx_constx-5.bz2 | Bin 0 -> 2847 bytes tests/varlocs.c | 65 +--- 19 files changed, 523 insertions(+), 41 deletions(-) create mode 100644 tests/addrx_constx-4.dwo.bz2 create mode 100644 tests/addrx_constx-5.dwo.bz2 create mode 100755 tests/testfile-addrx_constx-4.bz2 create mode 100755 tests/testfile-addrx_constx-5.bz2 diff --git a/libdw/ChangeLog b/libdw/ChangeLog index c575f64..e7fd217 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,22 @@ +2018-05-21 Mark Wielaard + + * 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 * dwarf_cu_info.c: New file. diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c index 0e435c5..5d8e79e 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 4702f1b..1954674 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 != (
[PATCH] libdw: Handle all address FORMs for dwarf_highpc, handle errors better.
dwarf_highpc can use any address FORM, not just DW_FORM_addr. Just try whether the address can be resolved as address. Always set error when attribute couldn't be found or resolved. When calculating the base address for a CU don't try to second guess the error code, just treat an error the same as the attribute not being there. Signed-off-by: Mark Wielaard --- libdw/ChangeLog | 7 +++ libdw/dwarf_getlocation.c | 17 - libdw/dwarf_highpc.c | 10 ++ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/libdw/ChangeLog b/libdw/ChangeLog index e7fd217..3045359 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,10 @@ +2018-05-22 Mark Wielaard + + * dwarf_getlocation.c (__libdw_cu_base_address): Treat errors of + getting lowpc or entrypc the same as missing base address (zero). + * dwarf_highpc (dwarf_highpc): Handle any address form. Always set + error when attribute could not be found. + 2018-05-21 Mark Wielaard * dwarf_begin_elf.c (valid_p): Add a fake_addr_cu to the result. diff --git a/libdw/dwarf_getlocation.c b/libdw/dwarf_getlocation.c index 116fa71..ade3e6c 100644 --- a/libdw/dwarf_getlocation.c +++ b/libdw/dwarf_getlocation.c @@ -682,19 +682,10 @@ __libdw_cu_base_address (Dwarf_CU *cu) &attr_mem), &base) != 0) { - int err = INTUSE(dwarf_errno) (); - if (err != 0) - { - __libdw_seterrno (err); - base = (Dwarf_Addr) -1; - } - else - { - /* The compiler provided no base address when it should -have. Buggy GCC does this when it used absolute -addresses in the location list and no DW_AT_ranges. */ - base = 0; - } + /* The compiler provided no base address when it should +have. Buggy GCC does this when it used absolute +addresses in the location list and no DW_AT_ranges. */ + base = 0; } cu->base_address = base; } diff --git a/libdw/dwarf_highpc.c b/libdw/dwarf_highpc.c index 1baffa7..5b2f0fd 100644 --- a/libdw/dwarf_highpc.c +++ b/libdw/dwarf_highpc.c @@ -47,10 +47,10 @@ dwarf_highpc (Dwarf_Die *die, Dwarf_Addr *return_addr) attr_high = INTUSE(dwarf_attr) (die, DW_AT_high_pc, &attr_high_mem); if (attr_high == NULL) -return -1; +goto no_addr; - if (attr_high->form == DW_FORM_addr) -return INTUSE(dwarf_formaddr) (attr_high, return_addr); + if (INTUSE(dwarf_formaddr) (attr_high, return_addr) == 0) +return 0; /* DWARF 4 allows high_pc to be a constant offset from low_pc. */ if (INTUSE(dwarf_lowpc) (die, return_addr) == 0) @@ -61,8 +61,10 @@ dwarf_highpc (Dwarf_Die *die, Dwarf_Addr *return_addr) *return_addr += uval; return 0; } - __libdw_seterrno (DWARF_E_NO_ADDR); } + +no_addr: + __libdw_seterrno (DWARF_E_NO_ADDR); return -1; } INTDEF(dwarf_highpc) -- 1.8.3.1
Re: [PATCH] libdw: Handle split dwarf debuglines.
On Fri, 2018-05-18 at 12:58 +0200, Mark Wielaard wrote: > Split DWARF .dwo files do contain a .debug_line section, but only with > the file table, there is no actual line program. Also split DWARF CU DIEs > don't have a DW_AT_stmt_list attribute. To get at the file (and dir) table > for a split unit DIE take just the files from the .debug_line section > (at offset zero). To get the full line table use the skeleton DIE (which > does have a DW_AT_stmt_list). Pushed to master.
Re: get backtrace of KVM VM from host
Hi, On Mon, 2018-05-21 at 10:26 +0200, Justin Cinkelj wrote: > Is it possible to get stack backtrace into KVM VM from the host side? > So > if I run './stack -p PID' (stack from elfutilfs > https://sourceware.org/elfutils/), I get backtrace of some process. I > would like to do the same for VM. I can assume VM will run only a kernel > (a unikernel, like OSv or IncludeOS), so most/all debug symbols will be > there in a single file, and at least IncludeOS doesnt load any code > beside its own kernel. > > I did notice KVM_GET_REGS and KVM_SET_MEMORY_REGION, and at least for > > trivial examples (like https://github.com/dpw/kvm-hello-world) this > provides enough information to track which code was loaded into VM, > observe current stack content and registers. I can only guess much more > work is required to get similar result with qemu-kvm. Hence I'm asking > if this is already implemented. Providing the registers and memory view inside the KVM VM would be the first step. elfutils would also need to know the memory/ELF process layout. For a normal process that would come from e.g. /proc/pid/maps. Using such a layout eu-stack would then be able to find the unwind tables and symbols associated with a particular address. I believe qemu already has an gdb stub that gdb can use to get at the registers, memory and process layout. Maybe you could adapt that provide the information needed. Cheers, Mark
Re: get backtrace of KVM VM from host
Something like that was suggested at KVM devel list too. I was able to get an useful backtrace for a trivial VM (a single ELF file, VM code runs directly from (virtual) physical memory). Well, that was more to learn a bit about elfutils than anything else. A more realistic VM will be more difficult, I guess. Justin On 05/22/2018 04:04 PM, Mark Wielaard wrote: Hi, On Mon, 2018-05-21 at 10:26 +0200, Justin Cinkelj wrote: Is it possible to get stack backtrace into KVM VM from the host side? So if I run './stack -p PID' (stack from elfutilfs https://sourceware.org/elfutils/), I get backtrace of some process. I would like to do the same for VM. I can assume VM will run only a kernel (a unikernel, like OSv or IncludeOS), so most/all debug symbols will be there in a single file, and at least IncludeOS doesnt load any code beside its own kernel. I did notice KVM_GET_REGS and KVM_SET_MEMORY_REGION, and at least for trivial examples (like https://github.com/dpw/kvm-hello-world) this provides enough information to track which code was loaded into VM, observe current stack content and registers. I can only guess much more work is required to get similar result with qemu-kvm. Hence I'm asking if this is already implemented. Providing the registers and memory view inside the KVM VM would be the first step. elfutils would also need to know the memory/ELF process layout. For a normal process that would come from e.g. /proc/pid/maps. Using such a layout eu-stack would then be able to find the unwind tables and symbols associated with a particular address. I believe qemu already has an gdb stub that gdb can use to get at the registers, memory and process layout. Maybe you could adapt that provide the information needed. Cheers, Mark
Re: [PATCH] libdw: Handle GNU DebugFission split ranges.
On Sat, May 19, 2018 at 04:03:51PM +0200, Mark Wielaard wrote: > GNU DebugFission split dwarf handles DW_FORM_sec_offset specially for > attributes that point to ranges. The .debug_ranges section is not in > the .dwo file, but in the main/skeleton object file. The sec_offset is > not relocated (in the ELF file), but is an offset against the skeleton > DIE DW_AT_GNU_ranges_base attribute. dwarf_formudata is changed so it > still looks like a normal offset ptr into the .debug_ranges section. > dwarf_ranges is adapted to look for the .debug_ranges in the main object > file. dwarf_highpc and dwarf_lowpc now handle the highpc and lowpc > attributes being inherited for the split unit DIE from the skeleton. > > A new testcase is added to iterate over all ranges in a split GNU > DebugFission file. Pushed to master.