1# Copyright (C) 2009-2024 Free Software Foundation, Inc.
2
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 3 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16require allow_shlib_tests allow_ifunc_tests
17
18standard_testfile .c
19set staticexecutable ${testfile}-static
20set staticbinfile [standard_output_file ${staticexecutable}]
21
22set libfile "${testfile}-lib"
23set libsrc ${libfile}.c
24
25set final_file "${testfile}-final"
26set final_src ${final_file}.c
27
28# Return the binary suffix appended to program and library names to
29# make each testcase variant unique.
30proc make_binsuffix {resolver_attr resolver_debug final_debug} {
31    return "$resolver_attr-$resolver_debug-$final_debug"
32}
33
34# Compile the testcase.  RESOLVER_ATTR is true if we're testing with
35# an ifunc resolver that has a different name from the user symbol,
36# specified with GCC's __attribute__ ifunc.  RESOLVER_DEBUG is true
37# iff the resolver was compiled with debug info.  FINAL_DEBUG is true
38# iff the target function was compiled with debug info.
39proc build {resolver_attr resolver_debug final_debug} {
40    global srcdir subdir srcfile binfile
41    global libsrc lib_so libfile
42    global exec_opts executable
43    global hex gdb_prompt
44    global final_file final_src
45
46    set suffix [make_binsuffix $resolver_attr $resolver_debug $final_debug]
47
48    set lib_so [standard_output_file ${libfile}-$suffix.so]
49    # $lib_o must not have {debug}, it would override the STT_GNU_IFUNC ELF markers.
50    set lib_o [standard_output_file ${libfile}-$suffix.o]
51
52    set exec_opts [list debug shlib=$lib_so]
53
54    set lib_opts {}
55    set final_opts {}
56
57    # Force lazy binding so we don't resolve everything at process startup.
58    lappend exec_opts "ldflags=-Wl,-z,lazy"
59    lappend lib_opts "ldflags=-Wl,-z,lazy"
60
61    if {$resolver_attr} {
62          lappend lib_opts "additional_flags=-DIFUNC_RESOLVER_ATTR"
63    }
64
65    if {$resolver_debug} {
66          lappend lib_opts "debug"
67    }
68
69    if {$final_debug} {
70          lappend final_opts "debug"
71    }
72
73    set final_o [standard_output_file $final_file-$suffix.o]
74
75    if { [gdb_compile_shlib ${srcdir}/${subdir}/$libsrc \
76                $lib_so $lib_opts] != ""
77           || [gdb_compile ${srcdir}/${subdir}/$final_src \
78                     $final_o object $final_opts] != ""
79           || [gdb_compile [list ${srcdir}/${subdir}/$srcfile $final_o] \
80                     $binfile-$suffix executable $exec_opts] != ""} {
81          untested "failed to compile testcase"
82          return 0
83    }
84
85    return 1
86}
87
88# Test setting a breakpoint on a ifunc function before and after the
89# ifunc is resolved.  For the description of RESOLVER_ATTR,
90# RESOLVER_DEBUG and FINAL_DEBUG, see the "build" procedure above.
91proc_with_prefix set-break {resolver_attr resolver_debug final_debug} {
92    global binfile libfile lib_so
93    global hex decimal
94    global gdb_prompt
95
96    set suffix [make_binsuffix $resolver_attr $resolver_debug $final_debug]
97
98    set lib_so [standard_output_file ${libfile}-$suffix.so]
99    clean_restart $binfile-$suffix
100    gdb_load_shlib ${lib_so}
101
102    if {![runto_main]} {
103          return 1
104    }
105
106    gdb_breakpoint [gdb_get_line_number "break-at-call"]
107    gdb_continue_to_breakpoint "break-at-call" ".*break-at-call.*"
108
109    set ws "\[ \t\]+"
110    set dot "\\.?"
111
112    if {$resolver_attr} {
113          set gnu_ifunc_resolver "gnu_ifunc_resolver"
114    } else {
115          set gnu_ifunc_resolver "gnu_ifunc"
116    }
117
118    if {!$resolver_debug} {
119          set gnu_ifunc_resolver "${dot}${gnu_ifunc_resolver}"
120    }
121
122    if {!$final_debug} {
123          set final "${dot}final"
124    } else {
125          set final "final"
126    }
127
128    with_test_prefix "before resolving" {
129          delete_breakpoints
130          gdb_test "break gnu_ifunc" \
131              "Breakpoint $decimal at gnu-indirect-function resolver at $hex"
132          gdb_test "info breakpoints" \
133              "$decimal${ws}STT_GNU_IFUNC resolver${ws}keep${ws}y${ws}$hex <${gnu_ifunc_resolver}>"
134
135          # Make the breakpoint conditional on a condition that always
136          # fails.  This is so that when the ifunc-resolver breakpoint
137          # triggers, GDB resumes the program immediately.
138          gdb_test_no_output "condition \$bpnum 0"
139    }
140
141    global final_src
142
143    with_test_prefix "resolve" {
144          gdb_breakpoint [gdb_get_line_number "break-at-exit"]
145          gdb_continue_to_breakpoint "break-at-exit" ".*break-at-exit.*"
146    }
147
148    with_test_prefix "after resolving" {
149          if {!$final_debug} {
150              # Set a breakpoint both at the ifunc, and at the ifunc's
151              # target.  GDB should resolve both to the same address.
152              # Start with the ifunc's target.
153              set addr "-"
154              set test "break final"
155              # Extract the address without the leading "0x", because
156              # addresses in "info break" output include leading 0s
157              # (like "0x0000ADDR").
158              set hex_number {[0-9a-fA-F][0-9a-fA-F]*}
159              gdb_test_multiple $test $test {
160                    -re "Breakpoint .* at 0x($hex_number)\r\n$gdb_prompt $" {
161                        set addr $expect_out(1,string)
162                        pass $test
163                    }
164              }
165
166              # Now set a break at the ifunc.
167              gdb_test "break gnu_ifunc" "Breakpoint .* at 0x$addr"
168              set location "$decimal${ws}breakpoint${ws}keep${ws}y${ws}0x0*$addr${ws}<${final}\\+.*>"
169          } else {
170              set lineno -1
171              set test "break final"
172              gdb_test_multiple $test $test {
173                    -re "Breakpoint .* at $hex: file .*$final_src, line ($decimal)\\.\r\n$gdb_prompt $" {
174                        set lineno $expect_out(1,string)
175                        pass $test
176                    }
177              }
178              gdb_test "break gnu_ifunc" "Breakpoint .* at $hex: file .*$final_src, line $lineno\\."
179              set location "$decimal${ws}breakpoint${ws}keep${ws}y${ws}$hex in final at .*$final_src:$lineno"
180          }
181
182          # The first location here is for the breakpoint that was set
183          # before the ifunc was resolved.  It should be resolved by
184          # now, and it should have the exact same address/line as the
185          # other two locations.
186          gdb_test "info breakpoints" "$location\r\n.*$location\r\n$location"
187    }
188}
189
190# Misc GNU ifunc tests.  For the description of RESOLVER_ATTR,
191# RESOLVER_DEBUG and FINAL_DEBUG, see the "build" procedure above.
192proc misc_tests {resolver_attr resolver_debug final_debug} {
193    global srcdir subdir srcfile binfile
194    global libsrc lib_so libfile
195    global exec_opts executable
196    global hex gdb_prompt
197    global final_file final_src
198
199    set suffix [make_binsuffix $resolver_attr $resolver_debug $final_debug]
200
201    if {$resolver_attr} {
202          set gnu_ifunc_resolver "gnu_ifunc_resolver"
203    } else {
204          set gnu_ifunc_resolver "gnu_ifunc"
205    }
206
207    set dot "\\.?"
208
209    if {!$resolver_debug} {
210          set gnu_ifunc_resolver "${dot}${gnu_ifunc_resolver}"
211    }
212
213    if {!$final_debug} {
214          set final "${dot}final"
215    } else {
216          set final "final"
217    }
218
219    # Start with a fresh gdb.
220
221    clean_restart $binfile-$suffix
222    gdb_load_shlib ${lib_so}
223
224    if {![runto_main]} {
225          return 1
226    }
227
228    # The "if" condition is artifical to test regression of a former patch.
229    gdb_breakpoint "[gdb_get_line_number "break-at-nextcall"] if i && (int) gnu_ifunc (i) != 42"
230
231    gdb_breakpoint [gdb_get_line_number "break-at-call"]
232    gdb_continue_to_breakpoint "break-at-call" ".*break-at-call.*"
233
234    # Test GDB will automatically indirect the call.
235
236    if {!$resolver_debug && !$final_debug} {
237          # Do the test that is supposed to succeed first, to make sure
238          # elf_gnu_ifunc_record_cache is empty.  This excercises PR28224.
239          gdb_test "p (int) gnu_ifunc (3)" " = 4"
240
241          gdb_test "p gnu_ifunc()" \
242              "'${dot}final' has unknown return type; cast the call to its declared return type"
243          gdb_test "p gnu_ifunc (3)" \
244              "'${dot}final' has unknown return type; cast the call to its declared return type"
245    } else {
246          # Do the test that is supposed to succeed first, see above.
247          gdb_test "p gnu_ifunc (3)" " = 4"
248
249          gdb_test "p gnu_ifunc()" "Too few arguments in function call\\."
250    }
251
252    # Test that the resolver received its argument.
253
254    set actual_hwcap "0x0"
255    set test "info auxv"
256    gdb_test_multiple $test $test {
257          -re "\r\n\\d+\\s+AT_HWCAP\[^\r\n\]+($hex)\r\n.*$gdb_prompt $" {
258              set actual_hwcap $expect_out(1,string)
259          }
260          -re ".*$gdb_prompt $" {
261              pass "$test (no HWCAP)"
262          }
263    }
264
265    gdb_test "p/x resolver_hwcap" "= $actual_hwcap" "resolver received HWCAP"
266
267    # Test GDB will skip the gnu_ifunc resolver on first call.
268
269    # Even if the resolver has debug info, stepping into an ifunc call
270    # should skip the resolver.
271    if {!$final_debug} {
272          # Make GDB stop stepping even if it steps into a function with
273          # no debug info.
274          gdb_test_no_output "set step-mode on"
275          gdb_test "step" "$hex in ${dot}final \\\(\\\)"
276    } else {
277          gdb_test "step" "\r\nfinal .*"
278    }
279
280    # Test GDB will not break before the final chosen implementation.
281
282    # Also test a former patch regression:
283    # Continuing.
284    # Error in testing condition for breakpoint NUM:
285    # Attempt to take address of value not located in memory.
286    #
287    # Breakpoint 2, main () at ./gdb.base/gnu-ifunc.c:33
288
289    gdb_test "continue" \
290          "Continuing.\r\n\r\nBreakpoint .* (at|in) .*break-at-nextcall.*" \
291          "continue to break-at-nextcall"
292
293    gdb_breakpoint "gnu_ifunc"
294
295    gdb_continue_to_breakpoint "nextcall gnu_ifunc"
296
297    gdb_test "frame" \
298          "#0 +(0x\[0-9a-f\]+ in +)?${final} \\(.*" "nextcall gnu_ifunc skipped"
299
300    # Check any commands not doing an inferior call access the address of the
301    # STT_GNU_IFUNC resolver, not the target function.
302
303    if {[istarget powerpc64-*] && [is_lp64_target]} {
304          # With only minimal symbols GDB provides the function descriptors.  With
305          # full debug info the function code would be displayed.
306    }
307
308    gdb_test "p gnu_ifunc" \
309          " = {<text gnu-indirect-function variable, no debug info>} 0x\[0-9a-f\]+ <${gnu_ifunc_resolver}>" \
310          "p gnu_ifunc executing"
311    gdb_test "info sym gnu_ifunc" \
312          "${gnu_ifunc_resolver} in section .*" \
313          "info sym gnu_ifunc executing"
314
315    set test "info addr gnu_ifunc"
316    if {!$resolver_attr && $resolver_debug} {
317          gdb_test_multiple $test $test {
318              -re "Symbol \"gnu_ifunc\" is a function at address (0x\[0-9a-f\]+).*$gdb_prompt $" {
319                    pass $test
320              }
321          }
322    } else {
323          gdb_test_multiple $test $test {
324              -re "Symbol \"gnu_ifunc\" is at (0x\[0-9a-f\]+) in .*$gdb_prompt $" {
325                    pass $test
326              }
327          }
328    }
329    gdb_test "info sym $expect_out(1,string)" \
330          "${gnu_ifunc_resolver} in section .*" \
331          "info sym <gnu_ifunc-address>"
332
333    # Test calling the resolver directly instead of the ifunc symbol.
334    # Can only do that if the ifunc and the ifunc resolver have
335    # different names.
336    if {$resolver_attr} {
337          if {$resolver_debug} {
338              if {[istarget powerpc64-*] && [is_lp64_target]} {
339                    gdb_test "p gnu_ifunc_resolver(0)" \
340                        " = \\(int \\(\\*\\)\\(int\\)\\) @$hex: $hex <${final}>"
341              } else {
342                    gdb_test "p gnu_ifunc_resolver(0)" \
343                        " = \\(int \\(\\*\\)\\(int\\)\\) $hex <final>"
344              }
345          } else {
346              gdb_test "p gnu_ifunc_resolver(0)" \
347                    "'${gnu_ifunc_resolver}' has unknown return type; cast the call to its declared return type"
348              gdb_test "p (void *) gnu_ifunc_resolver(0)" \
349                    " = \\(void \\*\\) $hex <${final}>"
350          }
351    }
352}
353
354# Test all the combinations of:
355#
356# - An ifunc resolver with the same name as the ifunc symbol vs an
357#   ifunc resolver with a different name as the ifunc symbol.
358#
359# - ifunc resolver compiled with and without debug info.  This ensures
360#   that GDB understands that a function not a regular function by
361#   looking at the STT_GNU_IFUNC type in the elf symbols.  DWARF has
362#   no way to express the STT_GNU_IFUNC type.
363#
364# - ifunc target function (resolved) compiled with and without debug
365#   info.
366foreach_with_prefix resolver_attr {0 1} {
367    foreach_with_prefix resolver_debug {0 1} {
368          foreach_with_prefix final_debug {0 1} {
369              if { [build $resolver_attr $resolver_debug $final_debug] != 0 } {
370                    misc_tests $resolver_attr $resolver_debug $final_debug
371                    set-break $resolver_attr $resolver_debug $final_debug
372              }
373          }
374    }
375}
376
377# Test statically linked ifunc resolving during inferior start.
378# https://bugzilla.redhat.com/show_bug.cgi?id=624967
379
380with_test_prefix "static" {
381    # Compile $staticbinfile separately as it may exit on error
382    # (ld/12595).
383
384    set lib_o [standard_output_file ${libfile}.o]
385    set final_o [standard_output_file ${final_file}.o]
386    if { [gdb_compile ${srcdir}/${subdir}/$libsrc $lib_o object {}] != ""
387           || [gdb_compile ${srcdir}/${subdir}/$final_src $final_o object {}] != ""
388           || [gdb_compile "${srcdir}/${subdir}/$srcfile $lib_o $final_o" \
389                     $staticbinfile executable {debug}] != "" } {
390          untested "failed to compile second testcase"
391          return -1
392    }
393
394    clean_restart $staticexecutable
395
396    gdb_breakpoint "gnu_ifunc"
397    gdb_breakpoint "main"
398    gdb_run_cmd
399    gdb_test "" "Breakpoint \[0-9\]*, main .*" "static gnu_ifunc"
400}
401