[applied] dwarf-reader: Support indirectly referenced subrange_type DIEs

Message ID 87355blfxq.fsf@redhat.com
State New
Headers
Series [applied] dwarf-reader: Support indirectly referenced subrange_type DIEs |

Commit Message

Dodji Seketeli April 7, 2023, 6:12 p.m. UTC
  Hello,

This is about supporting an Ada-induced DWARF construct related to
ranged types.

To reproduce the issue this patch originated from, you can type:

    $ fedabipkgdiff --self-compare -a --from fc37 gprbuild

From that gprbuild package from fc37, consider this subrange_type DIE
coming from the debuginfo file prtests/gprbuild-2020-11.fc37.aarch64:

     1	 [ 3c10d]        subrange_type        abbrev: 34
     2	                 type                 (ref_addr) [ 6191e]
     3	                 lower_bound          (sdata) 2
     4	                 upper_bound          (ref_udata) [ 3c0eb]

At line 4, look at how the DW_AT_upper_bound attribute is a reference
to another DIE, instead of being a (signed) constant value, like the
DW_AT_lower_bound attribute at line 3.  The referenced DIE is at
offset 0x3c0eb.

How do we get the actual value of the upper_bound of that subrange
type?

To answer that question, let's look at the DIE, at offset 0x3c0eb that
is referenced by the DW_AT_upper_bound attribute at line 4:

     1	 [ 3c0eb]      member               abbrev: 87
     2	               name                 (strp) "last"
     3	               decl_file            (data1) a-coinve.ads (35)
     4	               decl_line            (data2) 415
     5	               decl_column          (data1) 24
     6	               type                 (ref_udata) [ 3c0f7]

It's a data member which type is referenced at line 6.  Let's look at
that type, which offset is 0x3c0f7:

     1	 [ 3c0f7]      subrange_type        abbrev: 122
     2	               upper_bound          (sdata) 99999999
     3	               name                 (strp) "gpr__names__name_vectors__T449bXn"
     4	               type                 (ref_addr) [ 6191e]
     5	               artificial           (flag_present) yes

That type is a DW_TAG_subrange_type and its DW_AT_upper_bound value is
a constant.  Finally.

Actually, the value of DW_AT_upper_bound of this DIE at offset 0x3c0f7
is the value of the DW_AT_upper_bound of the subrange_type DIE at
offset 0x3c10d that we were initially looking for.

The DIE at 0x3c0f7 is said to be indirectly referenced by the DIE at
0x3c10d, through its DW_AT_upper_bound attribute.

This patch supports retrieving the value of the DW_AT_upper_bound of
0x3c10d through the 0x3c0f7 DIE that it indirectly references.

The package gprbuild from fc37 now passes self comparison with this patch.

	* src/abg-dwarf-reader.cc (subrange_die_indirect_bound_value)
	(subrange_die_indirectly_references_subrange_die): Define new
	static function.
	(build_subrange_type): If the value of DW_AT_upper_bound is not a
	constant, try to consider it as an indirect reference to a
	DW_TAG_subrange_type DIE, whose DW_AT_upper_bound might carry the
	constant value that we are looking for.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 src/abg-dwarf-reader.cc | 123 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 122 insertions(+), 1 deletion(-)
  

Patch

diff --git a/src/abg-dwarf-reader.cc b/src/abg-dwarf-reader.cc
index d5dfc48f..ef1d8a45 100644
--- a/src/abg-dwarf-reader.cc
+++ b/src/abg-dwarf-reader.cc
@@ -523,6 +523,16 @@  die_die_attribute(const Dwarf_Die* die,
 		  Dwarf_Die& result,
 		  bool recursively = true);
 
+static bool
+subrange_die_indirect_bound_value(const Dwarf_Die *die,
+				  unsigned attr_name,
+				  array_type_def::subrange_type::bound_value& v,
+				  bool& is_signed);
+
+static bool
+subrange_die_indirectly_references_subrange_die(const Dwarf_Die *die,
+						unsigned attr_name,
+						Dwarf_Die& referenced_subrange);
 static string
 get_internal_anonymous_die_prefix_name(const Dwarf_Die *die);
 
@@ -6199,6 +6209,110 @@  die_die_attribute(const Dwarf_Die* die,
   return dwarf_formref_die(&attr, &result);
 }
 
