[10/11] Extend GNU ifunc testcases
Commit Message
For convenience, here's the same patch with "git diff -w",
easier to read because the patch moves code at global
scope to functions.
@@ -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
new file mode 100644
@@ -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;
+}
@@ -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. */
@@ -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"
+}