Re: [PATCH] libdw: Handle GNU DebugFission split ranges.
On Sat, 2018-05-19 at 16:03 +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. After double checking the test results after incorporating the full DWARF5 rnglists support into readelf and libdw I found an embarrassing bug. We didn't initialize the CU ranges_base causing some bad test results. We also didn't handle bad DWARF correctly in one case. I am pushing the following fixes for this.From c2d14cc492aa7fd28740d5789fede64ce81a063b Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Thu, 24 May 2018 15:20:25 +0200 Subject: [PATCH] libdw: Initialize ranges_base, add invalid DWARF test and fix expected output. We never initialized the CU ranges_base, which meant we didn't actually calculate it correctly. This caused bad ranges on some DIEs. The expected output in the testcase was wrong. We also crashed on invalid dwarf. Signed-off-by: Mark Wielaard --- libdw/ChangeLog | 5 + libdw/dwarf_ranges.c | 6 ++ libdw/libdw_findcu.c | 1 + tests/ChangeLog | 7 ++- tests/get-units-invalid.c | 7 +++ tests/run-all-dwarf-ranges.sh | 7 --- 6 files changed, 29 insertions(+), 4 deletions(-) diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 4db0f5c..c302628 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,8 @@ +2018-05-24 Mark Wielaard + + * dwarf_ranges.c (dwarf_ranges): Check for NULL cu. + * libdw_findcu.c (__libdw_intern_next_unit): Initialize ranges_base. + 2018-05-18 Mark Wielaard * dwarf_formudata.c (__libdw_formptr): Handle the special case diff --git a/libdw/dwarf_ranges.c b/libdw/dwarf_ranges.c index b0450cf..52a61ee 100644 --- a/libdw/dwarf_ranges.c +++ b/libdw/dwarf_ranges.c @@ -123,6 +123,12 @@ dwarf_ranges (Dwarf_Die *die, ptrdiff_t offset, Dwarf_Addr *basep, /* We have to look for a noncontiguous range. */ size_t secidx = IDX_debug_ranges; Dwarf_CU *cu = die->cu; + if (cu == NULL) +{ + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; +} + const Elf_Data *d = cu->dbg->sectiondata[secidx]; if (d == NULL && cu->unit_type == DW_UT_split_compile) { diff --git a/libdw/libdw_findcu.c b/libdw/libdw_findcu.c index d22ddae..83c2eb1 100644 --- a/libdw/libdw_findcu.c +++ b/libdw/libdw_findcu.c @@ -121,6 +121,7 @@ __libdw_intern_next_unit (Dwarf *dbg, bool debug_types) newp->base_address = (Dwarf_Addr) -1; newp->addr_base = (Dwarf_Off) -1; newp->str_off_base = (Dwarf_Off) -1; + newp->ranges_base = (Dwarf_Off) -1; newp->startp = data->d_buf + newp->start; newp->endp = data->d_buf + newp->end; diff --git a/tests/ChangeLog b/tests/ChangeLog index a021a01..86bcf9d 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,6 +1,11 @@ +2018-05-24 Mark Wielaard + + * get-units-invalid.c (main): Add check for invalid dwarf_ranges. + * run-all-dwarf-ranges.sh: Correct expected output. + 2018-05-18 Mark Wielaard - * tests/Makefiles.am (check_PROGRAMS): Add all-dwarf-ranges. + * Makefiles.am (check_PROGRAMS): Add all-dwarf-ranges. (TESTS): Add run-all-dwarf-ranges.sh. (EXTRA_DIST): Add run-all-dwarf-ranges.sh, testfilesplitranges4.debug.bz2, testfile-ranges-hello.dwo.bz2 diff --git a/tests/get-units-invalid.c b/tests/get-units-invalid.c index 9ec16ee..58b32c0 100644 --- a/tests/get-units-invalid.c +++ b/tests/get-units-invalid.c @@ -79,6 +79,13 @@ main (int argc, char *argv[]) dwarf_diename (&result)); return -1; } + Dwarf_Addr base, start, end; + if (dwarf_ranges (&subdie, 0, &base, &start, &end) != -1) + { + printf ("Should NOT have a ranges: %s\n", + dwarf_diename (&result)); + return -1; + } } else if (unit_type == DW_UT_type) printf ("subdie: %s\n", dwarf_diename (&subdie)); diff --git a/tests/run-all-dwarf-ranges.sh b/tests/run-all-dwarf-ranges.sh index 0bd641b..ba5528d 100755 --- a/tests/run-all-dwarf-ranges.sh +++ b/tests/run-all-dwarf-ranges.sh @@ -37,11 +37,12 @@ die: world.c (11) 400500..400567 die: happy (1d) - 8009e0..8009ff - 8008e0..8008f7 + 40051c..400526 + 400530..400534 + 400535..40053f die: sad (1d) - 400530..400534 + 40051c..400526 400535..40053f EOF -- 1.8.3.1
Re: [PATCH] libdw: Add new dwarf_cu_info function.
On Sun, 2018-05-20 at 15:21 +0200, Mark Wielaard wrote: > This allows getting a (split) subdie lazily, only when needed. > All arguments to dwarf_get_units are optional. When not given > then unit DIE and sub DIE are not looked up. This new function > allows them to be looked up when not immediately retrieved with > dwarf_get_units, or for a Dwarf_CU gotten through some other way. > > Add a new testcase to make sure the results of calling dwarf_cu_info > and dwarf_get_units are consistent. Pushed to master.
Re: [PATCH] readelf: print split CUs when given --debug-dump=info+
On Sun, 2018-05-20 at 21:28 +0200, Mark Wielaard wrote: > To show the difference between "regular" CUs and split CUs print > offsets and references between { and } instead of [ and ]. > > When --debug-dump=info+ is given (implied by -w) instead of > --debug-dump=info any skeleton unit will be immediately followed > by the corresponding split compile unit (from the .dwo file). > > DWARF section [27] '.debug_info' at offset 0x1075: > [Offset] > Compilation unit at offset 0: > Version: 4, Abbreviation section offset: 0, Address size: 8, Offset > size: 4 > Unit type: skeleton (4), Unit id: 0xc152129eb4b99599 > [ b] compile_unit abbrev: 1 > ranges (sec_offset) range list [ 0] > low_pc (addr) +00 > stmt_list(sec_offset) 0 > GNU_dwo_name (strp) "foo.dwo" > comp_dir (strp) "/tmp" > GNU_pubnames (flag_present) yes > GNU_addr_base(sec_offset) 0 > GNU_dwo_id (data8) 0xc152129eb4b99599 > GNU_ranges_base (sec_offset) 0 > Split compilation unit at offset 0: > Version: 4, Abbreviation section offset: 0, Address size: 8, Offset > size: 4 > Unit type: skeleton (4), Unit id: 0xc152129eb4b99599 > { b} compile_unit abbrev: 1 > producer (GNU_str_index) "GNU C11 7.3.0 > -gsplit-dwarf -g" > language (data1) C99 (12) > name (GNU_str_index) "foo.c" > comp_dir (GNU_str_index) "/tmp" > GNU_dwo_id (data8) 0xc152129eb4b99599 > {18}subprogram abbrev: 2 > external (flag_present) yes > name (GNU_str_index) "main" > decl_file(data1) foo.c (1) > decl_line(data1) 1 > type (ref4) {2b} > high_pc (data8) 3 > frame_base (exprloc) > [ 0] call_frame_cfa > GNU_all_call_sites (flag_present) yes > {2b}base_typeabbrev: 3 > byte_size(data1) 4 > encoding (data1) signed (5) > name (string) "int" Pushed to master.
Re: dwarf_begin_elf() won't create handle without .debug_* sections
On Wed, 2018-05-23 at 20:09 +, Sasha Da Rocha Pinheiro wrote: > Hi all, > > I have some binaries that do not have .debug_* sections but have > .eh_frame and .gcc_except_table. > Looking at: > https://sourceware.org/git/?p=elfutils.git;a=blob;f=libdw/dwarf_b > egin_elf.c;hb=144b73c49acf3ed894e4635aedb9b0d1208ade2e#l50 > it seems that dwarf_begin_elf() will not create a Dwarf handle for > this file. Am I correct? Yes, this is correct. libdw relies on having at least a .debug_info section. See the valid_p () function: /* We looked at all the sections. Now determine whether all the sections with debugging information we need are there. XXX Which sections are absolutely necessary? Add tests if necessary. For now we require only .debug_info. Hopefully this is correct. */ if (likely (result != NULL) && unlikely (result->sectiondata[IDX_debug_info] == NULL)) { Dwarf_Sig8_Hash_free (&result->sig8_hash); __libdw_seterrno (DWARF_E_NO_DWARF); free (result); result = NULL; } I have been a little reluctant to change this because I am afraid that there is code that relies on at least sectiondata[IDX_debug_info] never being NULL. And in general (you point out exceptions below) most libdw data structures rely on having an associated DWARF CU DIE (which will come from the .debug_info section). But I certainly wouldn't mind if someone did some testing and changed the above "sanity" check to allow to create a Dwarf *handle in more cases. > So, the functions > dwarf_cfi_addrframe, > dwarf_frame_info, > dwarf_frame_cfa, and > dwarf_frame_register > will get info from .debug_frame while dwarf_next_cfi can get info > either from .debug_frame or .gcc_except_table, but without some > abstractions? > > Since > /* Opaque type representing a CFI section found in a DWARF or ELF > file. */ > typedef struct Dwarf_CFI_s Dwarf_CFI; > can we say Dwarf_CFI is only about .debug_frame? Even though > dwarf_next_cfi uses Dwarf_CFI_Entry but not Dwarf_CFI? > > I know .eh_frame has slightly different format from .debug_frame, and > it's not defined by the DWARF specification but LSB, so is it the > reason why this is kinda confusing? Right. But note that dwarf_getcfi_elf () (unlike every other dwarf_... function) takes an Elf *handle, not a Dwarf *handle. So given an Elf *elf handle gotten with elf_begin () you can do: Dwarf_CFI *cfi = dwarf_getcfi_elf (elf); And then use that cfi with dwarf_cfi_addrframe () to get a Dwarf_Frame *, which you can then use with dwarf_frame_register (), etc. They don't care whether the CFI came from a Dwarf .debug_frame or Elf .eh_frame. Cheers, Mark
[PATCH] libdw: Handle .debug_rnglists in dwarf_ranges.
Handle all new DW_RLE opcodes in .debug_rnglists in dwarf_ranges. Extract code for reading .debug_addr indexes from dwarf_formaddr as __libdw_addrx to reuse in __libdw_read_begin_end_pair_inc. And add new testcase. Signed-off-by: Mark Wielaard --- libdw/ChangeLog | 15 ++ libdw/dwarf_formaddr.c | 76 libdw/dwarf_getlocation.c| 2 +- libdw/dwarf_ranges.c | 258 +++ libdw/libdwP.h | 84 - src/ChangeLog| 3 +- src/readelf.c| 2 + tests/ChangeLog | 6 + tests/Makefile.am| 2 +- tests/run-all-dwarf-ranges.sh| 55 ++ tests/run-dwarf-ranges.sh| 18 ++ tests/testfile-ranges-hello5.dwo.bz2 | Bin 0 -> 1296 bytes tests/testfile-ranges-world5.dwo.bz2 | Bin 0 -> 1466 bytes tests/testfileranges5.debug.bz2 | Bin 0 -> 2857 bytes tests/testfilesplitranges5.debug.bz2 | Bin 0 -> 2235 bytes 15 files changed, 442 insertions(+), 79 deletions(-) create mode 100644 tests/testfile-ranges-hello5.dwo.bz2 create mode 100644 tests/testfile-ranges-world5.dwo.bz2 create mode 100755 tests/testfileranges5.debug.bz2 create mode 100755 tests/testfilesplitranges5.debug.bz2 diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 9c0ab3c5..b19ebe96 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,18 @@ +2018-04-06 Mark Wielaard + + * dwarf_formaddr.c (__libdw_addrx): New function, extracted from... + (dwarf_formaddr): here. Use __libdw_addrx. + * dwarf_getlocation.c (getlocations_addr): Pass cu to + __libdw_read_begin_end_pair_inc. + * dwarf_ranges.c (__libdw_read_begin_end_pair_inc): Take cu as + argument. Handle .debug_rnglists. + (initial_offset): Handle .debug_rnglists and DW_FORM_rnglistx. + (dwarf_ranges): Likewise. Check cu isn't NULL before use. Pass cu to + __libdw_read_begin_end_pair_inc. + * libdwP.h (__libdw_read_begin_end_pair_inc): Take cu as argument. + (__libdw_cu_ranges_base): Handle DW_AT_rnglists_base. + (__libdw_addrx): New function definition. + 2018-04-11 Mark Wielaard * dwarf.h: Add DWARF5 range list entry DW_RLE encodings. diff --git a/libdw/dwarf_formaddr.c b/libdw/dwarf_formaddr.c index 3c89a5d2..9cd3d200 100644 --- a/libdw/dwarf_formaddr.c +++ b/libdw/dwarf_formaddr.c @@ -34,6 +34,48 @@ #include "libdwP.h" +int +__libdw_addrx (Dwarf_CU *cu, Dwarf_Word idx, Dwarf_Addr *addr) +{ + Dwarf_Off addr_off = __libdw_cu_addr_base (cu); + if (addr_off == (Dwarf_Off) -1) +return -1; + + Dwarf *dbg = cu->dbg; + 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; + + const unsigned char *datap; + datap = dbg->sectiondata[IDX_debug_addr]->d_buf + addr_off + idx; + if (address_size == 4) +*addr = read_4ubyte_unaligned (dbg, datap); + else +*addr = read_8ubyte_unaligned (dbg, datap); + + return 0; +} + int dwarf_formaddr (Dwarf_Attribute *attr, Dwarf_Addr *return_addr) { @@ -98,41 +140,9 @@ dwarf_formaddr (Dwarf_Attribute *attr, Dwarf_Addr *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) + if (__libdw_addrx (cu, idx, return_addr) != 0) 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) d