| # Copyright (C) 2009-2021 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/>. |
| |
| if {[skip_shlib_tests]} { |
| return 0 |
| } |
| |
| if {[skip_ifunc_tests]} { |
| return 0 |
| } |
| |
| standard_testfile .c |
| set staticexecutable ${testfile}-static |
| set staticbinfile [standard_output_file ${staticexecutable}] |
| |
| set libfile "${testfile}-lib" |
| set libsrc ${libfile}.c |
| |
| set final_file "${testfile}-final" |
| set final_src ${final_file}.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 final_debug} { |
| return "$resolver_attr-$resolver_debug-$final_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. 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 {} |
| |
| # Force lazy binding so we don't resolve everything at process startup. |
| lappend exec_opts "additional_flags=-Wl,-z,lazy" |
| lappend lib_opts "additional_flags=-Wl,-z,lazy" |
| |
| 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" |
| } |
| |
| set final_o [standard_output_file $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 |
| } |
| |
| return 1 |
| } |
| |
| # 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 { |
| return 1 |
| } |
| |
| gdb_breakpoint [gdb_get_line_number "break-at-call"] |
| gdb_continue_to_breakpoint "break-at-call" ".*break-at-call.*" |
| |
| set ws "\[ \t\]+" |
| set dot "\\.?" |
| |
| if {$resolver_attr} { |
| set gnu_ifunc_resolver "gnu_ifunc_resolver" |
| } else { |
| set gnu_ifunc_resolver "gnu_ifunc" |
| } |
| |
| if {!$resolver_debug} { |
| set gnu_ifunc_resolver "${dot}${gnu_ifunc_resolver}" |
| } |
| |
| if {!$final_debug} { |
| set final "${dot}final" |
| } else { |
| set final "final" |
| } |
| |
| 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}>" |
| |
| # Make the breakpoint conditional on a condition that always |
| # fails. This is so that when the ifunc-resolver breakpoint |
| # triggers, GDB resumes the program immediately. |
| gdb_test_no_output "condition \$bpnum 0" |
| } |
| |
| global final_src |
| |
| with_test_prefix "resolve" { |
| gdb_breakpoint [gdb_get_line_number "break-at-exit"] |
| gdb_continue_to_breakpoint "break-at-exit" ".*break-at-exit.*" |
| } |
| |
| with_test_prefix "after resolving" { |
| 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" |
| } |
| |
| # The first location here is for the breakpoint that was set |
| # before the ifunc was resolved. It should be resolved by |
| # now, and it should have the exact same address/line as the |
| # other two locations. |
| gdb_test "info breakpoints" "$location\r\n.*$location\r\n$location" |
| } |
| } |
| |
| # 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 "\\.?" |
| |
| if {!$resolver_debug} { |
| set gnu_ifunc_resolver "${dot}${gnu_ifunc_resolver}" |
| } |
| |
| if {!$final_debug} { |
| set final "${dot}final" |
| } else { |
| set final "final" |
| } |
| |
| # Start with a fresh gdb. |
| |
| clean_restart $binfile-$suffix |
| gdb_load_shlib ${lib_so} |
| |
| if ![runto_main] then { |
| 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 && (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 && !$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" |
| } |
| |
| # 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)" |
| } |
| } |
| |
| 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 {!$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}>" |
| } |
| } |
| } |
| |
| # 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} { |
| 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 statically linked ifunc resolving during inferior start. |
| # https://bugzilla.redhat.com/show_bug.cgi?id=624967 |
| |
| 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 |
| |
| gdb_breakpoint "gnu_ifunc" |
| gdb_breakpoint "main" |
| gdb_run_cmd |
| gdb_test "" "Breakpoint \[0-9\]*, main .*" "static gnu_ifunc" |
| } |