[binutils-gdb] gdb: Preserve IFUNC marker when finding inferior functions
Yury Khrustalev via Gdb-cvs <[email protected]>
| Newsgroups | gmane.comp.gdb.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=de930032d883219559d1dba575f2c0f5359e80fc commit de930032d883219559d1dba575f2c0f5359e80fc Author: Muhammad Kamran <[email protected]> Date: Tue Aug 11 13:12:18 2026 +0000 gdb: Preserve IFUNC marker when finding inferior functions GDB calls find_function_in_inferior ("malloc") when expression evaluation needs to allocate memory in the inferior, e.g. for string literal arguments. The minimal-symbol fallback created a synthetic ordinary function pointer from msymbol.value_address (). If the symbol was a GNU IFUNC, this discarded the IFUNC marker, so call_function_by_hand did not resolve the symbol before calling it. Check the minimal symbol kind directly and propagate the GNU IFUNC marker to the synthetic function type for mst_text_gnu_ifunc and mst_data_gnu_ifunc symbols. This keeps the existing fallback address and return type while allowing inferior calls through IFUNC symbols to be resolved correctly. Extend gdb.base/gnu-ifunc.exp with an internal inferior-call test that uses an IFUNC malloc. The test runs through the existing IFUNC matrix for resolver attr, resolver debug info, and resolved-target debug info. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=34330 Reviewed-By: Kevin Buettner <[email protected]> Approved-By: Andrew Burgess <[email protected]> Diff: --- .../gdb.base/gnu-ifunc-inferior-call-final.c | 27 ++++++ .../gdb.base/gnu-ifunc-inferior-call-resolver.c | 43 +++++++++ gdb/testsuite/gdb.base/gnu-ifunc-inferior-call.c | 37 ++++++++ gdb/testsuite/gdb.base/gnu-ifunc.exp | 105 +++++++++++++++++++++ gdb/valops.c | 5 + 5 files changed, 217 insertions(+) diff --git a/gdb/testsuite/gdb.base/gnu-ifunc-inferior-call-final.c b/gdb/testsuite/gdb.base/gnu-ifunc-inferior-call-final.c new file mode 100644 index 00000000000..1a9ae6fdcba --- /dev/null +++ b/gdb/testsuite/gdb.base/gnu-ifunc-inferior-call-final.c @@ -0,0 +1,27 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2026 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <stddef.h> + +extern char arena[32]; + +void * +dummy_malloc (size_t size) +{ + (void) size; + return arena; +} diff --git a/gdb/testsuite/gdb.base/gnu-ifunc-inferior-call-resolver.c b/gdb/testsuite/gdb.base/gnu-ifunc-inferior-call-resolver.c new file mode 100644 index 00000000000..217002968de --- /dev/null +++ b/gdb/testsuite/gdb.base/gnu-ifunc-inferior-call-resolver.c @@ -0,0 +1,43 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2026 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <stddef.h> + +extern void *dummy_malloc (size_t size); + +typedef void *(*malloc_t) (size_t size); + +#ifndef IFUNC_RESOLVER_ATTR +asm (".type malloc, %gnu_indirect_function"); +malloc_t +malloc (unsigned long hwcap) +#else +static malloc_t +resolve_malloc (void) +#endif +{ +#ifndef IFUNC_RESOLVER_ATTR + (void) hwcap; +#endif + return dummy_malloc; +} + +#ifdef IFUNC_RESOLVER_ATTR +extern void *malloc (size_t size); + +__typeof (malloc) malloc __attribute__ ((ifunc ("resolve_malloc"))); +#endif diff --git a/gdb/testsuite/gdb.base/gnu-ifunc-inferior-call.c b/gdb/testsuite/gdb.base/gnu-ifunc-inferior-call.c new file mode 100644 index 00000000000..67d195d25f0 --- /dev/null +++ b/gdb/testsuite/gdb.base/gnu-ifunc-inferior-call.c @@ -0,0 +1,37 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2026 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +char arena[32]; +const char *str; + +int +str_in_arena (void) +{ + return str == arena; +} + +void +get_string (const char *s) +{ + str = s; +} + +int +main (void) +{ + return 0; +} diff --git a/gdb/testsuite/gdb.base/gnu-ifunc.exp b/gdb/testsuite/gdb.base/gnu-ifunc.exp index e6389102ae3..d146a197776 100644 --- a/gdb/testsuite/gdb.base/gnu-ifunc.exp +++ b/gdb/testsuite/gdb.base/gnu-ifunc.exp @@ -25,6 +25,13 @@ set libsrc ${libfile}.c set final_file "${testfile}-final" set final_src ${final_file}.c +set infcall_file "${testfile}-inferior-call" +set infcall_src ${infcall_file}.c +set infcall_resolver_file "${testfile}-inferior-call-resolver" +set infcall_resolver_src ${infcall_resolver_file}.c +set infcall_final_file "${testfile}-inferior-call-final" +set infcall_final_src ${infcall_final_file}.c + # Return the binary suffix appended to program and library names to # make each testcase variant unique. proc make_binsuffix {resolver_attr resolver_debug final_debug} { @@ -356,6 +363,103 @@ proc misc_tests {resolver_attr resolver_debug final_debug} { } } +# Test that GDB resolves a GNU IFUNC minimal symbol when it uses +# find_function_in_inferior to make an internal inferior call. String +# literals are copied into the inferior with a call to malloc, so an +# IFUNC malloc exercises this path. + +proc_with_prefix test_inferior_call {resolver_attr resolver_debug final_debug} { + global srcdir subdir + global infcall_file infcall_src + global infcall_resolver_file infcall_resolver_src + global infcall_final_file infcall_final_src + + set suffix [make_binsuffix $resolver_attr $resolver_debug $final_debug] + set executable ${infcall_file}-$suffix + set binfile [standard_output_file $executable] + set infcall_lib_so [standard_output_file ${infcall_file}-$suffix.so] + set resolver_obj [standard_output_file ${infcall_resolver_file}-$suffix.o] + set final_obj [standard_output_file ${infcall_final_file}-$suffix.o] + + set resolver_opts {additional_flags=-fno-builtin-malloc additional_flags=-fpic} + set final_opts {additional_flags=-fpic} + set shlib_opts {ldflags=-Wl,-z,lazy} + set exec_opts [list debug shlib=$infcall_lib_so] + lappend exec_opts "ldflags=-Wl,-z,lazy" + + if {$resolver_attr} { + lappend resolver_opts "additional_flags=-DIFUNC_RESOLVER_ATTR" + } + + if {$resolver_debug} { + lappend resolver_opts "debug" + } + + if {$final_debug} { + lappend final_opts "debug" + } + + if { [gdb_compile ${srcdir}/${subdir}/${infcall_resolver_src} \ + $resolver_obj object $resolver_opts] != "" + || [gdb_compile ${srcdir}/${subdir}/${infcall_final_src} \ + $final_obj object $final_opts] != "" + || [gdb_compile_shlib [list $resolver_obj $final_obj] \ + $infcall_lib_so $shlib_opts] != "" + || [gdb_compile ${srcdir}/${subdir}/${infcall_src} \ + $binfile executable $exec_opts] != "" } { + untested "failed to compile inferior call testcase" + return + } + + clean_restart $executable + gdb_load_shlib $infcall_lib_so + if {![runto_main]} { + return + } + + set malloc_addr {} + gdb_test_multiple "pipe maint print msymbols | grep \" malloc \"" \ + "look for malloc msyms" { + -re "($::hex) malloc section \[^\r\n\]+\r\n" { + lappend malloc_addr $expect_out(1,string) + exp_continue + } + + -re "$::gdb_prompt $" { + gdb_assert {[llength $malloc_addr] > 0} \ + "found at least one malloc symbol" + } + } + + set found_correct_malloc false + set first_addr [lindex $malloc_addr 0] + set infcall_lib_tail [file tail $infcall_lib_so] + gdb_test_multiple "info symbol $first_addr" "check first malloc symbol" { + -re -wrap " in section \[^\r\n\]+ of \[^\r\n\]+/$infcall_lib_tail" { + set found_correct_malloc true + } + + -re -wrap " in section \[^\r\n\]+" { + # Nothing to do. + } + } + if { !$found_correct_malloc } { + unsupported "found some other malloc symbol" + return + } + + # Without debug info for both the resolver and the resolved target, + # find_function_addr currently loses the synthetic return type after + # resolving the IFUNC. + if {!$resolver_debug && !$final_debug} { + unsupported "internal call resolves IFUNC malloc" + return + } + gdb_test "print (get_string (\"hello-ifunc\"), str_in_arena ())" \ + " = 1" \ + "internal call resolves IFUNC malloc" +} + # Test all the combinations of: # # - An ifunc resolver with the same name as the ifunc symbol vs an @@ -374,6 +478,7 @@ foreach_with_prefix resolver_attr {0 1} { if { [build $resolver_attr $resolver_debug $final_debug] != 0 } { misc_tests $resolver_attr $resolver_debug $final_debug set-break $resolver_attr $resolver_debug $final_debug + test_inferior_call $resolver_attr $resolver_debug $final_debug } } } diff --git a/gdb/valops.c b/gdb/valops.c index d2dc3d9e869..c478bdc3f15 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -138,6 +138,11 @@ find_function_in_inferior (const char *name, struct objfile **objf_p) type = lookup_function_type (type); type = lookup_pointer_type (type); maddr = msymbol.value_address (); + minimal_symbol_type minsym_type = msymbol.minsym->type (); + + if (minsym_type == mst_text_gnu_ifunc + || minsym_type == mst_data_gnu_ifunc) + type->target_type ()->set_is_gnu_ifunc (true); if (objf_p) *objf_p = objfile;