diff options
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/testsuite/ChangeLog | 13 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/gnu-ifunc-final.c | 22 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/gnu-ifunc-lib.c | 12 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/gnu-ifunc.c | 6 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/gnu-ifunc.exp | 408 |
5 files changed, 366 insertions, 95 deletions
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 7f7a92f..29ae9b2 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,5 +1,18 @@ 2018-04-26 Pedro Alves <palves@redhat.com> + * gdb.base/gnu-ifunc-final.c: New file. + * gdb.base/gnu-ifunc.c (final): Delete, moved to gnu-ifunc-final.c. + * gdb.base/gnu-ifunc.exp (executable): Delete. + (staticexecutable): Adjust. + (lib_opts, exec_opts): Delete. + (make_binsuffix, build, set-break): New procedures. + (misc_tests): New, with tests factored out from the top level. + (top level): Test different combinations of ifunc resolver name, + resolver with and with debug info, and ifunc target with and + without debug info. Wrap static tests with with_target_prefix. + +2018-04-26 Pedro Alves <palves@redhat.com> + * gdb.compile/compile-ifunc.exp: Also expect "function has unknown return type" warnings. diff --git a/gdb/testsuite/gdb.base/gnu-ifunc-final.c b/gdb/testsuite/gdb.base/gnu-ifunc-final.c new file mode 100644 index 0000000..ef19fc9 --- /dev/null +++ b/gdb/testsuite/gdb.base/gnu-ifunc-final.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 a/gdb/testsuite/gdb.base/gnu-ifunc-lib.c b/gdb/testsuite/gdb.base/gnu-ifunc-lib.c index b9d446c..7aac81f 100644 --- a/gdb/testsuite/gdb.base/gnu-ifunc-lib.c +++ b/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 a/gdb/testsuite/gdb.base/gnu-ifunc.c b/gdb/testsuite/gdb.base/gnu-ifunc.c index 78fd30d..a4abaca 100644 --- a/gdb/testsuite/gdb.base/gnu-ifunc.c +++ b/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 a/gdb/testsuite/gdb.base/gnu-ifunc.exp b/gdb/testsuite/gdb.base/gnu-ifunc.exp index 1d0d040..827ac12 100644 --- a/gdb/testsuite/gdb.base/gnu-ifunc.exp +++ b/gdb/testsuite/gdb.base/gnu-ifunc.exp @@ -18,139 +18,371 @@ 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] -# $lib_o must not have {debug}, it would override the STT_GNU_IFUNC ELF markers. -set lib_o [standard_output_file ${libfile}.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 lib_opts {} -set exec_opts [list debug shlib=$lib_so] +set final_file "${testfile}-final" +set final_src ${final_file}.c if [get_compiler_info] { return -1 } -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 +# 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} { + return "$resolver_attr-$resolver_debug-$final_debug" } -# Start with a fresh gdb. +# 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. FINAL_DEBUG is true +# iff the target function was compiled with debug info. +proc build {resolver_attr resolver_debug final_debug} { + global srcdir subdir srcfile binfile + global libsrc lib_so libfile + global exec_opts executable + global hex gdb_prompt + global final_file final_src + + set suffix [make_binsuffix $resolver_attr $resolver_debug $final_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}-$suffix.o] + + set exec_opts [list debug shlib=$lib_so] + + set lib_opts {} + set final_opts {} + + if {$resolver_attr} { + lappend lib_opts "additional_flags=-DIFUNC_RESOLVER_ATTR" + } + + if {$resolver_debug} { + lappend lib_opts "debug" + } + + if {$final_debug} { + lappend final_opts "debug" + } -clean_restart $executable -gdb_load_shlib ${lib_so} + set final_o $final_file-$suffix.o + + if { [gdb_compile_shlib ${srcdir}/${subdir}/$libsrc \ + $lib_so $lib_opts] != "" + || [gdb_compile ${srcdir}/${subdir}/$final_src \ + $final_o object $final_opts] != "" + || [gdb_compile [list ${srcdir}/${subdir}/$srcfile $final_o] \ + $binfile-$suffix executable $exec_opts] != ""} { + untested "failed to compile testcase" + return 0 + } -if ![runto_main] then { - fail "can't run to main" return 1 } -# 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" +# Test setting a breakpoint on a ifunc function before and after the +# ifunc is resolved. For the description of RESOLVER_ATTR, +# RESOLVER_DEBUG and FINAL_DEBUG, see the "build" procedure above. +proc_with_prefix set-break {resolver_attr resolver_debug final_debug} { + global binfile libfile lib_so + global hex decimal + global gdb_prompt + + set suffix [make_binsuffix $resolver_attr $resolver_debug $final_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\]+" + set dot "\\.?" -gdb_breakpoint [gdb_get_line_number "break-at-call"] -gdb_continue_to_breakpoint "break-at-call" ".*break-at-call.*" + if {$resolver_attr} { + set gnu_ifunc_resolver "gnu_ifunc_resolver" + } else { + set gnu_ifunc_resolver "gnu_ifunc" + } -# Test GDB will automatically indirect the call. + if {!$resolver_debug} { + set gnu_ifunc_resolver "${dot}${gnu_ifunc_resolver}" + } -gdb_test "p gnu_ifunc (3)" " = 4" + if {!$final_debug} { + set final "${dot}final" + } else { + set final "final" + } -# Test that the resolver received its argument. + 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}>" + } -set actual_hwcap "0x0" -set test "info auxv" -gdb_test_multiple $test $test { - -re "\r\n\\d+\\s+AT_HWCAP\[^\r\n\]+($hex)\r\n.*$gdb_prompt $" { - set actual_hwcap $expect_out(1,string) + global final_src + + 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.*" } - -re ".*$gdb_prompt $" { - pass "$test (no HWCAP)" + + with_test_prefix "after resolving" { + delete_breakpoints + + if {!$final_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 .*$final_src, line ($decimal)\\.\r\n$gdb_prompt $" { + set lineno $expect_out(1,string) + pass $test + } + } + gdb_test "break gnu_ifunc" "Breakpoint .* at $hex: file .*$final_src, line $lineno\\." + set location "$decimal${ws}breakpoint${ws}keep${ws}y${ws}$hex in final at .*$final_src:$lineno" + } + gdb_test "info breakpoints" "$location\r\n$location" } } -gdb_test "p/x resolver_hwcap" "= $actual_hwcap" "resolver received HWCAP" +# Misc GNU ifunc tests. For the description of RESOLVER_ATTR, +# RESOLVER_DEBUG and FINAL_DEBUG, see the "build" procedure above. +proc misc_tests {resolver_attr resolver_debug final_debug} { + global srcdir subdir srcfile binfile + global libsrc lib_so libfile + global exec_opts executable + global hex gdb_prompt + global final_file final_src + + set suffix [make_binsuffix $resolver_attr $resolver_debug $final_debug] + + if {$resolver_attr} { + set gnu_ifunc_resolver "gnu_ifunc_resolver" + } else { + set gnu_ifunc_resolver "gnu_ifunc" + } + + set dot "\\.?" -# Test GDB will skip the gnu_ifunc resolver on first call. + if {!$resolver_debug} { + set gnu_ifunc_resolver "${dot}${gnu_ifunc_resolver}" + } -gdb_test "step" "\r\nfinal .*" + if {!$final_debug} { + set final "${dot}final" + } else { + set final "final" + } -# Test GDB will not break before the final chosen implementation. + # Start with a fresh gdb. -# Also test a former patch regression: -# Continuing. -# Error in testing breakpoint condition: -# Attempt to take address of value not located in memory. -# -# Breakpoint 2, main () at ./gdb.base/gnu-ifunc.c:33 + clean_restart $binfile-$suffix + gdb_load_shlib ${lib_so} -gdb_test "continue" "Continuing.\r\n\r\nBreakpoint .* (at|in) .*break-at-nextcall.*" \ - "continue to break-at-nextcall" + if ![runto_main] then { + fail "can't run to main" + return 1 + } -gdb_breakpoint "gnu_ifunc" + # The "if" condition is artifical to test regression of a former patch. + gdb_breakpoint "[gdb_get_line_number "break-at-nextcall"] if i && (int) gnu_ifunc (i) != 42" -gdb_continue_to_breakpoint "nextcall gnu_ifunc" + gdb_breakpoint [gdb_get_line_number "break-at-call"] + gdb_continue_to_breakpoint "break-at-call" ".*break-at-call.*" -gdb_test "frame" "#0 +(0x\[0-9a-f\]+ in +)?final \\(.*" "nextcall gnu_ifunc skipped" + # Test GDB will automatically indirect the call. + if {!$resolver_debug && !$final_debug} { + gdb_test "p gnu_ifunc()" \ + "'${dot}final' has unknown return type; cast the call to its declared return type" + gdb_test "p gnu_ifunc (3)" \ + "'${dot}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" + } -# Check any commands not doing an inferior call access the address of the -# STT_GNU_IFUNC resolver, not the target function. + # Test that the resolver received its argument. + + set actual_hwcap "0x0" + set test "info auxv" + gdb_test_multiple $test $test { + -re "\r\n\\d+\\s+AT_HWCAP\[^\r\n\]+($hex)\r\n.*$gdb_prompt $" { + set actual_hwcap $expect_out(1,string) + } + -re ".*$gdb_prompt $" { + pass "$test (no HWCAP)" + } + } -if {[istarget powerpc64-*] && [is_lp64_target]} { - # With only minimal symbols GDB provides the function descriptors. With - # full debug info the function code would be displayed. - set func_prefix {\.} -} else { - set func_prefix {} -} + gdb_test "p/x resolver_hwcap" "= $actual_hwcap" "resolver received HWCAP" -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" + # Test GDB will skip the gnu_ifunc resolver on first call. -set test "info addr gnu_ifunc" -gdb_test_multiple $test $test { - -re "Symbol \"gnu_ifunc\" is at (0x\[0-9a-f\]+) in .*$gdb_prompt $" { - pass $test + # Even if the resolver has debug info, stepping into an ifunc call + # should skip the resolver. + if {!$final_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 ${dot}final \\\(\\\)" + } else { + gdb_test "step" "\r\nfinal .*" + } + + # Test GDB will not break before the final chosen implementation. + + # Also test a former patch regression: + # Continuing. + # Error in testing breakpoint condition: + # Attempt to take address of value not located in memory. + # + # Breakpoint 2, main () at ./gdb.base/gnu-ifunc.c:33 + + 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" + + # Check any commands not doing an inferior call access the address of the + # STT_GNU_IFUNC resolver, not the target function. + + if {[istarget powerpc64-*] && [is_lp64_target]} { + # With only minimal symbols GDB provides the function descriptors. With + # full debug info the function code would be displayed. + } + + gdb_test "p gnu_ifunc" \ + " = {<text gnu-indirect-function variable, no debug info>} 0x\[0-9a-f\]+ <${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_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} { + if {[istarget powerpc64-*] && [is_lp64_target]} { + gdb_test "p gnu_ifunc_resolver(0)" \ + " = \\(int \\(\\*\\)\\(int\\)\\) @$hex: $hex <${final}>" + } else { + 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}>" + } } } -gdb_test "info sym $expect_out(1,string)" "gnu_ifunc in section .*" "info sym <gnu_ifunc-address>" +# 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 final_debug {0 1} { + build $resolver_attr $resolver_debug $final_debug + misc_tests $resolver_attr $resolver_debug $final_debug + set-break $resolver_attr $resolver_debug $final_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). - -if { [gdb_compile ${srcdir}/${subdir}/$libsrc $lib_o object {}] != "" - || [gdb_compile "${srcdir}/${subdir}/$srcfile $lib_o" $staticbinfile executable {debug}] != "" } { - untested "failed to compile second testcase" - return -1 -} +with_test_prefix "static" { + # Compile $staticbinfile separately as it may exit on error + # (ld/12595). + + set lib_o [standard_output_file ${libfile}.o] + set final_o [standard_output_file ${final_file}.o] + if { [gdb_compile ${srcdir}/${subdir}/$libsrc $lib_o object {}] != "" + || [gdb_compile ${srcdir}/${subdir}/$final_src $final_o object {}] != "" + || [gdb_compile "${srcdir}/${subdir}/$srcfile $lib_o $final_o" \ + $staticbinfile executable {debug}] != "" } { + untested "failed to compile second testcase" + return -1 + } -clean_restart $staticexecutable + clean_restart $staticexecutable -gdb_breakpoint "gnu_ifunc" -gdb_breakpoint "main" -gdb_run_cmd -gdb_test "" "Breakpoint \[0-9\]*, main .*" "static gnu_ifunc" + gdb_breakpoint "gnu_ifunc" + gdb_breakpoint "main" + gdb_run_cmd + gdb_test "" "Breakpoint \[0-9\]*, main .*" "static gnu_ifunc" +} |