1# Copyright 2012-2014 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
16# Contributed by Mentor Graphics, written by Maciej W. Rozycki.
17
18# Test MIPS16 thunk support.
19
20# This should work on any targets that support MIPS16 execution, including
21# Linux and bare-iron ones, but not all of them do, for example MIPS16
22# support has been added to Linux relatively late in the game.  Also besides
23# environment support, the target processor has to support the MIPS16 ASE.
24# Finally as of this writing MIPS16 support has only been implemented in the
25# toolchain for a subset of ABIs, so we need to check that a MIPS16
26# executable can be built and run at all before we attempt the actual test.
27
28if { ![istarget "mips*-*-*"] } then {
29    verbose "Skipping MIPS16 thunk support tests."
30    return
31}
32
33# A helper to set caller's SRCFILE and OBJFILE based on FILENAME and SUFFIX.
34proc set_src_and_obj { filename { suffix "" } } {
35    upvar srcfile srcfile
36    upvar objfile objfile
37    global srcdir
38    global objdir
39    global subdir
40
41    if ![string equal "$suffix" ""] then {
42	set suffix "-$suffix"
43    }
44    set srcfile ${srcdir}/${subdir}/${filename}.c
45    set objfile ${objdir}/${subdir}/${filename}${suffix}.o
46}
47
48# First check if a trivial MIPS16 program can be built and debugged.  This
49# verifies environment and processor support, any failure here must be
50# classed as the lack of support.
51set testname mips16-thunks-main
52
53set_src_and_obj mips16-thunks-inmain
54set options [list debug nowarnings additional_flags=-mips16]
55set objfiles ${objfile}
56gdb_compile ${srcfile} ${objfile} object ${options}
57
58set_src_and_obj mips16-thunks-main
59set options [list debug nowarnings additional_flags=-mips16]
60lappend objfiles ${objfile}
61gdb_compile ${srcfile} ${objfile} object ${options}
62
63set binfile ${objdir}/${subdir}/${testname}
64set options [list debug nowarnings]
65if { [gdb_compile ${objfiles} ${binfile} executable ${options}] != "" } then {
66    unsupported "No MIPS16 support in the toolchain."
67    return
68}
69clean_restart ${testname}
70gdb_breakpoint inmain
71gdb_run_cmd
72gdb_test_multiple "" "check for MIPS16 support in the processor" {
73    -re "Breakpoint 1.*inmain .*$gdb_prompt $" {
74	gdb_test_multiple "finish" \
75	    "check for MIPS16 support in the processor" {
76	    -re "Value returned is \\\$\[0-9\]+ = 0\[^0-9\].*$gdb_prompt $" {
77		verbose "MIPS16 support check successful."
78	    }
79	    -re "$gdb_prompt $" {
80		unsupported "No MIPS16 support in the processor."
81		return
82	    }
83	    default {
84		unsupported "No MIPS16 support in the processor."
85		return
86	    }
87	}
88    }
89    -re "$gdb_prompt $" {
90	unsupported "No MIPS16 support in the processor."
91	return
92    }
93    default {
94	unsupported "No MIPS16 support in the processor."
95	return
96    }
97}
98
99# Check if MIPS16 PIC code can be built and debugged.  We want to check
100# PIC and MIPS16 thunks are handled correctly together if possible, but
101# on targets that do not support PIC code, e.g. bare iron, we still want
102# to test the rest of functionality.
103set testname mips16-thunks-pic
104set picflag ""
105
106set_src_and_obj mips16-thunks-inmain pic
107set options [list \
108    debug nowarnings additional_flags=-mips16 additional_flags=-fPIC]
109set objfiles ${objfile}
110gdb_compile ${srcfile} ${objfile} object ${options}
111
112set_src_and_obj mips16-thunks-main pic
113set options [list \
114    debug nowarnings additional_flags=-mips16 additional_flags=-fPIC]
115lappend objfiles ${objfile}
116gdb_compile ${srcfile} ${objfile} object ${options}
117
118set binfile ${objdir}/${subdir}/${testname}
119set options [list debug nowarnings additional_flags=-fPIC]
120if { [gdb_compile ${objfiles} ${binfile} executable ${options}] == "" } then {
121    clean_restart ${testname}
122    gdb_breakpoint inmain
123    gdb_run_cmd
124    gdb_test_multiple "" "check for PIC support" {
125	-re "Breakpoint 1.*inmain .*$gdb_prompt $" {
126	    note "PIC support present, will make additional PIC thunk checks."
127	    set picflag additional_flags=-fPIC
128	}
129	-re "$gdb_prompt $" {
130	    note "No PIC support, skipping additional PIC thunk checks."
131	}
132	default {
133	    note "No PIC support, skipping additional PIC thunk checks."
134	}
135    }
136} else {
137    note "No PIC support, skipping additional PIC thunk checks."
138}
139
140# OK, build the twisted executable.  This program contains the following
141# MIPS16 thunks:
142# - __call_stub_fp_sin,
143# - __call_stub_fp_sinblah,
144# - __call_stub_fp_sinfrob,
145# - __call_stub_fp_sinhelper,
146# - __call_stub_lsinhelper,
147# - __fn_stub_lsinmips16,
148# - __fn_stub_sinblah16,
149# - __fn_stub_sinfrob16,
150# - __fn_stub_sinmips16,
151# - __mips16_call_stub_df_2,
152# - __mips16_ret_df.
153# Additionally, if PIC code is supported, it contains the following PIC thunks:
154# - .pic.__mips16_call_stub_df_2,
155# - .pic.__mips16_ret_df,
156# - .pic.sinblah,
157# - .pic.sinblah16,
158# - .pic.sinfrob,
159# - .pic.sinfrob16.
160set testname mips16-thunks-sin
161
162set_src_and_obj mips16-thunks-sinmain
163set options [list debug nowarnings additional_flags=-mips16]
164set objfiles ${objfile}
165gdb_compile ${srcfile} ${objfile} object ${options}
166
167set_src_and_obj mips16-thunks-sin
168set options [list debug nowarnings additional_flags=-mno-mips16]
169lappend objfiles ${objfile}
170gdb_compile ${srcfile} ${objfile} object ${options}
171
172set_src_and_obj mips16-thunks-sinmips16
173set options [list debug nowarnings additional_flags=-mips16]
174lappend objfiles ${objfile}
175gdb_compile ${srcfile} ${objfile} object ${options}
176
177set_src_and_obj mips16-thunks-sinfrob
178set options [list \
179    debug nowarnings additional_flags=-mno-mips16 ${picflag}]
180lappend objfiles ${objfile}
181gdb_compile ${srcfile} ${objfile} object ${options}
182
183set_src_and_obj mips16-thunks-sinfrob16
184set options [list \
185    debug nowarnings additional_flags=-mips16 ${picflag}]
186lappend objfiles ${objfile}
187gdb_compile ${srcfile} ${objfile} object ${options}
188
189set binfile ${objdir}/${subdir}/${testname}
190set options [list debug nowarnings]
191gdb_compile ${objfiles} ${binfile} executable ${options}
192clean_restart ${testname}
193if ![runto_main] then {
194    fail "running test program, MIPS16 thunk tests aborted"
195    return
196}
197
198# Build some useful regular expressions out of a list of functions FUNCS
199# to be used to match against backtraces.
200proc build_frames_re { funcs } {
201    upvar anyframe anyframe
202    upvar frames frames
203    upvar frame frame
204    upvar func func
205
206    set fid 0
207    set argsandsource " +\\\(.*\\\) +at +\[^\r\n\]+\r\n"
208    set addrin "(?:\[^ \]+ +in +)?"
209    set anyframe "#${fid} +${addrin}(\[^ \]+)${argsandsource}"
210    set frame "#${fid} +${addrin}${func}${argsandsource}"
211    set frames "$frame"
212    foreach f [lrange $funcs 1 end] {
213	incr fid
214	append frames "#${fid} +${addrin}${f}${argsandsource}"
215    }
216}
217
218# Single-step through the function that is at the head of function list
219# FUNCS until a different function (frame) is reached.  Before each step
220# check the backtrace against FUNCS.  ID is used for reporting, to tell
221# apart different calls to this procedure for the same function.  If
222# successful, then return the name of the function we have stopped in.
223proc step_through { id funcs } {
224    global gdb_prompt
225
226    set func [lindex $funcs 0]
227    build_frames_re "$funcs"
228
229    set msg "single-stepping through \"${func}\" ($id)"
230
231    # Arbitrarily limit the maximium number of steps made to avoid looping
232    # indefinitely in the case something goes wrong, increase as (if)
233    # necessary.
234    set count 8
235    while { $count > 0 } {
236	if { [gdb_test_multiple "backtrace" "$msg (backtrace)" {
237	    -re "${frames}$gdb_prompt $" {
238		if { [gdb_test_multiple "step" "$msg (step)" {
239		    -re "$gdb_prompt $" {
240			if { [gdb_test_multiple "frame" "$msg (frame)" {
241			    -re "${frame}.*$gdb_prompt $" {
242			    }
243			    -re "${anyframe}.*$gdb_prompt $" {
244				pass "$msg"
245				return $expect_out(1,string)
246			    }
247			}] != 0 } then {
248			    return ""
249			}
250		    }
251		}] != 0 } then {
252		    return ""
253		}
254	    }
255	}] != 0 } then {
256	    return ""
257	}
258	incr count -1
259    }
260    fail "$msg (too many steps)"
261    return ""
262}
263
264# Finish the current function that must be one that is at the head of
265# function list FUNCS.  Before that check the backtrace against FUNCS.
266# ID is used for reporting, to tell apart different calls to this
267# procedure for the same function.  If successful, then return the name
268# of the function we have stopped in.
269proc finish_through { id funcs } {
270    global gdb_prompt
271
272    set func [lindex $funcs 0]
273    build_frames_re "$funcs"
274
275    set msg "finishing \"${func}\" ($id)"
276
277    gdb_test_multiple "backtrace" "$msg (backtrace)" {
278	-re "${frames}$gdb_prompt $" {
279	    gdb_test_multiple "finish" "$msg (finish)" {
280		-re "Run till exit from ${frame}.*$gdb_prompt $" {
281		    gdb_test_multiple "frame" "$msg (frame)" {
282			-re "${anyframe}.*$gdb_prompt $" {
283			    pass "$msg"
284			    return $expect_out(1,string)
285			}
286		    }
287		}
288	    }
289	}
290    }
291    return ""
292}
293
294# Report PASS if VAL is equal to EXP, otherwise report FAIL, using MSG.
295proc pass_if_eq { val exp msg } {
296    if [string equal "$val" "$exp"] then {
297	pass "$msg"
298    } else {
299	fail "$msg"
300    }
301}
302
303# Check if FUNC is equal to WANT.  If not, then assume that we have stepped
304# into a library call.  In this case finish it, then step out of the caller.
305# ID is used for reporting, to tell apart different calls to this procedure
306# for the same function.  If successful, then return the name of the
307# function we have stopped in.
308proc finish_if_ne { id func want funcs } {
309    if ![string equal "$func" "$want"] then {
310	set call "$func"
311	set want [lindex $funcs 0]
312	set func [finish_through "$id" [linsert $funcs 0 "$func"]]
313	pass_if_eq "$func" "$want" "\"${call}\" finishing to \"${want}\" ($id)"
314	set func [step_through "$id" $funcs]
315    }
316    return "$func"
317}
318
319# Now single-step through the program, making sure all thunks are correctly
320# stepped over and omitted from backtraces.
321
322set id 1
323set func [step_through $id [list main]]
324pass_if_eq "$func" sinfrob16 "stepping from \"main\" into \"sinfrob16\" ($id)"
325
326incr id
327set func [step_through $id [list sinfrob16 main]]
328set func [finish_if_ne $id "$func" main [list sinfrob16 main]]
329pass_if_eq "$func" main "stepping from \"sinfrob16\" back to \"main\" ($id)"
330
331incr id
332set func [step_through $id [list main]]
333pass_if_eq "$func" sinfrob "stepping from \"main\" into \"sinfrob\" ($id)"
334
335incr id
336set func [step_through $id [list sinfrob main]]
337set func [finish_if_ne $id "$func" main [list sinfrob main]]
338pass_if_eq "$func" main "stepping from \"sinfrob\" back to \"main\" ($id)"
339
340# 5
341incr id
342set func [step_through $id [list main]]
343pass_if_eq "$func" sinhelper "stepping from \"main\" into \"sinhelper\" ($id)"
344
345incr id
346set func [step_through $id [list sinhelper main]]
347set func [finish_if_ne $id "$func" sinfrob16 [list sinhelper main]]
348pass_if_eq "$func" sinfrob16 \
349    "stepping from \"sinhelper\" into \"sinfrob16\" ($id)"
350
351incr id
352set func [step_through $id [list sinfrob16 sinhelper main]]
353set func [finish_if_ne $id "$func" sinhelper [list sinfrob16 sinhelper main]]
354pass_if_eq "$func" sinhelper \
355    "stepping from \"sinfrob16\" back to \"sinhelper\" ($id)"
356
357incr id
358set func [step_through $id [list sinhelper main]]
359pass_if_eq "$func" sinfrob "stepping from \"sinhelper\" into \"sinfrob\" ($id)"
360
361incr id
362set func [step_through $id [list sinfrob sinhelper main]]
363set func [finish_if_ne $id "$func" sinhelper [list sinfrob sinhelper main]]
364pass_if_eq "$func" sinhelper \
365    "stepping from \"sinfrob\" back to \"sinhelper\" ($id)"
366
367# 10
368incr id
369set func [step_through $id [list sinhelper main]]
370pass_if_eq "$func" sinmips16 \
371    "stepping from \"sinhelper\" into \"sinmips16\" ($id)"
372
373incr id
374set func [step_through $id [list sinmips16 sinhelper main]]
375set func [finish_if_ne $id "$func" sinfrob16 [list sinmips16 sinhelper main]]
376pass_if_eq "$func" sinfrob16 \
377    "stepping from \"sinmips16\" into \"sinfrob16\" ($id)"
378
379incr id
380set func [step_through $id [list sinfrob16 sinmips16 sinhelper main]]
381set func [finish_if_ne $id "$func" sinmips16 \
382	      [list sinfrob16 sinmips16 sinhelper main]]
383pass_if_eq "$func" sinmips16 \
384    "stepping from \"sinfrob16\" back to \"sinmips16\" ($id)"
385
386incr id
387set func [step_through $id [list sinmips16 sinhelper main]]
388pass_if_eq "$func" sinfrob "stepping from \"sinmips16\" into \"sinfrob\" ($id)"
389
390incr id
391set func [step_through $id [list sinfrob sinmips16 sinhelper main]]
392set func [finish_if_ne $id "$func" sinhelper \
393	      [list sinfrob sinmips16 sinhelper main]]
394pass_if_eq "$func" sinmips16 \
395    "stepping from \"sinfrob\" back to \"sinmips16\" ($id)"
396
397# 15
398incr id
399set func [step_through $id [list sinmips16 sinhelper main]]
400pass_if_eq "$func" sinfrob16 \
401    "stepping from \"sinmips16\" into \"sinfrob16\" (indirectly) ($id)"
402
403incr id
404set func [step_through $id [list sinfrob16 sinmips16 sinhelper main]]
405set func [finish_if_ne $id "$func" sinmips16 \
406	      [list sinfrob16 sinmips16 sinhelper main]]
407pass_if_eq "$func" sinmips16 \
408    "stepping from \"sinfrob16\" back to \"sinmips16\" (indirectly) ($id)"
409
410incr id
411set func [step_through $id [list sinmips16 sinhelper main]]
412pass_if_eq "$func" sinfrob \
413    "stepping from \"sinmips16\" into \"sinfrob\" (indirectly) ($id)"
414
415incr id
416set func [step_through $id [list sinfrob sinmips16 sinhelper main]]
417set func [finish_if_ne $id "$func" sinhelper \
418	      [list sinfrob sinmips16 sinhelper main]]
419pass_if_eq "$func" sinmips16 \
420    "stepping from \"sinfrob\" back to \"sinmips16\" (indirectly) ($id)"
421
422incr id
423set func [step_through $id [list sinmips16 sinhelper main]]
424pass_if_eq "$func" sinhelper \
425    "stepping from \"sinmips16\" back to \"sinhelper\" ($id)"
426
427# 20
428incr id
429set func [step_through $id [list sinhelper main]]
430pass_if_eq "$func" main "stepping from \"sinhelper\" back to \"main\" ($id)"
431
432incr id
433set func [step_through $id [list main]]
434pass_if_eq "$func" sinblah "stepping from \"main\" into \"sinblah\" ($id)"
435
436incr id
437set func [step_through $id [list sinblah main]]
438set func [finish_if_ne $id "$func" main [list sinblah main]]
439pass_if_eq "$func" main "stepping from \"sinblah\" back to \"main\" ($id)"
440
441incr id
442set func [step_through $id [list main]]
443pass_if_eq "$func" sinblah16 "stepping from \"main\" into \"sinblah16\" ($id)"
444
445incr id
446set func [step_through $id [list sinblah16 main]]
447set func [finish_if_ne $id "$func" main [list sinblah16 main]]
448pass_if_eq "$func" main "stepping from \"sinblah16\" back to \"main\" ($id)"
449
450# 25
451incr id
452set func [step_through $id [list main]]
453pass_if_eq "$func" lsinhelper \
454    "stepping from \"main\" into \"lsinhelper\" ($id)"
455
456incr id
457set func [step_through $id [list lsinhelper main]]
458set func [finish_if_ne $id "$func" sinblah [list lsinhelper main]]
459pass_if_eq "$func" sinblah \
460    "stepping from \"lsinhelper\" into \"sinblah\" ($id)"
461
462incr id
463set func [step_through $id [list sinblah lsinhelper main]]
464set func [finish_if_ne $id "$func" lsinhelper [list sinblah lsinhelper main]]
465pass_if_eq "$func" lsinhelper \
466    "stepping from \"sinblah\" back to \"lsinhelper\" ($id)"
467
468incr id
469set func [step_through $id [list lsinhelper main]]
470pass_if_eq "$func" sinblah16 \
471    "stepping from \"lsinhelper\" into \"sinblah16\" ($id)"
472
473incr id
474set func [step_through $id [list sinblah16 lsinhelper main]]
475set func [finish_if_ne $id "$func" lsinhelper [list sinblah16 lsinhelper main]]
476pass_if_eq "$func" lsinhelper \
477    "stepping from \"sinblah16\" back to \"lsinhelper\" ($id)"
478
479# 30
480incr id
481set func [step_through $id [list lsinhelper main]]
482pass_if_eq "$func" lsinmips16 \
483    "stepping from \"lsinhelper\" into \"lsinmips16\" ($id)"
484
485incr id
486set func [step_through $id [list lsinmips16 lsinhelper main]]
487set func [finish_if_ne $id "$func" sinblah [list lsinmips16 lsinhelper main]]
488pass_if_eq "$func" sinblah \
489    "stepping from \"lsinmips16\" into \"sinblah\" ($id)"
490
491incr id
492set func [step_through $id [list sinblah lsinmips16 lsinhelper main]]
493set func [finish_if_ne $id "$func" lsinmips16 \
494	      [list sinblah lsinmips16 lsinhelper main]]
495pass_if_eq "$func" lsinmips16 \
496    "stepping from \"sinblah\" back to \"lsinmips16\" ($id)"
497
498incr id
499set func [step_through $id [list lsinmips16 lsinhelper main]]
500pass_if_eq "$func" sinblah16 \
501    "stepping from \"lsinmips16\" into \"sinblah16\" ($id)"
502
503incr id
504set func [step_through $id [list sinblah16 lsinmips16 lsinhelper main]]
505set func [finish_if_ne $id "$func" lsinhelper \
506	      [list sinblah16 lsinmips16 lsinhelper main]]
507pass_if_eq "$func" lsinmips16 \
508    "stepping from \"sinblah16\" back to \"lsinmips16\" ($id)"
509
510# 35
511incr id
512set func [step_through $id [list lsinmips16 lsinhelper main]]
513pass_if_eq "$func" sinblah \
514    "stepping from \"lsinmips16\" into \"sinblah\" (indirectly) ($id)"
515
516incr id
517set func [step_through $id [list sinblah lsinmips16 lsinhelper main]]
518set func [finish_if_ne $id "$func" lsinmips16 \
519	      [list sinblah lsinmips16 lsinhelper main]]
520pass_if_eq "$func" lsinmips16 \
521    "stepping from \"sinblah\" back to \"lsinmips16\" (indirectly) ($id)"
522
523incr id
524set func [step_through $id [list lsinmips16 lsinhelper main]]
525pass_if_eq "$func" sinblah16 \
526    "stepping from \"lsinmips16\" into \"sinblah16\" (indirectly) ($id)"
527
528incr id
529set func [step_through $id [list sinblah16 lsinmips16 lsinhelper main]]
530set func [finish_if_ne $id "$func" lsinhelper \
531	      [list sinblah16 lsinmips16 lsinhelper main]]
532pass_if_eq "$func" lsinmips16 \
533    "stepping from \"sinblah16\" back to \"lsinmips16\" (indirectly) ($id)"
534
535incr id
536set func [step_through $id [list lsinmips16 lsinhelper main]]
537pass_if_eq "$func" lsinhelper \
538    "stepping from \"lsinmips16\" back to \"lsinhelper\" ($id)"
539
540# 40
541incr id
542set func [step_through $id [list lsinhelper main]]
543pass_if_eq "$func" main "stepping from \"lsinhelper\" back to \"main\" ($id)"
544