[binutils-gdb/gdb-18-branch] gdb: Preserve IFUNC marker when finding inferior functions

Andrew Burgess 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=8f39b11736558b6d63b8950e91bd9dbdf08490b1

commit 8f39b11736558b6d63b8950e91bd9dbdf08490b1
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;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.