WIP MachO binutils port assisted by AI I may consider upstreaming this in the future.
0

Configure Feed

Select the types of activity you want to include in your feed.

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 <eliz@gnu.org>
Approved-by: Kevin Buettner <kevinb@redhat.com>

Simon Marchi (Feb 13, 2026, 2:03 PM EST) e5ebb42e fadfc038

+125 -7
+4
gdb/NEWS
··· 85 85 Test splitting and joining of inferior arguments ARGS as they would 86 86 be split and joined when being passed to a remote target. 87 87 88 + set debug gnu-ifunc on|off 89 + show debug gnu-ifunc 90 + Turn on or off debug messages related to GNU ifunc resolution. 91 + 88 92 set progress-bars enabled on|off 89 93 show progress-bars enabled 90 94 Allows the progress bars, used when debuginfod is downloading
+106 -7
gdb/elfread.c
··· 47 47 /* Whether ctf should always be read, or only if no dwarf is present. */ 48 48 static bool always_read_ctf; 49 49 50 + /* Value of the 'set debug gnu-ifunc' configuration variable. */ 51 + static bool debug_gnu_ifunc; 52 + 53 + static void 54 + show_debug_gnu_ifunc (struct ui_file *file, int from_tty, 55 + struct cmd_list_element *c, const char *value) 56 + { 57 + gdb_printf (file, _("gnu-ifunc debugging is %s.\n"), value); 58 + } 59 + 60 + #define gnu_ifunc_debug_printf(fmt, ...) \ 61 + debug_prefixed_printf_cond (debug_gnu_ifunc, "gnu-ifunc", fmt, ##__VA_ARGS__) 62 + 63 + #define gnu_ifunc_debug_printf_func(func, fmt, ...) \ 64 + debug_prefixed_printf_cond_func (debug_gnu_ifunc, "gnu-ifunc", func, fmt, \ 65 + ##__VA_ARGS__) 66 + 50 67 /* The struct elfinfo is available only during ELF symbol table and 51 68 psymtab reading. It is destroyed at the completion of psymtab-reading. 52 69 It's local to elf_symfile_read. */ ··· 695 712 struct elf_gnu_ifunc_cache entry_local, *entry_p; 696 713 void **slot; 697 714 715 + gnu_ifunc_debug_printf ("recording cache entry for \"%s\" at %s", name, 716 + paddress (current_inferior ()->arch (), addr)); 717 + 698 718 bound_minimal_symbol msym = lookup_minimal_symbol_by_pc (addr); 699 719 if (msym.minsym == NULL) 700 - return 0; 720 + { 721 + gnu_ifunc_debug_printf ("no minimal symbol found at %s, not caching", 722 + paddress (current_inferior ()->arch (), addr)); 723 + return 0; 724 + } 725 + 701 726 if (msym.value_address () != addr) 702 - return 0; 727 + { 728 + gnu_ifunc_debug_printf ("minimal symbol \"%s\" at %s does not match " 729 + "addr %s, not caching", 730 + msym.minsym->linkage_name (), 731 + paddress (current_inferior ()->arch (), 732 + msym.value_address ()), 733 + paddress (current_inferior ()->arch (), addr)); 734 + return 0; 735 + } 736 + 703 737 objfile = msym.objfile; 704 738 705 739 /* If .plt jumps back to .plt the symbol is still deferred for later ··· 711 745 symbol is in the .plt section because some systems have @plt 712 746 symbols in the .text section. */ 713 747 if (len > 4 && strcmp (target_name + len - 4, "@plt") == 0) 714 - return 0; 748 + { 749 + gnu_ifunc_debug_printf ("target \"%s\" is a PLT stub, not caching", 750 + target_name); 751 + return 0; 752 + } 715 753 716 754 if (strcmp (target_name, "_PROCEDURE_LINKAGE_TABLE_") == 0) 717 - return 0; 755 + { 756 + gnu_ifunc_debug_printf ("target is _PROCEDURE_LINKAGE_TABLE_, " 757 + "not caching"); 758 + return 0; 759 + } 718 760 719 761 htab = elf_objfile_gnu_ifunc_cache_data.get (objfile); 720 762 if (htab == NULL) ··· 754 796 } 755 797 *slot = entry_p; 756 798 799 + gnu_ifunc_debug_printf ("cached \"%s\" -> %s in objfile %s", name, 800 + paddress (objfile->arch (), addr), 801 + objfile_name (objfile)); 757 802 return 1; 758 803 } 759 804 ··· 767 812 static int 768 813 elf_gnu_ifunc_resolve_by_cache (const char *name, CORE_ADDR *addr_p) 769 814 { 815 + gnu_ifunc_debug_printf ("resolving \"%s\" by cache", name); 770 816 int found = 0; 817 + const char *func = __func__; 771 818 772 819 /* FIXME: we only search the initial namespace. 773 820 774 821 To search other namespaces, we would need to provide context, e.g. in 775 822 form of an objfile in that namespace. */ 776 823 current_program_space->iterate_over_objfiles_in_search_order 777 - ([name, &addr_p, &found] (struct objfile *objfile) 824 + ([name, &addr_p, &found, func] (struct objfile *objfile) 778 825 { 779 826 htab_t htab; 780 827 elf_gnu_ifunc_cache *entry_p; ··· 797 844 if (addr_p) 798 845 *addr_p = entry_p->addr; 799 846 847 + gnu_ifunc_debug_printf_func 848 + (func, "cache hit for \"%s\" -> %s in objfile %s", 849 + name, paddress (objfile->arch (), entry_p->addr), 850 + objfile_name (objfile)); 800 851 found = 1; 801 852 return 1; 802 853 }, nullptr); 854 + 855 + if (!found) 856 + gnu_ifunc_debug_printf ("cache miss for \"%s\"", name); 803 857 804 858 return found; 805 859 } ··· 815 869 static int 816 870 elf_gnu_ifunc_resolve_by_got (const char *name, CORE_ADDR *addr_p) 817 871 { 872 + gnu_ifunc_debug_printf ("resolving \"%s\" by GOT", name); 818 873 char *name_got_plt; 819 874 const size_t got_suffix_len = strlen (SYMBOL_GOT_PLT_SUFFIX); 820 875 int found = 0; 876 + const char *func = __func__; 821 877 822 878 name_got_plt = (char *) alloca (strlen (name) + got_suffix_len + 1); 823 879 sprintf (name_got_plt, "%s" SYMBOL_GOT_PLT_SUFFIX, name); ··· 827 883 To search other namespaces, we would need to provide context, e.g. in 828 884 form of an objfile in that namespace. */ 829 885 current_program_space->iterate_over_objfiles_in_search_order 830 - ([name, name_got_plt, &addr_p, &found] (struct objfile *objfile) 886 + ([name, name_got_plt, &addr_p, &found, func] (struct objfile *objfile) 831 887 { 832 888 bfd *obfd = objfile->obfd.get (); 833 889 struct gdbarch *gdbarch = objfile->arch (); ··· 859 915 (gdbarch, addr, current_inferior ()->top_target ()); 860 916 addr = gdbarch_addr_bits_remove (gdbarch, addr); 861 917 918 + gnu_ifunc_debug_printf_func (func, "GOT entry \"%s\" points to %s", 919 + name_got_plt, paddress (gdbarch, addr)); 920 + 862 921 if (elf_gnu_ifunc_record_cache (name, addr)) 863 922 { 864 923 if (addr_p != NULL) 865 924 *addr_p = addr; 866 925 926 + gnu_ifunc_debug_printf_func (func, 927 + "resolved \"%s\" via GOT to %s", 928 + name, paddress (gdbarch, addr)); 867 929 found = 1; 868 930 return 1; 869 931 } 870 932 871 933 return 0; 872 934 }, nullptr); 935 + 936 + if (!found) 937 + gnu_ifunc_debug_printf ("failed to resolve \"%s\" by GOT", name); 873 938 874 939 return found; 875 940 } ··· 884 949 static bool 885 950 elf_gnu_ifunc_resolve_name (const char *name, CORE_ADDR *addr_p) 886 951 { 952 + gnu_ifunc_debug_printf ("resolving name \"%s\"", name); 953 + 887 954 if (elf_gnu_ifunc_resolve_by_cache (name, addr_p)) 888 955 return true; 889 956 890 957 if (elf_gnu_ifunc_resolve_by_got (name, addr_p)) 891 958 return true; 892 959 960 + gnu_ifunc_debug_printf ("failed to resolve name \"%s\"", name); 893 961 return false; 894 962 } 895 963 896 964 /* Call STT_GNU_IFUNC - a function returning address of a real function to 897 - call. PC is theSTT_GNU_IFUNC resolving function entry. The value returned 965 + call. PC is the STT_GNU_IFUNC resolving function entry. The value returned 898 966 is the entry point of the resolved STT_GNU_IFUNC target function to call. 899 967 */ 900 968 ··· 908 976 CORE_ADDR hwcap = 0; 909 977 struct value *hwcap_val; 910 978 979 + gnu_ifunc_debug_printf ("resolving ifunc %s", paddress (gdbarch, pc)); 980 + 911 981 /* Try first any non-intrusive methods without an inferior call. */ 912 982 913 983 if (find_pc_partial_function (pc, &name_at_pc, &start_at_pc, NULL) ··· 918 988 } 919 989 else 920 990 name_at_pc = NULL; 991 + 992 + gnu_ifunc_debug_printf ("resolving via inferior call to resolver at %s", 993 + paddress (gdbarch, pc)); 921 994 922 995 function = value::allocate (func_func_type); 923 996 function->set_lval (lval_memory); ··· 936 1009 (gdbarch, address, current_inferior ()->top_target ()); 937 1010 address = gdbarch_addr_bits_remove (gdbarch, address); 938 1011 1012 + gnu_ifunc_debug_printf ("resolver at %s returned %s", paddress (gdbarch, pc), 1013 + paddress (gdbarch, address)); 1014 + 939 1015 if (name_at_pc) 940 1016 elf_gnu_ifunc_record_cache (name_at_pc, address); 941 1017 ··· 952 1028 struct frame_id prev_frame_id = get_stack_frame_id (prev_frame); 953 1029 CORE_ADDR prev_pc = get_frame_pc (prev_frame); 954 1030 int thread_id = inferior_thread ()->global_num; 1031 + 1032 + gnu_ifunc_debug_printf ("stop on resolver for \"%s\"", 1033 + b->locspec->to_string ()); 955 1034 956 1035 gdb_assert (b->type == bp_gnu_ifunc_resolver); 957 1036 ··· 983 1062 prev_frame_id, 984 1063 bp_gnu_ifunc_resolver_return).release (); 985 1064 1065 + gnu_ifunc_debug_printf ("created resolver return breakpoint at %s", 1066 + paddress (get_frame_arch (prev_frame), prev_pc)); 986 1067 987 1068 /* Add new b_return to the ring list b->related_breakpoint. */ 988 1069 gdb_assert (b_return->related_breakpoint == b_return); 989 1070 b_return->related_breakpoint = b->related_breakpoint; 990 1071 b->related_breakpoint = b_return; 991 1072 } 1073 + else 1074 + gnu_ifunc_debug_printf ("found existing resolver return breakpoint at %s", 1075 + paddress (get_frame_arch (prev_frame), prev_pc)); 992 1076 } 993 1077 994 1078 /* Handle inferior hit of bp_gnu_ifunc_resolver_return, see its definition. */ ··· 1004 1088 struct value *func_func; 1005 1089 struct value *value; 1006 1090 CORE_ADDR resolved_address, resolved_pc; 1091 + 1092 + gnu_ifunc_debug_printf ("stop on resolver return"); 1007 1093 1008 1094 gdb_assert (b->type == bp_gnu_ifunc_resolver_return); 1009 1095 ··· 1039 1125 resolved_pc = gdbarch_convert_from_func_ptr_addr 1040 1126 (gdbarch, resolved_address, current_inferior ()->top_target ()); 1041 1127 resolved_pc = gdbarch_addr_bits_remove (gdbarch, resolved_pc); 1128 + 1129 + gnu_ifunc_debug_printf ("resolver for \"%s\" returned resolved address=%s, " 1130 + "resolved pc=%s", 1131 + b->locspec->to_string (), 1132 + paddress (gdbarch, resolved_address), 1133 + paddress (gdbarch, resolved_pc)); 1042 1134 1043 1135 gdb_assert (current_program_space == b->pspace || b->pspace == NULL); 1044 1136 elf_gnu_ifunc_record_cache (b->locspec->to_string (), resolved_pc); ··· 1342 1434 add_symtab_fns (bfd_target_elf_flavour, &elf_sym_fns); 1343 1435 1344 1436 gnu_ifunc_fns_p = &elf_gnu_ifunc_fns; 1437 + 1438 + add_setshow_boolean_cmd 1439 + ("gnu-ifunc", class_maintenance, &debug_gnu_ifunc, 1440 + _("Set GNU ifunc debugging."), 1441 + _("Show GNU ifunc debugging."), 1442 + _("When on, debug output for GNU ifunc resolution is displayed."), 1443 + nullptr, show_debug_gnu_ifunc, &setdebuglist, &showdebuglist); 1345 1444 1346 1445 /* Add "set always-read-ctf on/off". */ 1347 1446 add_setshow_boolean_cmd ("always-read-ctf", class_support, &always_read_ctf,
+9
gdbsupport/common-debug.h
··· 71 71 } \ 72 72 while (0) 73 73 74 + #define debug_prefixed_printf_cond_func(debug_enabled_cond, mod, func, fmt, \ 75 + ...) \ 76 + do \ 77 + { \ 78 + if (debug_enabled_cond) \ 79 + debug_prefixed_printf (mod, func, fmt, ##__VA_ARGS__); \ 80 + } \ 81 + while (0) 82 + 74 83 #define debug_prefixed_printf_cond_nofunc(debug_enabled_cond, mod, fmt, ...) \ 75 84 do \ 76 85 { \
+6
gdb/doc/gdb.texinfo
··· 29331 29331 Displays the current state of displaying @value{GDBN} frame debugging 29332 29332 info. 29333 29333 29334 + @cindex GNU ifunc debug messages 29335 + @item set debug gnu-ifunc 29336 + Turn on or off debugging messages related to GNU ifunc resolution. 29337 + @item show debug gnu-ifunc 29338 + Show the current state of GNU ifunc resolution debugging messages. 29339 + 29334 29340 @item set debug gnu-nat 29335 29341 @cindex @sc{gnu}/Hurd debug messages 29336 29342 Turn on or off debugging messages from the @sc{gnu}/Hurd debug support.