Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH 1/4] gdb/elfread: add debug output for GNU ifunc resolution
@ 2026-02-13  5:38 simon.marchi
  2026-02-13  5:38 ` [PATCH 2/4] gdb/elfread: replace ifunc htab_t with gdb::unordered_map simon.marchi
                   ` (4 more replies)
  0 siblings, 5 replies; 8+ messages in thread
From: simon.marchi @ 2026-02-13  5:38 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@polymtl.ca>

Add some debug prints throughout the ifunc resolution code, to be able
to better understand what GDB does.  Add the new "set debug gnu-ifunc"
knob to control it.

Add the debug_prefixed_printf_cond_func macro to implement
gnu_ifunc_debug_printf_func, that takes an explicit function name.  This
is needed to avoid showing "operator()" as the function name in the
debug message.

Here is a sample session with the new debug output enabled.

    (gdb) b the_function
    [gnu-ifunc] elf_gnu_ifunc_resolve_name: resolving name "the_function"
    [gnu-ifunc] elf_gnu_ifunc_resolve_by_cache: resolving "the_function" by cache
    [gnu-ifunc] elf_gnu_ifunc_resolve_by_cache: cache miss for "the_function"
    [gnu-ifunc] elf_gnu_ifunc_resolve_by_got: resolving "the_function" by GOT
    [gnu-ifunc] elf_gnu_ifunc_resolve_by_got: GOT entry "the_function@got.plt" points to 0x7ffff7fb7036
    [gnu-ifunc] elf_gnu_ifunc_record_cache: recording cache entry for "the_function" at 0x7ffff7fb7036
    [gnu-ifunc] elf_gnu_ifunc_record_cache: minimal symbol "the_function@plt" at 0x7ffff7fb7030 does not match addr 0x7ffff7fb7036, not caching
    [gnu-ifunc] elf_gnu_ifunc_resolve_by_got: GOT entry "the_function@got.plt" points to 0x7ffff7fb2036
    [gnu-ifunc] elf_gnu_ifunc_record_cache: recording cache entry for "the_function" at 0x7ffff7fb2036
    [gnu-ifunc] elf_gnu_ifunc_record_cache: minimal symbol "the_function@plt" at 0x7ffff7fb2030 does not match addr 0x7ffff7fb2036, not caching
    [gnu-ifunc] elf_gnu_ifunc_resolve_by_got: failed to resolve "the_function" by GOT
    [gnu-ifunc] elf_gnu_ifunc_resolve_name: failed to resolve name "the_function"
    Breakpoint 2 at gnu-indirect-function resolver at 0x7ffff7fa80e9
    (gdb) c
    Continuing.
    [gnu-ifunc] elf_gnu_ifunc_resolver_stop: stop on resolver for "the_function"
    [gnu-ifunc] elf_gnu_ifunc_resolver_stop: created resolver return breakpoint at 0x7ffff7fd7186
    [gnu-ifunc] elf_gnu_ifunc_resolver_return_stop: stop on resolver return
    [gnu-ifunc] elf_gnu_ifunc_resolver_return_stop: resolver for "the_function" returned resolved address=0x7ffff7fad0e9, resolved pc=0x7ffff7fad0e9
    [gnu-ifunc] elf_gnu_ifunc_record_cache: recording cache entry for "the_function" at 0x7ffff7fad0e9
    [gnu-ifunc] elf_gnu_ifunc_record_cache: cached "the_function" -> 0x7ffff7fad0e9 in objfile /home/simark/build/binutils-gdb/gdb/testsuite/outputs/gdb.base/ifunc-resolver/libimpl.so

    Breakpoint 2, the_function_impl_0 (caller_id=1) at /home/simark/src/binutils-gdb/gdb/testsuite/gdb.base/ifunc-resolver-libimpl.c:25
    25        the_function_last_caller_id = caller_id; /* break-in-impl */

