}
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"
+}