+/// Test if a subrange DIE indirectly references another subrange DIE
+/// through a given attribute.
+///
+/// A DW_TAG_subrange_type DIE can have its DW_AT_{lower,upper}_bound
+/// attribute be a reference to either a data member or a variable
+/// which type is itself a DW_TAG_subrange_type.  This latter subrange
+/// DIE is said to be "indirectly referenced" by the former subrange
+/// DIE.  In that case, the DW_AT_{lower,upper}_bound of the latter is
+/// the value we want for the DW_AT_upper_bound of the former.
+///
+/// This function tests if the former subrange DIE does indirectly
+/// reference another subrange DIE through a given attribute (not
+/// necessarily DW_AT_upper_bound).
+///
+/// @param die the DIE to consider.  Note that It must be a
+/// DW_TAG_subrange_type.
+///
+/// @param attr_name the name of the attribute to look through for the
+/// indirectly referenced subrange DIE.
+///
+/// @param referenced_subrange if the function returns true, then the
+/// argument of this parameter is set to the indirectly referenced
+/// DW_TAG_subrange_type DIE.
+///
+/// @return true iff @p DIE indirectly references a subrange DIE
+/// through the attribute @p attr_name.
+static bool
+subrange_die_indirectly_references_subrange_die(const Dwarf_Die *die,
+						unsigned attr_name,
+						Dwarf_Die& referenced_subrange)
+{
+  bool result = false;
+
+  if (dwarf_tag(const_cast<Dwarf_Die*>(die)) != DW_TAG_subrange_type)
+    return result;
+
+  Dwarf_Die referenced_die;
+  if (die_die_attribute(die, attr_name, referenced_die))
+    {
+      unsigned tag = dwarf_tag(&referenced_die);
+      if ( tag == DW_TAG_member || tag == DW_TAG_variable)
+	{
+	  Dwarf_Die type_die;
+	  if (die_die_attribute(&referenced_die, DW_AT_type, type_die))
+	    {
+	      tag = dwarf_tag(&type_die);
+	      if (tag == DW_TAG_subrange_type)
+		{
+		  memcpy(&referenced_subrange, &type_die, sizeof(type_die));
+		  result = true;
+		}
+	    }
+	}
+    }
+  return result;
+}
+
+/// Return the bound value of subrange die by looking at an indirectly
+/// referenced subrange DIE.
+///
+/// A DW_TAG_subrange_type DIE can have its DW_AT_{lower,upper}_bound
+/// attribute be a reference to either a data member or a variable
+/// which type is itself a DW_TAG_subrange_type.  This latter subrange
+/// DIE is said to be "indirectly referenced" by the former subrange
+/// DIE.  In that case, the DW_AT_{lower,upper}_bound of the latter is
+/// the value we want for the DW_AT_{lower,upper}_bound of the former.
+///
+/// This function gets the DW_AT_{lower,upper}_bound value of a
+/// subrange type by looking at the DW_AT_{lower,upper}_bound value of
+/// the indirectly referenced subrange type, if it exists.
+///
+/// @param die the subrange DIE to consider.
+///
+/// @param attr_name the name of the attribute to consider, typically,
+/// DW_AT_{lower,upper}_bound.
+///
+/// @param v the found value, iff this function returned true.
+///
+/// @param is_signed, this is set to true if @p v is signed.  This
+/// parameter is set at all only if the function returns true.
+///
+/// @return true iff the DW_AT_{lower,upper}_bound was found on the
+/// indirectly referenced subrange type.
+static bool
+subrange_die_indirect_bound_value(const Dwarf_Die *die,
+				  unsigned attr_name,
+				  array_type_def::subrange_type::bound_value& v,
+				  bool& is_signed)
+{
+  bool result = false;
+
+  if (dwarf_tag(const_cast<Dwarf_Die*>(die)) != DW_TAG_subrange_type)
+    return result;
+
+  Dwarf_Die subrange_die;
+  if (subrange_die_indirectly_references_subrange_die(die, attr_name,
+						      subrange_die))
+    {
+      if (die_constant_attribute(&subrange_die, attr_name, is_signed, v))
+	result = true;
+    }
+  return result;
+}
+
 /// Read and return an addresss class attribute from a given DIE.
 ///
 /// @param die the DIE to consider.
@@ -14162,8 +14276,15 @@  build_subrange_type(reader&		rdr,
   // So let's look for DW_AT_lower_bound first.
   die_constant_attribute(die, DW_AT_lower_bound, is_signed, lower_bound);
 
+  bool found_upper_bound = die_constant_attribute(die, DW_AT_upper_bound,
+						  is_signed, upper_bound);
+  if (!found_upper_bound)
+    found_upper_bound = subrange_die_indirect_bound_value(die,
+							  DW_AT_upper_bound,
+							  upper_bound,
+							  is_signed);
   // Then, DW_AT_upper_bound.
-  if (!die_constant_attribute(die, DW_AT_upper_bound, is_signed, upper_bound))
+  if (!found_upper_bound)
     {
       // The DWARF 4 spec says, in [5.11 Subrange Type
       // Entries]: