From: simon.marchi@polymtl.ca
To: gdb-patches@sourceware.org
Cc: Simon Marchi <simon.marchi@polymtl.ca>
Subject: [PATCH 1/4] gdb/elfread: add debug output for GNU ifunc resolution
Date: Fri, 13 Feb 2026 00:38:48 -0500 [thread overview]
Message-ID: <20260213053912.3520505-1-simon.marchi@polymtl.ca> (raw)
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
next reply other threads:[~2026-02-13 5:41 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-13 5:38 simon.marchi [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260213053912.3520505-1-simon.marchi@polymtl.ca \
--to=simon.marchi@polymtl.ca \
--cc=gdb-patches@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox