[v2,1/4] gdb, dwarf: add support for DW_AT_trampoline in DWARF reader

Message ID 20230605110410.3078-2-abdul.b.ijaz@intel.com
State New
Headers
Series GDB support for DW_AT_trampoline |

Commit Message

Ijaz, Abdul B June 5, 2023, 11:04 a.m. UTC
  From: Nils-Christian Kempke <nils-christian.kempke@intel.com>

DW_AT_trampoline can be used to describe compiler generated functions
that serve some intermediary purpose on making a call to another
function.  A compiler can emit this tag in order to help a debugger hide
the trampolines from a user.

The attribute is only applicable to DW_TAG_subroutine and
DW_TAG_inlined_subroutine tags.  It contains information about the
trampoline target either as a reference to its DIE, as its address or
its name.  DW_AT_trampoline can also be a flag indicating that the DIE
is a trampoline or not without specifying the target (e.g. if it is
unknown).

This patch adds support to GDB for reading the DW_AT_trampoline
attribute.  It stores the attribute and its value in the type_specific
part of a GDB type.  This patch is implemented in preparation of the
following patches, which will add a mechanism to hide DW_AT_trampoline
subroutines from the user.

2023-06-05 Nils-Christian Kempke <nils-christian.kempke@intel.com>
---
 gdb/dwarf2/read.c |  43 ++++++++++++++++++-
 gdb/gdbtypes.c    |  35 +++++++++++++++-
 gdb/gdbtypes.h    | 103 +++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 174 insertions(+), 7 deletions(-)
  

Patch

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 44b54f77de9..38eab809a66 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -16599,6 +16599,47 @@  read_subroutine_type (struct die_info *die, struct dwarf2_cu *cu)
   if (prototyped_function_p (die, cu))
     ftype->set_is_prototyped (true);
 
+  /* If this is a trampoline function store it and its target here.  */
+  attr = dwarf2_attr (die, DW_AT_trampoline, cu);
+  if (attr != nullptr)
+    {
+      TYPE_FUNC_FLAGS (ftype) |= FUNC_TYPE_TRAMPOLINE;
+      TYPE_TRAMPOLINE_TARGET (ftype)
+	= (trampoline_target *) TYPE_ZALLOC (ftype,
+					     sizeof (trampoline_target));
+
+      /* A DW_AT_trampoline can be either an address, a flag, a reference or a
+	 string.  */
+      if (attr->form_is_string ())
+	TYPE_TRAMPOLINE_TARGET (ftype)->set_target_physname
+	  (attr->as_string ());
+      else if (attr->form_is_ref ())
+	{
+	  die_info *target_die;
+	  dwarf2_cu *target_cu = cu;
+
+	  target_die = follow_die_ref (die, attr, &target_cu);
+	  const char *target_name = dwarf2_name (target_die, target_cu);
+	  if (target_name == nullptr)
+	    {
+	      complaint (_("DW_AT_trampoline target DIE has no name for"
+			 "referencing DIE %s [in module %s]"),
+			 sect_offset_str (die->sect_off),
+			 objfile_name (objfile));
+	    }
+	  TYPE_TRAMPOLINE_TARGET (ftype)->set_target_physname (target_name);
+	}
+      else if (attr->form_is_unsigned ())
+	TYPE_TRAMPOLINE_TARGET (ftype)->set_target_flag (attr->as_boolean ());
+      else
+	{
+	  CORE_ADDR target_addr = attr->as_address ();
+	  target_addr = gdbarch_adjust_dwarf2_addr (objfile->arch (),
+						    target_addr);
+	  TYPE_TRAMPOLINE_TARGET (ftype)->set_target_physaddr (target_addr);
+	}
+    }
+
   /* Store the calling convention in the type if it's available in
      the subroutine die.  Otherwise set the calling convention to
      the default value DW_CC_normal.  */
@@ -16616,7 +16657,7 @@  read_subroutine_type (struct die_info *die, struct dwarf2_cu *cu)
      if the DWARF producer set that information.  */
   attr = dwarf2_attr (die, DW_AT_noreturn, cu);
   if (attr && attr->as_boolean ())
