[v11,02/10] gdb/symtab: add lookup for trampoline functions

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

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm success Test passed

Commit Message

Ijaz, Abdul B Aug. 3, 2026, 7:34 p.m. UTC
  From: Nils-Christian Kempke <nils-christian.kempke@intel.com>

In order to query information about the DW_AT_trampoline tag for
subroutines and inlined subroutines, three functions were added to symtab.

First, in_trampoline_code() a routine for querying whether the given
pc belongs to a block that is associated with a function (maybe inlined)
marked DW_AT_trampoline.  Used inside trampoline code (inline or concrete)
that was stepped into and continues stepping through it.  This function
uses find_symbol_for_pc_sect_maybe_inline() which calls containing_function
to detect inline trampolines as well as concrete ones.

Second, in_trampoline_function() a routine to check concrete trampoline
(non-inline) functions and attempt target resolution upfront.  Used in
forward stepping and for frame navigation commands.  This function uses
find_symbol_for_pc_sect() which calls block->linkage_function to skip
inlined blocks and return only the concrete function at the PC.

Third, find_function_trampoline_target() a routine for querying a trampoline
function's target.  Subroutines and inlined subroutines marked with
DW_AT_trampoline usually contain information about the target subroutine
they are 'wrapping'/ passing control to.

These lookup functions will be used in the follow up commits for the
trampoline handling in different GDB commands.

Co-Authored-By: Abdul Basit Ijaz <abdul.b.ijaz@intel.com>
---
 gdb/symtab.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/symtab.h |  24 ++++++++++++
 2 files changed, 132 insertions(+)
  

Patch

diff --git a/gdb/symtab.c b/gdb/symtab.c
index 5d5076f2e77..e5464551807 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -74,6 +74,7 @@ 
 #include "gdbsupport/common-utils.h"
 #include <optional>
 #include "gdbsupport/unordered_set.h"
+#include "gdbsupport/symbol.h"
 
 /* Forward declarations for local functions.  */
 
@@ -4103,6 +4104,113 @@  find_function_alias_target (bound_minimal_symbol msymbol)
   return NULL;
 }
 
+/* See symtab.h.  */
+
+bool
+in_trampoline_code (CORE_ADDR pc)
+{
+  /* Find the innermost function containing pc, which might be an inlined
+     function.  This is used by stepping logic to detect when execution is
+     within ANY trampoline code (inline or concrete) and continue stepping
+     through it.
+
+     Unlike in_trampoline_function(), this uses containing_function() by
+     calling find_symbol_for_pc_sect_maybe_inline to detect inline trampolines as
+     well as concrete ones.  */
+  symbol *sym = find_symbol_for_pc_sect_maybe_inline (pc,
+						  find_pc_mapped_section (pc));
+  return sym != nullptr && TYPE_IS_TRAMPOLINE (sym->type ());
+}
+
+/* See symtab.h.  */
+
+bool
+in_trampoline_function (CORE_ADDR pc)
+{
+  /* Find the concrete non-inlined function at pc, skipping inlined blocks.
+     The find_symbol_for_pc_sect calls block->linkage_function which skips
+     inlined blocks to return the concrete function at the PC.  This ensures
+     we check the concrete function's trampoline flag, not the flag from an
+     abstract inline origin that might be marked as a trampoline.
+
+     This is used by forward stepping to determine if about to step into a
+     concrete trampoline function and by frame navigation commands to skip
+     trampoline frames.
+
+     For O2 optimization, the compiler may create trampoline wrappers with
+     both DW_AT_trampoline + DW_AT_inline.  At a given PC, inlined code from
+     such abstract trampolines may exist, but the concrete function at that
+     PC is the user's real function (not a trampoline).  Using linkage_function
+     by calling find_symbol_for_pc_sect avoids incorrectly checking the inline
+     trampoline's abstract origin.  */
+  symbol *sym = find_symbol_for_pc_sect (pc, find_pc_mapped_section (pc));
+  return sym != nullptr && TYPE_IS_TRAMPOLINE (sym->type ());
+}
+
+/* See symtab.h.  */
+
+CORE_ADDR
+find_function_trampoline_target (CORE_ADDR pc)
+{
+  /* Find the innermost function containing pc.  This might be an inlined
+     function.  */
+  symbol *sym
+    = find_symbol_for_pc_sect_maybe_inline (pc, find_pc_mapped_section (pc));
+  CORE_ADDR target_address = 0;
+
+  if (sym != nullptr && TYPE_IS_TRAMPOLINE (sym->type ()))
+    {
+      trampoline_target *trampoline = TYPE_TRAMPOLINE_TARGET (sym->type ());
+
+      /* DW_AT_trampoline can be given as an address, name, or flag here (die
+	 references have been resolved as names at this point.  In the case
+	 where DW_AT_trampoline contains a flag we do not know the target
+	 address and return 0.  */
+      if (trampoline->target_kind () == TRAMPOLINE_TARGET_NAME)
+	{
+	  /* Handle both the mangled and demangled PHYSNAME.  */
+	  const char *physname = trampoline->target_name ();
+
+	  /* First, check whether there exists a symbol matching the
+	     physname.  If we cannot find one also check for minimal
+	     symbols.  */
+	  const block *blk = block_for_pc (pc);
+	  block_symbol bs
+	    = lookup_symbol (physname, blk, SEARCH_VAR_DOMAIN, 0);
+	  if (bs.symbol != nullptr)
+	    {
+	      const block *block = bs.symbol->value_block ();
+	      gdb_assert (block != nullptr);
+	      target_address = block->start ();
+	    }
+	  else
+	    {
+	      /* We normally expect the target symbol to be located in one
+		 objfile only.  However, a JIT compiler may have generated
+		 a duplicated symbol that most likely resides in the same
+		 objfile with the trampoline symbol.  Give priority to that
+		 objfile in the search.  If not found, try all objfiles.
+		 This is a heuristic.  */
+	      if ((find_minimal_symbol_address (physname, &target_address,
+						sym->objfile ()) != 0)
+		  && (find_minimal_symbol_address (physname, &target_address,
+						   nullptr) != 0))
+		target_address = 0;
+	    }
+	}
+      else if (trampoline->target_kind () == TRAMPOLINE_TARGET_ADDR)
+	{
+	  /* If the function symbol containing this trampoline target has
+	     been relocated we assume the target_address also needs relocation.
+	     If it has not been relocated the offset should be zero.  */
+	  target_address = \
+	    ( (CORE_ADDR) trampoline->target_addr ()
+	       + sym->objfile ()->section_offsets[sym->section_index ()]);
+	}
+    }
+
+  return target_address;
+}
 
 /* If P is of the form "operator[ \t]+..." where `...' is
    some legitimate operator text, return a pointer to the
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 6443216fd7f..10f64548f58 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -2362,6 +2362,30 @@  extern const struct gnu_ifunc_fns *gnu_ifunc_fns_p;
 
 extern CORE_ADDR find_solib_trampoline_target (const frame_info_ptr &, CORE_ADDR);
 
+/* Return whether the current pc is within a block that belongs to a
+   function that is marked as a trampoline by the compiler.  This checks
+   only the concrete (non-inline) function at the PC.  Used by forward stepping
+   to check if about to step into a concrete trampoline function and attempt
+   to resolve its target.  Also used for frame navigation commands.  */
+
+extern bool in_trampoline_function (CORE_ADDR pc);
+
+/* Return whether the current pc is within any trampoline code
+   (inline or concrete).  Used to detect when execution stepped into ANY
+   trampoline code (inline or concrete) and continue stepping through it.
+   Also used for reverse stepping logic.  */
+
+extern bool in_trampoline_code (CORE_ADDR pc);
+
+/* Find the target of a trampoline function marked via the DW_AT_trampoline
+   attribute and return its address.  Returns 0 if the pc is not contained
+   in a trampoline function (inlined or not).  If DW_AT_trampoline
+   is given as a flag, the target is unknown and the function will still return
+   0.  One has to additionally query in_trampoline_function to cover this
+   case.  */
+
+extern CORE_ADDR find_function_trampoline_target (CORE_ADDR pc);
+
 struct symtab_and_line
 {
   /* The program space of this sal.  */