Re: [PATCH] libdw: Handle GNU DebugFission split ranges.

2018-05-24 Thread Mark Wielaard
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.

2018-05-24 Thread Mark Wielaard
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+

2018-05-24 Thread Mark Wielaard
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

2018-05-24 Thread Mark Wielaard
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.

2018-05-24 Thread Mark Wielaard
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