[PATCH] readelf: Decode DW_AT_discr_list block attributes.

2019-05-10 Thread Mark Wielaard
Decode DW_AT_descr_list blocks using the DW_DSC values.
This requires knowing the signedness of the discriminant.
Which means the attr_callback function needs access to the
parent DIE. Pass the whole DIE path, plus the current level.
That way the type of the discriminant can be looked up in
the variant_part (parent) DIE of the variant DIE (which has
the discr_list attribute).

Add a testcase using both signed and unsigned discriminants.

https://sourceware.org/bugzilla/show_bug.cgi?id=24509

Signed-off-by: Mark Wielaard 
---
 src/ChangeLog |   7 +
 src/readelf.c | 103 -
 tests/ChangeLog   |   9 ++
 tests/Makefile.am |   7 +-
 tests/run-readelf-discr.sh| 337 ++
 tests/testfile-rng.debug.bz2  | Bin 0 -> 1286 bytes
 tests/testfile-urng.debug.bz2 | Bin 0 -> 1178 bytes
 7 files changed, 453 insertions(+), 10 deletions(-)
 create mode 100755 tests/run-readelf-discr.sh
 create mode 100644 tests/testfile-rng.debug.bz2
 create mode 100644 tests/testfile-urng.debug.bz2

diff --git a/src/ChangeLog b/src/ChangeLog
index ae13793..15ef415 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,10 @@
+2019-05-10  Mark Wielaard  
+
+   * readelf.c (struct attrcb_args): Rename die to dies.
+   (attr_callback): Get current current die using dies[level].
+   Handle DW_AT_discr_list as block, not as constant.
+   (print_debug_units): pass dies, not dies[level] as args.
+
 2019-04-28  Mark Wielaard  
 
* unstrip.c (add_new_section_symbols): Call ELF_CHECK after
diff --git a/src/readelf.c b/src/readelf.c
index c346e1f..7efdfa2 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -6930,7 +6930,7 @@ struct attrcb_args
 {
   Dwfl_Module *dwflmod;
   Dwarf *dbg;
-  Dwarf_Die *die;
+  Dwarf_Die *dies;
   int level;
   bool silent;
   bool is_split;
@@ -6946,7 +6946,7 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
 {
   struct attrcb_args *cbargs = (struct attrcb_args *) arg;
   const int level = cbargs->level;
-  Dwarf_Die *die = cbargs->die;
+  Dwarf_Die *die = &cbargs->dies[level];
   bool is_split = cbargs->is_split;
 
   unsigned int attr = dwarf_whatattr (attrp);
@@ -7290,9 +7290,6 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
case DW_AT_ordering:
  valuestr = dwarf_ordering_name (num);
  break;
-   case DW_AT_discr_list:
- valuestr = dwarf_discr_list_name (num);
- break;
case DW_AT_decl_file:
case DW_AT_call_file:
  {
@@ -7347,7 +7344,7 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
   /* When highpc is in constant form it is relative to lowpc.
 In that case also show the address.  */
   Dwarf_Addr highpc;
-  if (attr == DW_AT_high_pc && dwarf_highpc (cbargs->die, &highpc) == 0)
+  if (attr == DW_AT_high_pc && dwarf_highpc (die, &highpc) == 0)
{
  printf ("   %*s%-20s (%s) %" PRIuMAX " (",
  (int) (level * 2), "", dwarf_attr_name (attr),
@@ -7369,7 +7366,7 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
  bool is_signed;
  int bytes = 0;
  if (attr == DW_AT_const_value)
-   die_type_sign_bytes (cbargs->die, &is_signed, &bytes);
+   die_type_sign_bytes (die, &is_signed, &bytes);
  else
is_signed = (form == DW_FORM_sdata
 || form == DW_FORM_implicit_const);
@@ -7524,6 +7521,96 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
  else
print_block (block.length, block.data);
  break;
+
+   case DW_AT_discr_list:
+ if (block.length == 0)
+   puts ("");
+ else if (form != DW_FORM_data16)
+   {
+ const unsigned char *readp = block.data;
+ const unsigned char *readendp = readp + block.length;
+
+ /* See if we are dealing with a signed or unsigned
+values.  If the parent of this variant DIE is a
+variant_part then it will either have a discriminant
+which points to the member which type is the
+discriminant type.  Or the variant_part itself has a
+type representing the discriminant.  */
+ bool is_signed = false;
+ if (level > 0)
+   {
+ Dwarf_Die *parent = &cbargs->dies[level - 1];
+ if (dwarf_tag (die) == DW_TAG_variant
+ && dwarf_tag (parent) == DW_TAG_variant_part)
+   {
+ Dwarf_Die member;
+ Dwarf_Attribute discr_attr;
+ int bytes;
+ if (dwarf_formref_die (dwarf_attr (parent,
+DW_AT_discr,
+&discr_attr),
+&member) != NUL

[Bug tools/24509] eu-readelf does not know how to dissect DW_AT_discr_list

2019-05-10 Thread mark at klomp dot org
https://sourceware.org/bugzilla/show_bug.cgi?id=24509

Mark Wielaard  changed:

   What|Removed |Added

 Status|NEW |ASSIGNED

--- Comment #6 from Mark Wielaard  ---
Proposed patch, added default/empty block handling, data bounds checks and
testcases: https://sourceware.org/ml/elfutils-devel/2019-q2/msg00072.html

-- 
You are receiving this mail because:
You are on the CC list for the bug.

Re: [PATCH] readelf: Decode DW_AT_discr_list block attributes.

2019-05-10 Thread Tom Tromey
> "Mark" == Mark Wielaard  writes:

Mark> Decode DW_AT_descr_list blocks using the DW_DSC values.
Mark> This requires knowing the signedness of the discriminant.
Mark> Which means the attr_callback function needs access to the
Mark> parent DIE. Pass the whole DIE path, plus the current level.
Mark> That way the type of the discriminant can be looked up in
Mark> the variant_part (parent) DIE of the variant DIE (which has
Mark> the discr_list attribute).

Mark> Add a testcase using both signed and unsigned discriminants.

Mark> https://sourceware.org/bugzilla/show_bug.cgi?id=24509

Thank you for doing this.

Tom


Re: Dwarf_Op

2019-05-10 Thread Mark Wielaard
On Wed, May 08, 2019 at 06:03:55PM +, Sasha Da Rocha Pinheiro wrote:
> About the *ops returned from dwarf_frame_register():
> Is it correct to say that if we don't get a DW_OP_stack_value as the
> last operation, the value on top of the stack will always be a
> memory address? Meaning you must dereference it? Even though there
> is no DW_OP_deref provided as last operation? If not, what else
> could it be?

There could be no operations at all, an empty location description,
then there simply is no location/value. It could be a single register
location description operation (DW_OP_reg[0..31] or DW_OP_regx) that
says the value is in that particular register (note this is different
from the DW_OP_bregx operations which pushes the contents of the
register on the expression stack). Or it is one or more of the other
operations which leave the location (an address) on the expression
stack. If the last operation is DW_OP_stack_value then the value on
the top of the expression stack is the value itself (and not a
location where the value can be changed).

Technically it can also be an DW_OP_implicit_value (again a value, not
a location), and DW_OP_implicit_pointer (a "fake" pointer/location,
but really a description of the value of that location), or a
composite location description (DW_OP_[bit_]piece, multiple location
descriptions that have to be combined to get the value). But these
don't come from dwarf_frame_register at the moment.

> Another issue: I am trying to decode the following *ops:
> DW_OP_call_frame_cfa DW_OP_stack_value This was returned after I
> requested register 31 (SP in aarch64) at address 0x40091c.  As you
> can see below, I pasted the CIE and FDE relative to this address
> using eu-readelf and dwarfdump.  Is it that, in this case,
> DW_OP_call_frame_cfa means "get the CFA value at 0x00400918"? Why
> isn't SP rule undefined or same_value, instead of those two
> operations?

Yes, that looks circular, but it is meant to be "efficient". Often
registers can be found at an offset of the current CFA (or in this
case, it is the CFA value). You get the CFA for a frame with
dwarf_frame_cfa (), which works similar to dwarf_frame_register (),
the CFA itself doesn't have/is a register number however.

Hope that helps,

Mark