From e5ebb42ea4e60713dba25894ffc0800531ff608c Mon Sep 17 00:00:00 2001 From: Simon Marchi Date: Thu, 12 Feb 2026 22:47:37 -0500 Subject: [PATCH] gdb/elfread: add debug output for GNU ifunc resolution 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 Approved-by: Kevin Buettner --- gdb/NEWS | 4 ++ gdb/doc/gdb.texinfo | 6 ++ gdb/elfread.c | 113 +++++++++++++++++++++++++++++++++++--- gdbsupport/common-debug.h | 9 +++ 4 files changed, 125 insertions(+), 7 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index fa6e7ca6121..1957e63eae4 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 5169be1965c..c614e93b812 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 722f21bf775..7d327674b05 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,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, _("\ diff --git a/gdbsupport/common-debug.h b/gdbsupport/common-debug.h index f0edd49faea..206a7c96dc1 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 \ { \ -- 2.47.3