Change-Id: I64f667e3457feaedfe9bb530de58faaf22545fa5
---
 gdb/NEWS                  |   4 ++
 gdb/doc/gdb.texinfo       |   6 +++
 gdb/elfread.c             | 110 +++++++++++++++++++++++++++++++++++---
 gdbsupport/common-debug.h |   9 ++++
 4 files changed, 123 insertions(+), 6 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index fa6e7ca61219..1957e63eae44 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -85,6 +85,10 @@ maintenance test-remote-args ARGS
   Test splitting and joining of inferior arguments ARGS as they would
   be split and joined when being passed to a remote target.
 
+set debug gnu-ifunc on|off
+show debug gnu-ifunc
+  Turn on or off debug messages related to GNU ifunc resolution.
+
 set progress-bars enabled on|off
 show progress-bars enabled
   Allows the progress bars, used when debuginfod is downloading
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 5169be1965c5..c614e93b812e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -29331,6 +29331,12 @@ default is off.
 Displays the current state of displaying @value{GDBN} frame debugging
 info.
 
+@cindex GNU ifunc debug messages
+@item set debug gnu-ifunc
+Turn on or off debugging messages related to GNU ifunc resolution.
+@item show debug gnu-ifunc
+Show the current state of GNU ifunc resolution debugging messages.
+
 @item set debug gnu-nat
 @cindex @sc{gnu}/Hurd debug messages
 Turn on or off debugging messages from the @sc{gnu}/Hurd debug support.
diff --git a/gdb/elfread.c b/gdb/elfread.c
index 722f21bf7753..61dd8ad260ba 100644
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
@@ -47,6 +47,23 @@
 /* Whether ctf should always be read, or only if no dwarf is present.  */
 static bool always_read_ctf;
 
+/* Value of the 'set debug gnu-ifunc' configuration variable.  */
+static bool debug_gnu_ifunc;
+
+static void
+show_debug_gnu_ifunc (struct ui_file *file, int from_tty,
+		      struct cmd_list_element *c, const char *value)
+{
+  gdb_printf (file, _("gnu-ifunc debugging is %s.\n"), value);
+}
+
+#define gnu_ifunc_debug_printf(fmt, ...) \
+  debug_prefixed_printf_cond (debug_gnu_ifunc, "gnu-ifunc", fmt, ##__VA_ARGS__)
+
+#define gnu_ifunc_debug_printf_func(func, fmt, ...)			    \
+  debug_prefixed_printf_cond_func (debug_gnu_ifunc, "gnu-ifunc", func, fmt, \
+				   ##__VA_ARGS__)
+
 /* The struct elfinfo is available only during ELF symbol table and
    psymtab reading.  It is destroyed at the completion of psymtab-reading.
    It's local to elf_symfile_read.  */
@@ -695,11 +712,28 @@ elf_gnu_ifunc_record_cache (const char *name, CORE_ADDR addr)
   struct elf_gnu_ifunc_cache entry_local, *entry_p;
   void **slot;
 
+  gnu_ifunc_debug_printf ("recording cache entry for \"%s\" at %s",
+			  name, paddress (current_inferior ()->arch (), addr));
+
   bound_minimal_symbol msym = lookup_minimal_symbol_by_pc (addr);
   if (msym.minsym == NULL)
-    return 0;
+    {
+      gnu_ifunc_debug_printf ("no minimal symbol found at %s, not caching",
+			      paddress (current_inferior ()->arch (), addr));
+      return 0;
+    }
+
   if (msym.value_address () != addr)
-    return 0;
+    {
+      gnu_ifunc_debug_printf ("minimal symbol \"%s\" at %s does not match "
+			      "addr %s, not caching",
+			      msym.minsym->linkage_name (),
+			      paddress (current_inferior ()->arch (),
+					msym.value_address ()),
+			      paddress (current_inferior ()->arch (), addr));
+      return 0;
+    }
+
   objfile = msym.objfile;
 
   /* If .plt jumps back to .plt the symbol is still deferred for later
@@ -711,10 +745,18 @@ elf_gnu_ifunc_record_cache (const char *name, CORE_ADDR addr)
      symbol is in the .plt section because some systems have @plt
      symbols in the .text section.  */
   if (len > 4 && strcmp (target_name + len - 4, "@plt") == 0)
-    return 0;
+    {
+      gnu_ifunc_debug_printf ("target \"%s\" is a PLT stub, not caching",
+			      target_name);
+      return 0;
+    }
 
   if (strcmp (target_name, "_PROCEDURE_LINKAGE_TABLE_") == 0)
-    return 0;
+    {
+      gnu_ifunc_debug_printf ("target is _PROCEDURE_LINKAGE_TABLE_, "
+			      "not caching");
+      return 0;
+    }
 
   htab = elf_objfile_gnu_ifunc_cache_data.get (objfile);
   if (htab == NULL)
@@ -754,6 +796,9 @@ elf_gnu_ifunc_record_cache (const char *name, CORE_ADDR addr)
     }
   *slot = entry_p;
 
+  gnu_ifunc_debug_printf ("cached \"%s\" -> %s in objfile %s",
+			  name, paddress (objfile->arch (), addr),
+			  objfile_name (objfile));
   return 1;
 }
 
