[10/11] Extend GNU ifunc testcases

Message ID a299a13e-f5bf-0fe9-d0f5-1911ff42bff8@redhat.com
State New, archived
Headers

Commit Message

Pedro Alves March 9, 2018, 9:20 p.m. UTC
  For convenience, here's the same patch with "git diff -w",
easier to read because the patch moves code at global
scope to functions.
  

Patch

diff --git c/gdb/testsuite/gdb.base/gnu-ifunc-lib.c w/gdb/testsuite/gdb.base/gnu-ifunc-lib.c
index b9d446c92fa..7aac81faec6 100644
--- c/gdb/testsuite/gdb.base/gnu-ifunc-lib.c
+++ w/gdb/testsuite/gdb.base/gnu-ifunc-lib.c
@@ -22,10 +22,14 @@  extern int final (int arg);
 
 typedef int (*final_t) (int arg);
 
+#ifndef IFUNC_RESOLVER_ATTR
 asm (".type gnu_ifunc, %gnu_indirect_function");
-
 final_t
 gnu_ifunc (unsigned long hwcap)
+#else
+final_t
+gnu_ifunc_resolver (unsigned long hwcap)
+#endif
 {
   resolver_hwcap = hwcap;
   if (! gnu_ifunc_initialized)
@@ -33,3 +37,9 @@  gnu_ifunc (unsigned long hwcap)
   else
     return final;
 }
+
+#ifdef IFUNC_RESOLVER_ATTR
+extern int gnu_ifunc (int arg);
+
+__typeof (gnu_ifunc) gnu_ifunc __attribute__ ((ifunc ("gnu_ifunc_resolver")));
+#endif
diff --git c/gdb/testsuite/gdb.base/gnu-ifunc-resd.c w/gdb/testsuite/gdb.base/gnu-ifunc-resd.c
new file mode 100644
index 00000000000..ef19fc98fab
--- /dev/null
+++ w/gdb/testsuite/gdb.base/gnu-ifunc-resd.c
@@ -0,0 +1,22 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2009-2018 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/>.  */
+
+int
+final (int arg)
+{
+  return arg + 1;
+}
diff --git c/gdb/testsuite/gdb.base/gnu-ifunc.c w/gdb/testsuite/gdb.base/gnu-ifunc.c
index 78fd30d9fd2..a4abacaccee 100644
--- c/gdb/testsuite/gdb.base/gnu-ifunc.c
+++ w/gdb/testsuite/gdb.base/gnu-ifunc.c
@@ -23,12 +23,6 @@  init_stub (int arg)
   return 0;
 }
 
-int
-final (int arg)
-{
-  return arg + 1;
-}
-
 /* Make differentiation of how the gnu_ifunc call resolves before and after
    calling gnu_ifunc_pre.  This ensures the resolved function address is not
    being cached anywhere for the debugging purposes.  */
diff --git c/gdb/testsuite/gdb.base/gnu-ifunc.exp w/gdb/testsuite/gdb.base/gnu-ifunc.exp
index 1d0d0401c37..fa1464bec51 100644
--- c/gdb/testsuite/gdb.base/gnu-ifunc.exp
+++ w/gdb/testsuite/gdb.base/gnu-ifunc.exp
@@ -18,47 +18,177 @@  if {[skip_shlib_tests]} {
 }
 
 standard_testfile .c
-set executable ${testfile}
-set staticexecutable ${executable}-static
+set staticexecutable ${testfile}-static
 set staticbinfile [standard_output_file ${staticexecutable}]
 
 set libfile "${testfile}-lib"
 set libsrc ${libfile}.c
-set lib_so [standard_output_file ${libfile}.so]
+
+set resdfile "${testfile}-resd"
+set resdsrc ${resdfile}.c
+
+if [get_compiler_info] {
+    return -1
+}
+
+# Return the binary suffix appended to program and library names to
+# make each testcase variant unique.
+proc make_binsuffix {resolver_attr resolver_debug resolved_debug} {
+    return "$resolver_attr-$resolver_debug-$resolved_debug"
+}
+
+# Compile the testcase.  RESOLVER_ATTR is true if we're testing with
+# an ifunc resolver that has a different name from the user symbol,
+# specified with GCC's __attribute__ ifunc.  RESOLVER_DEBUG is true
+# iff the resolver was compiled with debug info.  RESOLVED_DEBUG is
+# true iff the target function was compiled with debug info.
+proc build {resolver_attr resolver_debug resolved_debug} {
+    global srcdir subdir srcfile binfile
+    global libsrc lib_so libfile
+    global exec_opts executable
+    global hex gdb_prompt
+    global resdfile resdsrc
+
+    set suffix [make_binsuffix $resolver_attr $resolver_debug $resolved_debug]
+
+    set lib_so [standard_output_file ${libfile}-$suffix.so]
     # $lib_o must not have {debug}, it would override the STT_GNU_IFUNC ELF markers.
-set lib_o [standard_output_file ${libfile}.o]
+    set lib_o [standard_output_file ${libfile}-$suffix.o]
 
-# We need DWARF for the "final" function as we "step" into the function and GDB
-# would step-over the "final" function if there would be no line number debug
-# information (DWARF) available.
-#
-# We must not have DWARF for the "gnu_ifunc" function as DWARF has no way to
-# express the STT_GNU_IFUNC type and it would be considered as a regular
-# function due to DWARF by GDB.  In ELF gnu-ifunc is expressed by the
-# STT_GNU_IFUNC type.
-#
-# Both functions need to be in the same shared library file but
-# gdb_compile_shlib has no way to specify source-specific compilation options.
-#
-# Therefore $libfile contains only the STT_GNU_IFUNC function with no DWARF
-# referencing all the other parts from the main executable with DWARF.
+    set exec_opts [list debug shlib=$lib_so]
 
     set lib_opts {}
-set exec_opts [list debug shlib=$lib_so]
+    set resd_opts {}
 
-if [get_compiler_info] {
-    return -1
+    if {$resolver_attr} {
+	lappend lib_opts "additional_flags=-DIFUNC_RESOLVER_ATTR"
     }
 
-if { [gdb_compile_shlib ${srcdir}/${subdir}/$libsrc $lib_so $lib_opts] != ""
-     || [gdb_compile ${srcdir}/${subdir}/$srcfile $binfile executable $exec_opts] != ""} {
-    untested "failed to compile first testcase"
-    return -1
+    if {$resolver_debug} {
+	lappend lib_opts "debug"
+    }
+
+    if {$resolved_debug} {
+	lappend resd_opts "debug"
+    }
+
+    set resd_o $resdfile-$suffix.o
+
+    if { [gdb_compile_shlib ${srcdir}/${subdir}/$libsrc \
+	      $lib_so $lib_opts] != ""
+	 || [gdb_compile ${srcdir}/${subdir}/$resdsrc \
+		 $resd_o object $resd_opts] != ""
+	 || [gdb_compile [list ${srcdir}/${subdir}/$srcfile $resd_o] \
+		 $binfile-$suffix executable $exec_opts] != ""} {
+	untested "failed to compile testcase"
+	return 0
+    }
+
+    return 1
+}
+
+# Test setting a breakpoint on a ifunc function before and after the
+# ifunc is resolved.  RESOLVER_ATTR is true if we're testing with a
+# resolver specified with __attribute__ ifunc.  RESOLVER_DEBUG is true
+# iff the resolver was compiled with debug info.  RESOLVED_DEBUG is
+# true iff the target function was compiled with debug info.
+proc_with_prefix set-break {resolver_attr resolver_debug resolved_debug} {
+    global binfile libfile lib_so
+    global hex decimal
+    global gdb_prompt
+
+    set suffix [make_binsuffix $resolver_attr $resolver_debug $resolved_debug]
+
+    set lib_so [standard_output_file ${libfile}-$suffix.so]
+    clean_restart $binfile-$suffix
+    gdb_load_shlib ${lib_so}
+
+    if ![runto_main] then {
+	fail "can't run to main"
+	return 1
+    }
+
+    set ws "\[ \t\]+"
+
+    if {$resolver_attr} {
+	set gnu_ifunc_resolver "gnu_ifunc_resolver"
+    } else {
+	set gnu_ifunc_resolver "gnu_ifunc"
+    }
+
+    with_test_prefix "before resolving" {
+	delete_breakpoints
+	gdb_test "break gnu_ifunc" \
+	    "Breakpoint $decimal at gnu-indirect-function resolver at $hex"
+	gdb_test "info breakpoints" \
+	    "$decimal${ws}STT_GNU_IFUNC resolver${ws}keep${ws}y${ws}$hex <${gnu_ifunc_resolver}>"
+    }
+
+    global resdsrc
+
+    with_test_prefix "resolve" {
+	delete_breakpoints
+	gdb_breakpoint [gdb_get_line_number "break-at-exit"]
+	gdb_continue_to_breakpoint "break-at-exit" ".*break-at-exit.*"
+    }
+
+    with_test_prefix "after resolving" {
+	delete_breakpoints
+
+	if {!$resolved_debug} {
+	    # Set a breakpoint both at the ifunc, and at the ifunc's
+	    # target.  GDB should resolve both to the same address.
+	    # Start with the ifunc's target.
+	    set addr "-"
+	    set test "break final"
+	    # Extract the address without the leading "0x", because
+	    # addresses in "info break" output include leading 0s
+	    # (like "0x0000ADDR").
+	    set hex_number {[0-9a-fA-F][0-9a-fA-F]*}
+	    gdb_test_multiple $test $test {
+		-re "Breakpoint .* at 0x($hex_number)\r\n$gdb_prompt $" {
+		    set addr $expect_out(1,string)
+		    pass $test
+		}
+	    }
+
+	    # Now set a break at the ifunc.
+	    gdb_test "break gnu_ifunc" "Breakpoint .* at 0x$addr"
+	    set location "$decimal${ws}breakpoint${ws}keep${ws}y${ws}0x0*$addr${ws}<final\\+.*>"
+	} else {
+	    set lineno -1
+	    set test "break final"
+	    gdb_test_multiple $test $test {
+		-re "Breakpoint .* at $hex: file .*$resdsrc, line ($decimal)\\.\r\n$gdb_prompt $" {
+		    set lineno $expect_out(1,string)
+		    pass $test
+		}
+	    }
+	    gdb_test "break gnu_ifunc" "Breakpoint .* at $hex: file .*$resdsrc, line $lineno\\."
+	    set location "$decimal${ws}breakpoint${ws}keep${ws}y${ws}$hex in final at .*$resdsrc:$lineno"
+	}
+	gdb_test "info breakpoints" "$location\r\n$location"
+    }
+}
+
+proc misc_tests {resolver_attr resolver_debug resolved_debug} {
+    global srcdir subdir srcfile binfile
+    global libsrc lib_so libfile
+    global exec_opts executable
+    global hex gdb_prompt
+    global resdfile resdsrc
+
+    set suffix [make_binsuffix $resolver_attr $resolver_debug $resolved_debug]
+
+    if {$resolver_attr} {
+	set gnu_ifunc_resolver "gnu_ifunc_resolver"
+    } else {
+	set gnu_ifunc_resolver "gnu_ifunc"
     }
 
     # Start with a fresh gdb.
 
-clean_restart $executable
+    clean_restart $binfile-$suffix
     gdb_load_shlib ${lib_so}
 
     if ![runto_main] then {
@@ -67,14 +197,23 @@  if ![runto_main] then {
     }
 
     # The "if" condition is artifical to test regression of a former patch.
-gdb_breakpoint "[gdb_get_line_number "break-at-nextcall"] if i && gnu_ifunc (i) != 42"
+    gdb_breakpoint "[gdb_get_line_number "break-at-nextcall"] if i && (int) gnu_ifunc (i) != 42"
 
     gdb_breakpoint [gdb_get_line_number "break-at-call"]
     gdb_continue_to_breakpoint "break-at-call" ".*break-at-call.*"
 
     # Test GDB will automatically indirect the call.
 
+    if {!$resolver_debug && !$resolved_debug} {
+	gdb_test "p gnu_ifunc()" \
+	    "'final' has unknown return type; cast the call to its declared return type"
+	gdb_test "p gnu_ifunc (3)" \
+	    "'final' has unknown return type; cast the call to its declared return type"
+	gdb_test "p (int) gnu_ifunc (3)" " = 4"
+    } else {
+	gdb_test "p gnu_ifunc()" "Too few arguments in function call\\."
 	gdb_test "p gnu_ifunc (3)" " = 4"
+    }
 
     # Test that the resolver received its argument.
 
@@ -93,7 +232,16 @@  gdb_test "p/x resolver_hwcap" "= $actual_hwcap" "resolver received HWCAP"
 
     # Test GDB will skip the gnu_ifunc resolver on first call.
 
+    # Even if the resolver has debug info, stepping into an ifunc call
+    # should skip the resolver.
+    if {!$resolved_debug} {
+	# Make GDB stop stepping even if it steps into a function with
+	# no debug info.
+	gdb_test_no_output "set step-mode on"
+	gdb_test "step" "$hex in final \\\(\\\)"
+    } else {
 	gdb_test "step" "\r\nfinal .*"
+    }
 
     # Test GDB will not break before the final chosen implementation.
 
@@ -104,14 +252,16 @@  gdb_test "step" "\r\nfinal .*"
     #
     # Breakpoint 2, main () at ./gdb.base/gnu-ifunc.c:33
 
-gdb_test "continue" "Continuing.\r\n\r\nBreakpoint .* (at|in) .*break-at-nextcall.*" \
+    gdb_test "continue" \
+	"Continuing.\r\n\r\nBreakpoint .* (at|in) .*break-at-nextcall.*" \
 	"continue to break-at-nextcall"
 
     gdb_breakpoint "gnu_ifunc"
 
     gdb_continue_to_breakpoint "nextcall gnu_ifunc"
 
-gdb_test "frame" "#0 +(0x\[0-9a-f\]+ in +)?final \\(.*" "nextcall gnu_ifunc skipped"
+    gdb_test "frame" \
+	"#0 +(0x\[0-9a-f\]+ in +)?final \\(.*" "nextcall gnu_ifunc skipped"
 
 
     # Check any commands not doing an inferior call access the address of the
@@ -125,25 +275,82 @@  if {[istarget powerpc64-*] && [is_lp64_target]} {
 	set func_prefix {}
     }
 
-gdb_test "p gnu_ifunc" " = {<text gnu-indirect-function variable, no debug info>} 0x\[0-9a-f\]+ <${func_prefix}gnu_ifunc>" "p gnu_ifunc executing"
-gdb_test "info sym gnu_ifunc" "gnu_ifunc in section .*" "info sym gnu_ifunc executing"
+    gdb_test "p gnu_ifunc" \
+	" = {<text gnu-indirect-function variable, no debug info>} 0x\[0-9a-f\]+ <${func_prefix}${gnu_ifunc_resolver}>" \
+	"p gnu_ifunc executing"
+    gdb_test "info sym gnu_ifunc" \
+	"${gnu_ifunc_resolver} in section .*" \
+	"info sym gnu_ifunc executing"
 
     set test "info addr gnu_ifunc"
+    if {!$resolver_attr && $resolver_debug} {
+	gdb_test_multiple $test $test {
+	    -re "Symbol \"gnu_ifunc\" is a function at address (0x\[0-9a-f\]+).*$gdb_prompt $" {
+		pass $test
+	    }
+	}
+    } else {
 	gdb_test_multiple $test $test {
 	    -re "Symbol \"gnu_ifunc\" is at (0x\[0-9a-f\]+) in .*$gdb_prompt $" {
 		pass $test
 	    }
 	}
-gdb_test "info sym $expect_out(1,string)" "gnu_ifunc in section .*" "info sym <gnu_ifunc-address>"
+    }
+    gdb_test "info sym $expect_out(1,string)" \
+	"${gnu_ifunc_resolver} in section .*" \
+	"info sym <gnu_ifunc-address>"
+
+    # Test calling the resolver directly instead of the ifunc symbol.
+    # Can only do that if the ifunc and the ifunc resolver have
+    # different names.
+    if {$resolver_attr} {
+	if {$resolver_debug} {
+	    gdb_test "p gnu_ifunc_resolver(0)" \
+		" = \\(int \\(\\*\\)\\(int\\)\\) $hex <final>"
+	} else {
+	    gdb_test "p gnu_ifunc_resolver(0)" \
+		"'gnu_ifunc_resolver' has unknown return type; cast the call to its declared return type"
+	    gdb_test "p (void *) gnu_ifunc_resolver(0)" \
+		" = \\(void \\*\\) $hex <final>"
+	}
+    }
+}
 
+# Test all the combinations of:
+#
+# - An ifunc resolver with the same name as the ifunc symbol vs an
+#   ifunc resolver with a different name as the ifunc symbol.
+#
+# - ifunc resolver compiled with and without debug info.  This ensures
+#   that GDB understands that a function not a regular function by
+#   looking at the STT_GNU_IFUNC type in the elf symbols.  DWARF has
+#   no way to express the STT_GNU_IFUNC type.
+#
+# - ifunc target function (resolved) compiled with and without debug
+#   info.
+foreach_with_prefix resolver_attr {0 1} {
+    foreach_with_prefix resolver_debug {0 1} {
+	foreach_with_prefix resolved_debug {0 1} {
+	    build $resolver_attr $resolver_debug $resolved_debug
+	    misc_tests $resolver_attr $resolver_debug $resolved_debug
+	    set-break $resolver_attr $resolver_debug $resolved_debug
+	}
+    }
+}
 
 # Test statically linked ifunc resolving during inferior start.
 # https://bugzilla.redhat.com/show_bug.cgi?id=624967
 
-# Compile $staticbinfile separately as it may exit on error (ld/12595).
+with_test_prefix "static" {
+    # Compile $staticbinfile separately as it may exit on error
+    # (ld/12595).
 
+    set lib_o [standard_output_file ${libfile}.o]
+    set resd_o [standard_output_file ${resdfile}.o]
     if { [gdb_compile ${srcdir}/${subdir}/$libsrc $lib_o object {}] != ""
-     || [gdb_compile "${srcdir}/${subdir}/$srcfile $lib_o" $staticbinfile executable {debug}] != "" } {
+	 || [gdb_compile ${srcdir}/${subdir}/$resdsrc $resd_o object {}] != ""
+	 || [gdb_compile "${srcdir}/${subdir}/$srcfile $lib_o $resd_o" \
+		 $staticbinfile executable {debug}] != "" } {
 	untested "failed to compile second testcase"
 	return -1
     }
@@ -154,3 +361,4 @@  gdb_breakpoint "gnu_ifunc"
     gdb_breakpoint "main"
     gdb_run_cmd
     gdb_test "" "Breakpoint \[0-9\]*, main .*" "static gnu_ifunc"
+}