-    TYPE_NO_RETURN (ftype) = 1;
+    TYPE_FUNC_FLAGS (ftype) |= FUNC_TYPE_NO_RETURN;
 
   /* We need to add the subroutine type to the die immediately so
      we don't infinitely recurse when dealing with parameters
diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index 6a4c5976f18..fc18fc9ce3f 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -5206,6 +5206,33 @@  print_fixed_point_type_info (struct type *type, int spaces)
 	      type->fixed_point_scaling_factor ().str ().c_str ());
 }
 
+/* Print the contents of the TYPE's self_trampoline_target, assuming that its
+   type-specific kind is TYPE_SPECIFIC_FUNC and is_trampoline is not 0.  */
+static void
+print_trampoline_target_info (struct type *type, int spaces)
+{
+  switch (TYPE_TRAMPOLINE_TARGET (type)->target_kind ())
+    {
+    case TRAMPOLINE_TARGET_PHYSADDR:
+      gdb_printf ("%*starget physaddr: 0x%s\n", spaces + 2, "",
+		  print_core_address (type->arch_owner (),
+				      TYPE_TRAMPOLINE_TARGET (type)
+				      ->target_physaddr ()));
+      break;
+    case TRAMPOLINE_TARGET_PHYSNAME:
+      gdb_printf ("%*starget physname: %s\n", spaces + 2, "",
+		  TYPE_TRAMPOLINE_TARGET (type)->target_physname ());
+      break;
+    case TRAMPOLINE_TARGET_FLAG:
+      gdb_printf ("%*starget flag: %d\n", spaces + 2, "",
+		  TYPE_TRAMPOLINE_TARGET (type)->target_flag ());
+      break;
+    default:
+      gdb_assert_not_reached ("unhandled trampoline target kind");
+      break;
+    }
+}
+
 static struct obstack dont_print_type_obstack;
 
 /* Print the dynamic_prop PROP.  */
@@ -5532,6 +5559,10 @@  recursive_dump_type (struct type *type, int spaces)
 	gdb_printf ("%*scalling_convention %d\n", spaces, "",
 		    TYPE_CALLING_CONVENTION (type));
 	/* tail_call_list is not printed.  */
+	gdb_printf ("%*sfunc_type_flags %u\n", spaces, "",
+		    (unsigned int) TYPE_FUNC_FLAGS (type));
+	if (TYPE_IS_TRAMPOLINE (type))
+	  print_trampoline_target_info (type, spaces);
 	break;
 
       case TYPE_SPECIFIC_SELF_TYPE:
@@ -5748,8 +5779,10 @@  copy_type_recursive (struct type *type, htab_t copied_types)
     case TYPE_SPECIFIC_FUNC:
       INIT_FUNC_SPECIFIC (new_type);
       TYPE_CALLING_CONVENTION (new_type) = TYPE_CALLING_CONVENTION (type);
-      TYPE_NO_RETURN (new_type) = TYPE_NO_RETURN (type);
+      TYPE_FUNC_FLAGS (new_type) |= TYPE_NO_RETURN (type);
       TYPE_TAIL_CALL_LIST (new_type) = NULL;
+      TYPE_FUNC_FLAGS (new_type) |= TYPE_IS_TRAMPOLINE (type);
+      TYPE_TRAMPOLINE_TARGET (new_type) = TYPE_TRAMPOLINE_TARGET (type);
       break;
     case TYPE_SPECIFIC_FLOATFORMAT:
       TYPE_FLOATFORMAT (new_type) = TYPE_FLOATFORMAT (type);
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index a9abb0d8071..38379aced37 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -123,6 +123,21 @@  enum type_instance_flag_value : unsigned
 
 DEF_ENUM_FLAGS_TYPE (enum type_instance_flag_value, type_instance_flags);
 
+/* * Define flags for function types.  */
+enum func_type_flag_value : unsigned
+{
+  /* * Flag indicates, whether this function normally returns to its
+     caller.  It is set from the DW_AT_noreturn attribute if set on
+     the DW_TAG_subprogram.  */
+  FUNC_TYPE_NO_RETURN = (1 << 0),
+
+  /* * Flag is used for functions marked with DW_AT_trampoline.  These
+     are compiler generated wrappers that should be hidden from the user.  */
+  FUNC_TYPE_TRAMPOLINE = (1 << 1)
+};
+
+DEF_ENUM_FLAGS_TYPE (enum func_type_flag_value, func_type_flags);
+
 /* * Not textual.  By default, GDB treats all single byte integers as
    characters (or elements of strings) unless this flag is set.  */
 
@@ -1730,11 +1745,9 @@  struct func_type
 
     ENUM_BITFIELD (dwarf_calling_convention) calling_convention : 8;
 
-    /* * Whether this function normally returns to its caller.  It is
-       set from the DW_AT_noreturn attribute if set on the
-       DW_TAG_subprogram.  */
+    /* * For storing function types defined in eunm func_type_flag_value.  */
 