@@ -767,6 +812,7 @@ elf_gnu_ifunc_record_cache (const char *name, CORE_ADDR addr)
 static int
 elf_gnu_ifunc_resolve_by_cache (const char *name, CORE_ADDR *addr_p)
 {
+  gnu_ifunc_debug_printf ("resolving \"%s\" by cache", name);
   int found = 0;
 
   /* FIXME: we only search the initial namespace.
@@ -797,10 +843,17 @@ elf_gnu_ifunc_resolve_by_cache (const char *name, CORE_ADDR *addr_p)
 	 if (addr_p)
 	   *addr_p = entry_p->addr;
 
+	 gnu_ifunc_debug_printf ("cache hit for \"%s\" -> %s in objfile %s",
+				 name,
+				 paddress (objfile->arch (), entry_p->addr),
+				 objfile_name (objfile));
 	 found = 1;
 	 return 1;
        }, nullptr);
 
+  if (!found)
+    gnu_ifunc_debug_printf ("cache miss for \"%s\"", name);
+
   return found;
 }
 
@@ -815,9 +868,11 @@ elf_gnu_ifunc_resolve_by_cache (const char *name, CORE_ADDR *addr_p)
 static int
 elf_gnu_ifunc_resolve_by_got (const char *name, CORE_ADDR *addr_p)
 {
+  gnu_ifunc_debug_printf ("resolving \"%s\" by GOT", name);
   char *name_got_plt;
   const size_t got_suffix_len = strlen (SYMBOL_GOT_PLT_SUFFIX);
   int found = 0;
+  const char *func = __func__;
 
   name_got_plt = (char *) alloca (strlen (name) + got_suffix_len + 1);
   sprintf (name_got_plt, "%s" SYMBOL_GOT_PLT_SUFFIX, name);
@@ -827,7 +882,7 @@ elf_gnu_ifunc_resolve_by_got (const char *name, CORE_ADDR *addr_p)
      To search other namespaces, we would need to provide context, e.g. in
      form of an objfile in that namespace.  */
   current_program_space->iterate_over_objfiles_in_search_order
-    ([name, name_got_plt, &addr_p, &found] (struct objfile *objfile)
+    ([name, name_got_plt, &addr_p, &found, func] (struct objfile *objfile)
        {
 	 bfd *obfd = objfile->obfd.get ();
 	 struct gdbarch *gdbarch = objfile->arch ();
@@ -859,11 +914,16 @@ elf_gnu_ifunc_resolve_by_got (const char *name, CORE_ADDR *addr_p)
 	   (gdbarch, addr, current_inferior ()->top_target ());
 	 addr = gdbarch_addr_bits_remove (gdbarch, addr);
 
+	 gnu_ifunc_debug_printf_func (func, "GOT entry \"%s\" points to %s",
+				      name_got_plt, paddress (gdbarch, addr));
+
 	 if (elf_gnu_ifunc_record_cache (name, addr))
 	   {
 	     if (addr_p != NULL)
 	       *addr_p = addr;
 
+	     gnu_ifunc_debug_printf ("resolved \"%s\" via GOT to %s",
+				     name, paddress (gdbarch, addr));
 	     found = 1;
 	     return 1;
 	   }
@@ -871,6 +931,9 @@ elf_gnu_ifunc_resolve_by_got (const char *name, CORE_ADDR *addr_p)
 	 return 0;
        }, nullptr);
 
+  if (!found)
+    gnu_ifunc_debug_printf ("failed to resolve \"%s\" by GOT", name);
+
   return found;
 }
 
@@ -884,17 +947,20 @@ elf_gnu_ifunc_resolve_by_got (const char *name, CORE_ADDR *addr_p)
 static bool
 elf_gnu_ifunc_resolve_name (const char *name, CORE_ADDR *addr_p)
 {
+  gnu_ifunc_debug_printf ("resolving name \"%s\"", name);
+
   if (elf_gnu_ifunc_resolve_by_cache (name, addr_p))
     return true;
 
   if (elf_gnu_ifunc_resolve_by_got (name, addr_p))
     return true;
 
+  gnu_ifunc_debug_printf ("failed to resolve name \"%s\"", name);
   return false;
 }
 
 /* Call STT_GNU_IFUNC - a function returning address of a real function to
-   call.  PC is theSTT_GNU_IFUNC resolving function entry.  The value returned
+   call.  PC is the STT_GNU_IFUNC resolving function entry.  The value returned
    is the entry point of the resolved STT_GNU_IFUNC target function to call.
    */
 
@@ -908,6 +974,8 @@ elf_gnu_ifunc_resolve_addr (struct gdbarch *gdbarch, CORE_ADDR pc)
   CORE_ADDR hwcap = 0;
   struct value *hwcap_val;
 
+  gnu_ifunc_debug_printf ("resolving ifunc %s", paddress (gdbarch, pc));
+
   /* Try first any non-intrusive methods without an inferior call.  */
 
   if (find_pc_partial_function (pc, &name_at_pc, &start_at_pc, NULL)
@@ -919,6 +987,9 @@ elf_gnu_ifunc_resolve_addr (struct gdbarch *gdbarch, CORE_ADDR pc)
   else
     name_at_pc = NULL;
 
+  gnu_ifunc_debug_printf ("resolving via inferior call to resolver at %s",
+			  paddress (gdbarch, pc));
+
   function = value::allocate (func_func_type);
   function->set_lval (lval_memory);
   function->set_address (pc);
@@ -936,6 +1007,10 @@ elf_gnu_ifunc_resolve_addr (struct gdbarch *gdbarch, CORE_ADDR pc)
     (gdbarch, address, current_inferior ()->top_target ());
   address = gdbarch_addr_bits_remove (gdbarch, address);
 
+  gnu_ifunc_debug_printf ("resolver at %s returned %s",
+			  paddress (gdbarch, pc),
+			  paddress (gdbarch, address));
+
   if (name_at_pc)
     elf_gnu_ifunc_record_cache (name_at_pc, address);
 
@@ -953,6 +1028,9 @@ elf_gnu_ifunc_resolver_stop (code_breakpoint *b)
   CORE_ADDR prev_pc = get_frame_pc (prev_frame);
   int thread_id = inferior_thread ()->global_num;
 
+  gnu_ifunc_debug_printf ("stop on resolver for \"%s\"",
+			  b->locspec->to_string ());
+
   gdb_assert (b->type == bp_gnu_ifunc_resolver);
 
   for (b_return = b->related_breakpoint; b_return != b;
@@ -983,12 +1061,17 @@ elf_gnu_ifunc_resolver_stop (code_breakpoint *b)
 				    prev_frame_id,
 				    bp_gnu_ifunc_resolver_return).release ();
 
+      gnu_ifunc_debug_printf ("created resolver return breakpoint at %s",
+			      paddress (get_frame_arch (prev_frame), prev_pc));
 
       /* Add new b_return to the ring list b->related_breakpoint.  */
       gdb_assert (b_return->related_breakpoint == b_return);
       b_return->related_breakpoint = b->related_breakpoint;
       b->related_breakpoint = b_return;
     }
+  else
+    gnu_ifunc_debug_printf ("found existing resolver return breakpoint at %s",
+			    paddress (get_frame_arch (prev_frame), prev_pc));
 }
 
 /* Handle inferior hit of bp_gnu_ifunc_resolver_return, see its definition.  */
@@ -1005,6 +1088,8 @@ elf_gnu_ifunc_resolver_return_stop (code_breakpoint *b)
   struct value *value;
   CORE_ADDR resolved_address, resolved_pc;
 
+  gnu_ifunc_debug_printf ("stop on resolver return");
+
   gdb_assert (b->type == bp_gnu_ifunc_resolver_return);
 
   while (b->related_breakpoint != b)
@@ -1040,6 +1125,12 @@ elf_gnu_ifunc_resolver_return_stop (code_breakpoint *b)
     (gdbarch, resolved_address, current_inferior ()->top_target ());
   resolved_pc = gdbarch_addr_bits_remove (gdbarch, resolved_pc);
 
+  gnu_ifunc_debug_printf ("resolver for \"%s\" returned resolved address=%s, "
+			  "resolved pc=%s",
+			  b->locspec->to_string (),
+			  paddress (gdbarch, resolved_address),
+			  paddress (gdbarch, resolved_pc));
+
   gdb_assert (current_program_space == b->pspace || b->pspace == NULL);
   elf_gnu_ifunc_record_cache (b->locspec->to_string (), resolved_pc);
 
@@ -1343,6 +1434,13 @@ INIT_GDB_FILE (elfread)
 
   gnu_ifunc_fns_p = &elf_gnu_ifunc_fns;
 
+  add_setshow_boolean_cmd
+    ("gnu-ifunc", class_maintenance, &debug_gnu_ifunc,
+     _("Set GNU ifunc debugging."),
+     _("Show GNU ifunc debugging."),
+     _("When on, debug output for GNU ifunc resolution is displayed."),
+     nullptr, show_debug_gnu_ifunc, &setdebuglist, &showdebuglist);
+
   /* Add "set always-read-ctf on/off".  */
   add_setshow_boolean_cmd ("always-read-ctf", class_support, &always_read_ctf,
 			   _("\
diff --git a/gdbsupport/common-debug.h b/gdbsupport/common-debug.h
index f0edd49faead..206a7c96dc1a 100644
--- a/gdbsupport/common-debug.h
+++ b/gdbsupport/common-debug.h
@@ -71,6 +71,15 @@ extern void ATTRIBUTE_PRINTF (3, 0) debug_prefixed_vprintf
     } \
   while (0)
 
+#define debug_prefixed_printf_cond_func(debug_enabled_cond, mod, func, fmt, \
+					...) \
+  do \
+    { \
+      if (debug_enabled_cond) \
+	debug_prefixed_printf (mod, func, fmt, ##__VA_ARGS__); \
+    } \
+  while (0)
+
 #define debug_prefixed_printf_cond_nofunc(debug_enabled_cond, mod, fmt, ...) \
   do \
     { \

base-commit: 3a862dc6c3da4214aa12d36d5fbc45a420fb40d5
-- 
2.53.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-02-13 19:10 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-02-13  5:38 [PATCH 1/4] gdb/elfread: add debug output for GNU ifunc resolution simon.marchi
2026-02-13  5:38 ` [PATCH 2/4] gdb/elfread: replace ifunc htab_t with gdb::unordered_map simon.marchi
2026-02-13  5:38 ` [PATCH 3/4] gdbsupport: add gdb::unordered_string_map simon.marchi
2026-02-13  5:38 ` [PATCH 4/4] gdb: use gdb::unordered_string_map throughout simon.marchi
2026-02-13  8:30 ` [PATCH 1/4] gdb/elfread: add debug output for GNU ifunc resolution Eli Zaretskii
2026-02-13 17:20   ` Simon Marchi
2026-02-13 18:27 ` Kevin Buettner
2026-02-13 19:10   ` Simon Marchi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox