]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb/elfread: add debug output for GNU ifunc resolution
authorSimon Marchi <simon.marchi@polymtl.ca>
Fri, 13 Feb 2026 03:47:37 +0000 (22:47 -0500)
committerSimon Marchi <simon.marchi@polymtl.ca>
Fri, 13 Feb 2026 19:03:21 +0000 (14:03 -0500)
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
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Approved-by: Kevin Buettner <kevinb@redhat.com>
gdb/NEWS
gdb/doc/gdb.texinfo
gdb/elfread.c
gdbsupport/common-debug.h

index fa6e7ca6121901f9f01a496c894191c4a2fe5a1e..1957e63eae44b58c0c1a90ed8d47f2c6c9b19f0d 100644 (file)
--- 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
index 5169be1965c5fd92c85fdc269c05260a386f68e7..c614e93b812ef089553a209719fc1cab09227c94 100644 (file)
@@ -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.
index 722f21bf77536ad26e8c05da32977fb1197195ff..7d327674b0598f05954bdeab4c8bf8df071c828b 100644 (file)
 /* 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,14 +812,16 @@ 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;
+  const char *func = __func__;
 
   /* FIXME: we only search the initial namespace.
 
      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, &addr_p, &found] (struct objfile *objfile)
+    ([name, &addr_p, &found, func] (struct objfile *objfile)
        {
         htab_t htab;
         elf_gnu_ifunc_cache *entry_p;
@@ -797,10 +844,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_func
+          (func, "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 +869,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 +883,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 +915,17 @@ 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_func (func,
+                                         "resolved \"%s\" via GOT to %s",
+                                         name, paddress (gdbarch, addr));
             found = 1;
             return 1;
           }
@@ -871,6 +933,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 +949,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 +976,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 +989,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 +1009,9 @@ 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 +1029,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 +1062,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 +1089,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 +1126,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 +1435,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,
                           _("\
index f0edd49faead7e238f9bbd2282b4fa50e3b6f4fb..206a7c96dc1ad645e07162d748f3ed63c760a58b 100644 (file)
@@ -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 \
     { \