diff options
author | Mark Wielaard <mark@klomp.org> | 2019-05-05 23:18:36 +0200 |
---|---|---|
committer | Mark Wielaard <mark@klomp.org> | 2019-05-14 20:52:59 +0200 |
commit | 643cbb275d65533472c0f53391f9fc1d5d9a2efc (patch) | |
tree | 9358db34cac91f030d83f846b8b65462172fe325 /src | |
parent | 7d029dc4585f84937a9386dc6f7ce82fbe2e1e21 (diff) |
readelf: Decode DW_AT_discr_list block attributes.
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 <mark@klomp.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/ChangeLog | 7 | ||||
-rw-r--r-- | src/readelf.c | 103 |
2 files changed, 102 insertions, 8 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index 0c817f41..580eea9b 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,10 @@ +2019-05-10 Mark Wielaard <mark@klomp.org> + + * 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-05-09 Mark Wielaard <mark@klomp.org> * readelf.c (cleanup_list): New function. diff --git a/src/readelf.c b/src/readelf.c index 4c20c38e..062168bb 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -6944,7 +6944,7 @@ struct attrcb_args { Dwfl_Module *dwflmod; Dwarf *dbg; - Dwarf_Die *die; + Dwarf_Die *dies; int level; bool silent; bool is_split; @@ -6960,7 +6960,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); @@ -7304,9 +7304,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: { @@ -7361,7 +7358,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), @@ -7383,7 +7380,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); @@ -7538,6 +7535,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 ("<default>"); + 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) != NULL) + die_type_sign_bytes (&member, &is_signed, &bytes); + else + die_type_sign_bytes (parent, &is_signed, &bytes); + } + } + while (readp < readendp) + { + int d = (int) *readp++; + printf ("%s ", dwarf_discr_list_name (d)); + if (readp >= readendp) + goto attrval_out; + + Dwarf_Word val; + Dwarf_Sword sval; + if (d == DW_DSC_label) + { + if (is_signed) + { + get_sleb128 (sval, readp, readendp); + printf ("%" PRId64 "", sval); + } + else + { + get_uleb128 (val, readp, readendp); + printf ("%" PRIu64 "", val); + } + } + else if (d == DW_DSC_range) + { + if (is_signed) + { + get_sleb128 (sval, readp, readendp); + printf ("%" PRId64 "..", sval); + if (readp >= readendp) + goto attrval_out; + get_sleb128 (sval, readp, readendp); + printf ("%" PRId64 "", sval); + } + else + { + get_uleb128 (val, readp, readendp); + printf ("%" PRIu64 "..", val); + if (readp >= readendp) + goto attrval_out; + get_uleb128 (val, readp, readendp); + printf ("%" PRIu64 "", val); + } + } + else + { + print_block (readendp - readp, readp); + break; + } + if (readp < readendp) + printf (", "); + } + putchar ('\n'); + } + else + print_block (block.length, block.data); + break; } break; @@ -7738,7 +7825,7 @@ print_debug_units (Dwfl_Module *dwflmod, /* Print the attribute values. */ args.level = level; - args.die = &dies[level]; + args.dies = dies; (void) dwarf_getattrs (&dies[level], attr_callback, &args, 0); /* Make room for the next level's DIE. */ |