-    unsigned int is_noreturn : 1;
+    func_type_flags flags;
 
     /* * Only those DW_TAG_call_site's in this function that have
        DW_AT_call_tail_call set are linked in this list.  Function
@@ -1749,6 +1762,78 @@  struct func_type
        contains the method.  */
 
     struct type *self_type;
+
+    struct trampoline_target *self_trampoline_target;
+  };
+
+/* The kind of location held by this call site target.  */
+enum trampoline_target_kind
+  {
+    /* An address.  */
+    TRAMPOLINE_TARGET_PHYSADDR,
+    /* A (mangled) name.  */
+    TRAMPOLINE_TARGET_PHYSNAME,
+    /* An flag (target is unknown).  */
+    TRAMPOLINE_TARGET_FLAG,
+  };
+
+struct trampoline_target
+  {
+    trampoline_target_kind target_kind () const
+    {
+      return m_target_kind;
+    }
+
+    void set_target_physaddr (CORE_ADDR physaddr)
+    {
+      m_target_kind = TRAMPOLINE_TARGET_PHYSADDR;
+      m_trampoline_target.physaddr = physaddr;
+    }
+
+    CORE_ADDR target_physaddr () const
+    {
+      gdb_assert (m_target_kind == TRAMPOLINE_TARGET_PHYSADDR);
+      return m_trampoline_target.physaddr;
+    }
+
+    void set_target_physname (const char *physname)
+    {
+      m_target_kind = TRAMPOLINE_TARGET_PHYSNAME;
+      m_trampoline_target.physname = physname;
+    }
+
+    const char *target_physname () const
+    {
+      gdb_assert (m_target_kind == TRAMPOLINE_TARGET_PHYSNAME);
+      return m_trampoline_target.physname;
+    }
+
+    void set_target_flag (bool flag)
+    {
+      m_target_kind = TRAMPOLINE_TARGET_FLAG;
+      m_trampoline_target.flag = flag;
+    }
+
+    bool target_flag () const
+    {
+      gdb_assert (m_target_kind == TRAMPOLINE_TARGET_FLAG);
+      return m_trampoline_target.flag;
+    }
+
+  private:
+
+    union
+    {
+      /* Address.  */
+      CORE_ADDR physaddr;
+      /* Mangled name.  */
+      const char *physname;
+      /* Flag.  */
+      bool flag;
+    } m_trampoline_target;
+
+    /* * Discriminant for union m_trampoline_target.  */
+    ENUM_BITFIELD (trampoline_target_kind) m_target_kind : 2;
   };
 
 /* struct call_site_parameter can be referenced in callees by several ways.  */
@@ -2102,8 +2187,16 @@  extern void set_type_vptr_basetype (struct type *, struct type *);
 #define TYPE_GNAT_SPECIFIC(thistype) TYPE_MAIN_TYPE(thistype)->type_specific.gnat_stuff
 #define TYPE_DESCRIPTIVE_TYPE(thistype) TYPE_GNAT_SPECIFIC(thistype)->descriptive_type
 #define TYPE_CALLING_CONVENTION(thistype) TYPE_MAIN_TYPE(thistype)->type_specific.func_stuff->calling_convention
-#define TYPE_NO_RETURN(thistype) TYPE_MAIN_TYPE(thistype)->type_specific.func_stuff->is_noreturn
+#define TYPE_FUNC_FLAGS(thistype) \
+  TYPE_MAIN_TYPE(thistype)->type_specific.func_stuff->flags
+#define TYPE_NO_RETURN(thistype) \
+  (TYPE_MAIN_TYPE(thistype)->type_specific.func_stuff->flags \
+   & FUNC_TYPE_NO_RETURN)
 #define TYPE_TAIL_CALL_LIST(thistype) TYPE_MAIN_TYPE(thistype)->type_specific.func_stuff->tail_call_list
+#define TYPE_IS_TRAMPOLINE(thistype) \
+  (TYPE_MAIN_TYPE(thistype)->type_specific.func_stuff->flags \
+   & FUNC_TYPE_TRAMPOLINE)
+#define TYPE_TRAMPOLINE_TARGET(thistype) TYPE_MAIN_TYPE(thistype)->type_specific.func_stuff->self_trampoline_target
 #define TYPE_BASECLASS(thistype,index) ((thistype)->field (index).type ())
 #define TYPE_N_BASECLASSES(thistype) TYPE_CPLUS_SPECIFIC(thistype)->n_baseclasses
 #define TYPE_BASECLASS_NAME(thistype,index) (thistype->field (index).name ())