* [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* [PATCH 2/4] gdb/elfread: replace ifunc htab_t with gdb::unordered_map
2026-02-13 5:38 [PATCH 1/4] gdb/elfread: add debug output for GNU ifunc resolution simon.marchi
@ 2026-02-13 5:38 ` simon.marchi
2026-02-13 5:38 ` [PATCH 3/4] gdbsupport: add gdb::unordered_string_map simon.marchi
` (3 subsequent siblings)
4 siblings, 0 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@efficios.com>
Replace this htab_t with gdb::unordered_map. No functional change
expected.
Change-Id: Icad43938f761a032b6a6257f4be0f4ab5c60661c
---
gdb/elfread.c | 120 ++++++++++++--------------------------------------
1 file changed, 27 insertions(+), 93 deletions(-)
diff --git a/gdb/elfread.c b/gdb/elfread.c
index 61dd8ad260ba..010744639118 100644
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
@@ -43,6 +43,7 @@
#include <string_view>
#include "dwarf2/public.h"
#include "cli/cli-cmds.h"
+#include "gdbsupport/unordered_map.h"
/* Whether ctf should always be read, or only if no dwarf is present. */
static bool always_read_ctf;
@@ -657,44 +658,12 @@ elf_rel_plt_read (minimal_symbol_reader &reader,
}
}
-/* The data pointer is htab_t for gnu_ifunc_record_cache_unchecked. */
+/* Per-objfile cache mapping function names to resolved ifunc addresses. */
-static const registry<objfile>::key<htab, htab_deleter>
- elf_objfile_gnu_ifunc_cache_data;
-
-/* Map function names to CORE_ADDR in elf_objfile_gnu_ifunc_cache_data. */
-
-struct elf_gnu_ifunc_cache
-{
- /* This is always a function entry address, not a function descriptor. */
- CORE_ADDR addr;
-
- char name[1];
-};
-
-/* htab_hash for elf_objfile_gnu_ifunc_cache_data. */
-
-static hashval_t
-elf_gnu_ifunc_cache_hash (const void *a_voidp)
-{
- const struct elf_gnu_ifunc_cache *a
- = (const struct elf_gnu_ifunc_cache *) a_voidp;
-
- return htab_hash_string (a->name);
-}
-
-/* htab_eq for elf_objfile_gnu_ifunc_cache_data. */
-
-static int
-elf_gnu_ifunc_cache_eq (const void *a_voidp, const void *b_voidp)
-{
- const struct elf_gnu_ifunc_cache *a
- = (const struct elf_gnu_ifunc_cache *) a_voidp;
- const struct elf_gnu_ifunc_cache *b
- = (const struct elf_gnu_ifunc_cache *) b_voidp;
+using elf_gnu_ifunc_cache = gdb::unordered_map<std::string, CORE_ADDR>;
- return strcmp (a->name, b->name) == 0;
-}
+static const registry<objfile>::key<elf_gnu_ifunc_cache>
+ elf_objfile_gnu_ifunc_cache_data;
/* Record the target function address of a STT_GNU_IFUNC function NAME is the
function entry address ADDR. Return 1 if NAME and ADDR are considered as
@@ -707,13 +676,8 @@ elf_gnu_ifunc_cache_eq (const void *a_voidp, const void *b_voidp)
static int
elf_gnu_ifunc_record_cache (const char *name, CORE_ADDR addr)
{
- struct objfile *objfile;
- htab_t htab;
- 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));
+ 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)
@@ -734,7 +698,7 @@ elf_gnu_ifunc_record_cache (const char *name, CORE_ADDR addr)
return 0;
}
- objfile = msym.objfile;
+ objfile *objfile = msym.objfile;
/* If .plt jumps back to .plt the symbol is still deferred for later
resolution and it has no use for GDB. */
@@ -758,43 +722,23 @@ elf_gnu_ifunc_record_cache (const char *name, CORE_ADDR addr)
return 0;
}
- htab = elf_objfile_gnu_ifunc_cache_data.get (objfile);
- if (htab == NULL)
- {
- htab = htab_create_alloc (1, elf_gnu_ifunc_cache_hash,
- elf_gnu_ifunc_cache_eq,
- NULL, xcalloc, xfree);
- elf_objfile_gnu_ifunc_cache_data.set (objfile, htab);
- }
+ elf_gnu_ifunc_cache &cache
+ = elf_objfile_gnu_ifunc_cache_data.try_emplace (objfile);
- entry_local.addr = addr;
- obstack_grow (&objfile->objfile_obstack, &entry_local,
- offsetof (struct elf_gnu_ifunc_cache, name));
- obstack_grow_str0 (&objfile->objfile_obstack, name);
- entry_p
- = (struct elf_gnu_ifunc_cache *) obstack_finish (&objfile->objfile_obstack);
-
- slot = htab_find_slot (htab, entry_p, INSERT);
- if (*slot != NULL)
+ auto [it, inserted] = cache.emplace (name, addr);
+ if (!inserted && it->second != addr)
{
- struct elf_gnu_ifunc_cache *entry_found_p
- = (struct elf_gnu_ifunc_cache *) *slot;
+ /* This case indicates buggy inferior program, the resolved
+ address should never change. */
struct gdbarch *gdbarch = objfile->arch ();
- if (entry_found_p->addr != addr)
- {
- /* This case indicates buggy inferior program, the resolved address
- should never change. */
-
- warning (_("gnu-indirect-function \"%s\" has changed its resolved "
- "function_address from %s to %s"),
- name, paddress (gdbarch, entry_found_p->addr),
- paddress (gdbarch, addr));
- }
+ warning (_("gnu-indirect-function \"%s\" has changed its "
+ "resolved function_address from %s to %s"),
+ name, paddress (gdbarch, it->second),
+ paddress (gdbarch, addr));
- /* New ENTRY_P is here leaked/duplicate in the OBJFILE obstack. */
+ it->second = addr;
}
- *slot = entry_p;
gnu_ifunc_debug_printf ("cached \"%s\" -> %s in objfile %s",
name, paddress (objfile->arch (), addr),
@@ -822,30 +766,20 @@ elf_gnu_ifunc_resolve_by_cache (const char *name, CORE_ADDR *addr_p)
current_program_space->iterate_over_objfiles_in_search_order
([name, &addr_p, &found] (struct objfile *objfile)
{
- htab_t htab;
- elf_gnu_ifunc_cache *entry_p;
- void **slot;
-
- htab = elf_objfile_gnu_ifunc_cache_data.get (objfile);
- if (htab == NULL)
+ elf_gnu_ifunc_cache *cache
+ = elf_objfile_gnu_ifunc_cache_data.get (objfile);
+ if (cache == nullptr)
return 0;
- entry_p = ((elf_gnu_ifunc_cache *)
- alloca (sizeof (*entry_p) + strlen (name)));
- strcpy (entry_p->name, name);
-
- slot = htab_find_slot (htab, entry_p, NO_INSERT);
- if (slot == NULL)
+ auto it = cache->find (name);
+ if (it == cache->end ())
return 0;
- entry_p = (elf_gnu_ifunc_cache *) *slot;
- gdb_assert (entry_p != NULL);
- if (addr_p)
- *addr_p = entry_p->addr;
+ if (addr_p != nullptr)
+ *addr_p = it->second;
gnu_ifunc_debug_printf ("cache hit for \"%s\" -> %s in objfile %s",
- name,
- paddress (objfile->arch (), entry_p->addr),
+ name, paddress (objfile->arch (), it->second),
objfile_name (objfile));
found = 1;
return 1;
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH 3/4] gdbsupport: add gdb::unordered_string_map
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 ` simon.marchi
2026-02-13 5:38 ` [PATCH 4/4] gdb: use gdb::unordered_string_map throughout simon.marchi
` (2 subsequent siblings)
4 siblings, 0 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@efficios.com>
It occurred to me that when we use a
gdb::unordered_map<std::string, T>
we construct a temporary std::string for each lookup from a
`const char *`, possibly doing dynamic allocation. This is really
unnecessary, because it's easy to compare an existing `const char *` (or
std::string_view) with an std::string. This can therefore be avoided by
having transparent hash and eq types.
Because this is a common enough case, add it to
gdbsupport/unordered_map.h, so it can easily be reused. Define the
gdb::unordered_string_map type, which uses hash and eq types that work
on std::string_view. Both `const char *` and `std::string` can
implicitly be converted to std::string_view, so this should be
sufficient.
Change-Id: Id93448d831696d25472f13c15212f13712ad8492
---
gdbsupport/unordered_map.h | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/gdbsupport/unordered_map.h b/gdbsupport/unordered_map.h
index b615f2422f62..e0b97764d0b3 100644
--- a/gdbsupport/unordered_map.h
+++ b/gdbsupport/unordered_map.h
@@ -32,6 +32,40 @@ using unordered_map
<Key, T, Hash, KeyEqual, std::allocator<std::pair<Key, T>>,
ankerl::unordered_dense::bucket_type::standard>;
+/* An unordered_map with std::string keys that supports transparent
+ lookup from std::string_view, avoiding the construction of temporary
+ std::string objects during lookups. std::string_view is implicitly
+ constructible from `const char *` and `std::string`, so it covers those
+ too. */
+
+namespace detail
+{
+
+struct unordered_string_map_hash
+{
+ using is_transparent = void;
+ using is_avalanching = void;
+
+ std::uint64_t operator() (std::string_view sv) const noexcept
+ { return ankerl::unordered_dense::hash<std::string_view> () (sv); }
+};
+
+struct unordered_string_map_eq
+{
+ using is_transparent = void;
+
+ bool operator() (std::string_view lhs, std::string_view rhs) const noexcept
+ { return lhs == rhs; }
+};
+
+} /* namespace detail */
+
+template<typename T>
+using unordered_string_map
+ = gdb::unordered_map<std::string, T,
+ detail::unordered_string_map_hash,
+ detail::unordered_string_map_eq>;
+
} /* namespace gdb */
#endif /* GDBSUPPORT_UNORDERED_MAP_H */
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH 4/4] gdb: use gdb::unordered_string_map throughout
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 ` simon.marchi
2026-02-13 8:30 ` [PATCH 1/4] gdb/elfread: add debug output for GNU ifunc resolution Eli Zaretskii
2026-02-13 18:27 ` Kevin Buettner
4 siblings, 0 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@efficios.com>
Change all uses of
gdb::unordered_map<string, T>
to
gdb::unordered_string_map<T>
Using unordered_string_map avoids constructing a temporary std::string
(possibly with dynamic allocation) when looking up values from
`const char *`.
I don't see any downsides to using unordered_string_map over
unordered_map, even if all the lookups are done using existing
std::string objects that would be built anyway.
I think there are places where we could refactor the code to avoid
constructing unnecessary std::string objects, but that is out of scope
for this patch.
There should be no change of behavior with this patch.
Change-Id: Ic4df78b97f2755d5821bb66942b9e4d9a79a3dc5
---
gdb/ada-exp.y | 2 +-
gdb/amdgpu-tdep.c | 2 +-
gdb/amdgpu-tdep.h | 2 +-
gdb/corelow.c | 6 +++---
gdb/cp-namespace.c | 2 +-
gdb/elfread.c | 2 +-
gdb/gdb_bfd.c | 2 +-
gdb/gdbarch-selftests.c | 2 +-
gdb/mi/mi-cmds.c | 2 +-
gdb/solib-rocm.c | 4 ++--
gdb/solib-svr4.c | 2 +-
gdb/source-cache.h | 2 +-
gdb/tui/tui-layout.h | 2 +-
gdb/xml-tdesc.c | 2 +-
14 files changed, 17 insertions(+), 17 deletions(-)
diff --git a/gdb/ada-exp.y b/gdb/ada-exp.y
index 771a867c4ae6..e02f1e6d10a2 100644
--- a/gdb/ada-exp.y
+++ b/gdb/ada-exp.y
@@ -97,7 +97,7 @@ struct ada_parse_state
std::vector<ada_assign_up> assignments;
/* Track currently active iterated assignment names. */
- gdb::unordered_map<std::string, std::vector<ada_index_var_operation *>>
+ gdb::unordered_string_map<std::vector<ada_index_var_operation *>>
iterated_associations;
auto_obstack temp_space;
diff --git a/gdb/amdgpu-tdep.c b/gdb/amdgpu-tdep.c
index fc45623f92b1..5a275165fbb2 100644
--- a/gdb/amdgpu-tdep.c
+++ b/gdb/amdgpu-tdep.c
@@ -356,7 +356,7 @@ using amd_dbgapi_register_type_enum_up
/* Map type lookup names to types. */
using amd_dbgapi_register_type_map
- = gdb::unordered_map<std::string, amd_dbgapi_register_type_up>;
+ = gdb::unordered_string_map<amd_dbgapi_register_type_up>;
/* Parse S as a ULONGEST, raise an error on overflow. */
diff --git a/gdb/amdgpu-tdep.h b/gdb/amdgpu-tdep.h
index c0adf55fdf60..5404c2901b54 100644
--- a/gdb/amdgpu-tdep.h
+++ b/gdb/amdgpu-tdep.h
@@ -78,7 +78,7 @@ struct amdgpu_gdbarch_tdep : gdbarch_tdep_base
regnum_map;
/* A map of register_class_ids keyed by their name. */
- gdb::unordered_map<std::string, amd_dbgapi_register_class_id_t>
+ gdb::unordered_string_map<amd_dbgapi_register_class_id_t>
register_class_map;
};
diff --git a/gdb/corelow.c b/gdb/corelow.c
index 658ff6e0198c..a28a707c293a 100644
--- a/gdb/corelow.c
+++ b/gdb/corelow.c
@@ -124,7 +124,7 @@ struct mapped_file_info
/* A type that maps a string to a build-id. */
using string_to_build_id_map
- = gdb::unordered_map<std::string, const bfd_build_id *>;
+ = gdb::unordered_string_map<const bfd_build_id *>;
/* A type that maps a build-id to a string. */
using build_id_to_string_map
@@ -402,7 +402,7 @@ core_target::core_target (gdb_bfd_ref_ptr cbfd_ref)
void
core_target::build_file_mappings ()
{
- gdb::unordered_map<std::string, struct bfd *> bfd_map;
+ gdb::unordered_string_map<struct bfd *> bfd_map;
gdb::unordered_set<std::string> unavailable_paths;
/* All files mapped into the core file. The key is the filename. */
@@ -2122,7 +2122,7 @@ gdb_read_core_file_mappings (struct gdbarch *gdbarch, struct bfd *cbfd)
};
/* All files mapped into the core file. The key is the filename. */
- gdb::unordered_map<std::string, map_entry> mapped_files;
+ gdb::unordered_string_map<map_entry> mapped_files;
/* Get the build-id of the core file. At least on Linux, this will be
the build-id for the main executable. If other targets add the
diff --git a/gdb/cp-namespace.c b/gdb/cp-namespace.c
index 2e3c539507cc..efdb2a2c850a 100644
--- a/gdb/cp-namespace.c
+++ b/gdb/cp-namespace.c
@@ -349,7 +349,7 @@ cp_lookup_symbol_in_namespace (const char *the_namespace, const char *name,
}
/* Type used for collecting symbols. Maps names to symbols. */
-using symbol_map = gdb::unordered_map<std::string, block_symbol>;
+using symbol_map = gdb::unordered_string_map<block_symbol>;
/* This version of the function is internal, use the wrapper unless
the list of ambiguous symbols is needed.
diff --git a/gdb/elfread.c b/gdb/elfread.c
index 010744639118..04e9a986031c 100644
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
@@ -660,7 +660,7 @@ elf_rel_plt_read (minimal_symbol_reader &reader,
/* Per-objfile cache mapping function names to resolved ifunc addresses. */
-using elf_gnu_ifunc_cache = gdb::unordered_map<std::string, CORE_ADDR>;
+using elf_gnu_ifunc_cache = gdb::unordered_string_map<CORE_ADDR>;
static const registry<objfile>::key<elf_gnu_ifunc_cache>
elf_objfile_gnu_ifunc_cache_data;
diff --git a/gdb/gdb_bfd.c b/gdb/gdb_bfd.c
index 98ea46677493..1933166b5fbf 100644
--- a/gdb/gdb_bfd.c
+++ b/gdb/gdb_bfd.c
@@ -1241,7 +1241,7 @@ maintenance_info_bfds (const char *arg, int from_tty)
struct bfd_inferior_data
{
- gdb::unordered_map<std::string, unsigned long> bfd_error_string_counts;
+ gdb::unordered_string_map<unsigned long> bfd_error_string_counts;
};
/* Per-inferior data key. */
diff --git a/gdb/gdbarch-selftests.c b/gdb/gdbarch-selftests.c
index 424281134a36..c89ad5ce5fde 100644
--- a/gdb/gdbarch-selftests.c
+++ b/gdb/gdbarch-selftests.c
@@ -133,7 +133,7 @@ register_name_test (struct gdbarch *gdbarch)
scoped_mock_context<test_target_ops> mockctx (gdbarch);
/* Track the number of times each register name appears. */
- gdb::unordered_map<std::string, int> name_counts;
+ gdb::unordered_string_map<int> name_counts;
const int num_regs = gdbarch_num_cooked_regs (gdbarch);
for (auto regnum = 0; regnum < num_regs; regnum++)
diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c
index 552eafbbcbc4..48d85e8785c8 100644
--- a/gdb/mi/mi-cmds.c
+++ b/gdb/mi/mi-cmds.c
@@ -26,7 +26,7 @@
/* MI command table (built at run time). */
-static gdb::unordered_map<std::string, mi_command_up> mi_cmd_table;
+static gdb::unordered_string_map<mi_command_up> mi_cmd_table;
/* MI command with a pure MI implementation. */
diff --git a/gdb/solib-rocm.c b/gdb/solib-rocm.c
index c17d5932b553..291694176821 100644
--- a/gdb/solib-rocm.c
+++ b/gdb/solib-rocm.c
@@ -69,7 +69,7 @@ struct rocm_solib_fd_cache
};
inferior *m_inferior;
- gdb::unordered_map<std::string, refcnt_fd> m_cache;
+ gdb::unordered_string_map<refcnt_fd> m_cache;
};
int
@@ -102,7 +102,7 @@ rocm_solib_fd_cache::open (const std::string &filename,
int
rocm_solib_fd_cache::close (int fd, fileio_error *target_errno)
{
- using cache_val = gdb::unordered_map<std::string, refcnt_fd>::value_type;
+ using cache_val = gdb::unordered_string_map<refcnt_fd>::value_type;
auto it
= std::find_if (m_cache.begin (), m_cache.end (),
[fd](const cache_val &s) { return s.second.fd == fd; });
diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
index c8d78fbfc1dd..aeb6cd30f423 100644
--- a/gdb/solib-svr4.c
+++ b/gdb/solib-svr4.c
@@ -3753,7 +3753,7 @@ svr4_solib_ops::get_solibs_in_ns (int nsid) const
faster, and to be able to remove SOs from the map, to avoid
returning the dynamic linker multiple times. */
CORE_ADDR debug_base = info->namespace_id[nsid];
- gdb::unordered_map<std::string, const lm_info_svr4 *> namespace_solibs;
+ gdb::unordered_string_map<const lm_info_svr4 *> namespace_solibs;
for (svr4_so &so : info->solib_lists[debug_base])
namespace_solibs[so.name] = so.lm_info.get ();
diff --git a/gdb/source-cache.h b/gdb/source-cache.h
index 09f65cea62f4..ab4955e553ed 100644
--- a/gdb/source-cache.h
+++ b/gdb/source-cache.h
@@ -97,7 +97,7 @@ class source_cache
/* The file offset cache. The key is the full name of the source
file. */
- gdb::unordered_map<std::string, std::vector<off_t>> m_offset_cache;
+ gdb::unordered_string_map<std::vector<off_t>> m_offset_cache;
/* The list of files where styling failed. */
gdb::unordered_set<std::string> m_no_styling_files;
diff --git a/gdb/tui/tui-layout.h b/gdb/tui/tui-layout.h
index ca9afbabae6b..536174c784d4 100644
--- a/gdb/tui/tui-layout.h
+++ b/gdb/tui/tui-layout.h
@@ -367,7 +367,7 @@ typedef std::function<tui_win_info * (const char *name)> window_factory;
/* The type for a data structure that maps a window name to that window's
factory function. */
-typedef gdb::unordered_map<std::string, window_factory> window_types_map;
+typedef gdb::unordered_string_map<window_factory> window_types_map;
/* Register a new TUI window type. NAME is the name of the window
type. FACTORY is a function that can be called to instantiate the
diff --git a/gdb/xml-tdesc.c b/gdb/xml-tdesc.c
index ac73708f005d..472c604254a8 100644
--- a/gdb/xml-tdesc.c
+++ b/gdb/xml-tdesc.c
@@ -64,7 +64,7 @@ tdesc_parse_xml (const char *document, xml_fetch_another fetcher)
then we will create unnecessary duplicate gdbarches. See
gdbarch_list_lookup_by_info. */
-static gdb::unordered_map<std::string, target_desc_up> xml_cache;
+static gdb::unordered_string_map<target_desc_up> xml_cache;
/* Callback data for target description parsing. */
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH 1/4] gdb/elfread: add debug output for GNU ifunc resolution
2026-02-13 5:38 [PATCH 1/4] gdb/elfread: add debug output for GNU ifunc resolution simon.marchi
` (2 preceding siblings ...)
2026-02-13 5:38 ` [PATCH 4/4] gdb: use gdb::unordered_string_map throughout simon.marchi
@ 2026-02-13 8:30 ` Eli Zaretskii
2026-02-13 17:20 ` Simon Marchi
2026-02-13 18:27 ` Kevin Buettner
4 siblings, 1 reply; 8+ messages in thread
From: Eli Zaretskii @ 2026-02-13 8:30 UTC (permalink / raw)
To: simon.marchi; +Cc: gdb-patches
> From: simon.marchi@polymtl.ca
> Cc: Simon Marchi <simon.marchi@polymtl.ca>
> Date: Fri, 13 Feb 2026 00:38:48 -0500
>
> ---
> gdb/NEWS | 4 ++
> gdb/doc/gdb.texinfo | 6 +++
> gdb/elfread.c | 110 +++++++++++++++++++++++++++++++++++---
> gdbsupport/common-debug.h | 9 ++++
> 4 files changed, 123 insertions(+), 6 deletions(-)
Thanks. The documentation parts are okay, but I would suggest to say
in the manual a few words about what are "GNU ifunc" thingies, because
otherwise the documentation is not clear enough to the reader.
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH 1/4] gdb/elfread: add debug output for GNU ifunc resolution
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
0 siblings, 0 replies; 8+ messages in thread
From: Simon Marchi @ 2026-02-13 17:20 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: gdb-patches
On 2026-02-13 03:30, Eli Zaretskii wrote:
>> From: simon.marchi@polymtl.ca
>> Cc: Simon Marchi <simon.marchi@polymtl.ca>
>> Date: Fri, 13 Feb 2026 00:38:48 -0500
>>
>> ---
>> gdb/NEWS | 4 ++
>> gdb/doc/gdb.texinfo | 6 +++
>> gdb/elfread.c | 110 +++++++++++++++++++++++++++++++++++---
>> gdbsupport/common-debug.h | 9 ++++
>> 4 files changed, 123 insertions(+), 6 deletions(-)
>
> Thanks. The documentation parts are okay, but I would suggest to say
> in the manual a few words about what are "GNU ifunc" thingies, because
> otherwise the documentation is not clear enough to the reader.
>
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Would you put it right there where I did the change, or a new section?
If a new section, any suggestions where?
Simon
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/4] gdb/elfread: add debug output for GNU ifunc resolution
2026-02-13 5:38 [PATCH 1/4] gdb/elfread: add debug output for GNU ifunc resolution simon.marchi
` (3 preceding siblings ...)
2026-02-13 8:30 ` [PATCH 1/4] gdb/elfread: add debug output for GNU ifunc resolution Eli Zaretskii
@ 2026-02-13 18:27 ` Kevin Buettner
2026-02-13 19:10 ` Simon Marchi
4 siblings, 1 reply; 8+ messages in thread
From: Kevin Buettner @ 2026-02-13 18:27 UTC (permalink / raw)
To: gdb-patches
On Fri, 13 Feb 2026 00:38:48 -0500
simon.marchi@polymtl.ca wrote:
> 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/NEWS | 4 ++
> gdb/doc/gdb.texinfo | 6 +++
> gdb/elfread.c | 110 +++++++++++++++++++++++++++++++++++---
> gdbsupport/common-debug.h | 9 ++++
> 4 files changed, 123 insertions(+), 6 deletions(-)
This would have been helpful when I was debugging some ifunc related
problems last year.
I've looked over all four parts. They all LGTM.
Approved-by: Kevin Buettner <kevinb@redhat.com>
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH 1/4] gdb/elfread: add debug output for GNU ifunc resolution
2026-02-13 18:27 ` Kevin Buettner
@ 2026-02-13 19:10 ` Simon Marchi
0 siblings, 0 replies; 8+ messages in thread
From: Simon Marchi @ 2026-02-13 19:10 UTC (permalink / raw)
To: Kevin Buettner, gdb-patches
On 2026-02-13 13:27, Kevin Buettner wrote:
> On Fri, 13 Feb 2026 00:38:48 -0500
> simon.marchi@polymtl.ca wrote:
>
>> 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/NEWS | 4 ++
>> gdb/doc/gdb.texinfo | 6 +++
>> gdb/elfread.c | 110 +++++++++++++++++++++++++++++++++++---
>> gdbsupport/common-debug.h | 9 ++++
>> 4 files changed, 123 insertions(+), 6 deletions(-)
>
> This would have been helpful when I was debugging some ifunc related
> problems last year.
Glad to know that, I hope it will be useful in the future.
> I've looked over all four parts. They all LGTM.
>
> Approved-by: Kevin Buettner <kevinb@redhat.com>
Thanks, I pushed the series. I can work on Eli's request to clarify
ifunc in the doc as a separate patch.
Simon
^ 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