Index: gdb/testsuite/gdb.arch/mips16-thunks.exp |
diff --git a/gdb/testsuite/gdb.arch/mips16-thunks.exp b/gdb/testsuite/gdb.arch/mips16-thunks.exp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..79c079ae787ab50a39e999097f0094af814d030a |
--- /dev/null |
+++ b/gdb/testsuite/gdb.arch/mips16-thunks.exp |
@@ -0,0 +1,543 @@ |
+# Copyright 2012 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/>. |
+ |
+# Contributed by Mentor Graphics, written by Maciej W. Rozycki. |
+ |
+# Test MIPS16 thunk support. |
+ |
+# This should work on any targets that support MIPS16 execution, including |
+# Linux and bare-iron ones, but not all of them do, for example MIPS16 |
+# support has been added to Linux relatively late in the game. Also besides |
+# environment support, the target processor has to support the MIPS16 ASE. |
+# Finally as of this writing MIPS16 support has only been implemented in the |
+# toolchain for a subset of ABIs, so we need to check that a MIPS16 |
+# executable can be built and run at all before we attempt the actual test. |
+ |
+if { ![istarget "mips*-*-*"] } then { |
+ verbose "Skipping MIPS16 thunk support tests." |
+ return |
+} |
+ |
+# A helper to set caller's SRCFILE and OBJFILE based on FILENAME and SUFFIX. |
+proc set_src_and_obj { filename { suffix "" } } { |
+ upvar srcfile srcfile |
+ upvar objfile objfile |
+ global srcdir |
+ global objdir |
+ global subdir |
+ |
+ if ![string equal "$suffix" ""] then { |
+ set suffix "-$suffix" |
+ } |
+ set srcfile ${srcdir}/${subdir}/${filename}.c |
+ set objfile ${objdir}/${subdir}/${filename}${suffix}.o |
+} |
+ |
+# First check if a trivial MIPS16 program can be built and debugged. This |
+# verifies environment and processor support, any failure here must be |
+# classed as the lack of support. |
+set testname mips16-thunks-main |
+ |
+set_src_and_obj mips16-thunks-inmain |
+set options [list debug nowarnings additional_flags=-mips16] |
+set objfiles ${objfile} |
+gdb_compile ${srcfile} ${objfile} object ${options} |
+ |
+set_src_and_obj mips16-thunks-main |
+set options [list debug nowarnings additional_flags=-mips16] |
+lappend objfiles ${objfile} |
+gdb_compile ${srcfile} ${objfile} object ${options} |
+ |
+set binfile ${objdir}/${subdir}/${testname} |
+set options [list debug nowarnings] |
+if { [gdb_compile ${objfiles} ${binfile} executable ${options}] != "" } then { |
+ unsupported "No MIPS16 support in the toolchain." |
+ return |
+} |
+clean_restart ${testname} |
+gdb_breakpoint inmain |
+gdb_run_cmd |
+gdb_test_multiple "" "check for MIPS16 support in the processor" { |
+ -re "Breakpoint 1.*inmain .*$gdb_prompt $" { |
+ gdb_test_multiple "finish" \ |
+ "check for MIPS16 support in the processor" { |
+ -re "Value returned is \\\$\[0-9\]+ = 0\[^0-9\].*$gdb_prompt $" { |
+ verbose "MIPS16 support check successful." |
+ } |
+ -re "$gdb_prompt $" { |
+ unsupported "No MIPS16 support in the processor." |
+ return |
+ } |
+ default { |
+ unsupported "No MIPS16 support in the processor." |
+ return |
+ } |
+ } |
+ } |
+ -re "$gdb_prompt $" { |
+ unsupported "No MIPS16 support in the processor." |
+ return |
+ } |
+ default { |
+ unsupported "No MIPS16 support in the processor." |
+ return |
+ } |
+} |
+ |
+# Check if MIPS16 PIC code can be built and debugged. We want to check |
+# PIC and MIPS16 thunks are handled correctly together if possible, but |
+# on targets that do not support PIC code, e.g. bare iron, we still want |
+# to test the rest of functionality. |
+set testname mips16-thunks-pic |
+set picflag "" |
+ |
+set_src_and_obj mips16-thunks-inmain pic |
+set options [list \ |
+ debug nowarnings additional_flags=-mips16 additional_flags=-fPIC] |
+set objfiles ${objfile} |
+gdb_compile ${srcfile} ${objfile} object ${options} |
+ |
+set_src_and_obj mips16-thunks-main pic |
+set options [list \ |
+ debug nowarnings additional_flags=-mips16 additional_flags=-fPIC] |
+lappend objfiles ${objfile} |
+gdb_compile ${srcfile} ${objfile} object ${options} |
+ |
+set binfile ${objdir}/${subdir}/${testname} |
+set options [list debug nowarnings additional_flags=-fPIC] |
+if { [gdb_compile ${objfiles} ${binfile} executable ${options}] == "" } then { |
+ clean_restart ${testname} |
+ gdb_breakpoint inmain |
+ gdb_run_cmd |
+ gdb_test_multiple "" "check for PIC support" { |
+ -re "Breakpoint 1.*inmain .*$gdb_prompt $" { |
+ note "PIC support present, will make additional PIC thunk checks." |
+ set picflag additional_flags=-fPIC |
+ } |
+ -re "$gdb_prompt $" { |
+ note "No PIC support, skipping additional PIC thunk checks." |
+ } |
+ default { |
+ note "No PIC support, skipping additional PIC thunk checks." |
+ } |
+ } |
+} else { |
+ note "No PIC support, skipping additional PIC thunk checks." |
+} |
+ |
+# OK, build the twisted executable. This program contains the following |
+# MIPS16 thunks: |
+# - __call_stub_fp_sin, |
+# - __call_stub_fp_sinblah, |
+# - __call_stub_fp_sinfrob, |
+# - __call_stub_fp_sinhelper, |
+# - __call_stub_lsinhelper, |
+# - __fn_stub_lsinmips16, |
+# - __fn_stub_sinblah16, |
+# - __fn_stub_sinfrob16, |
+# - __fn_stub_sinmips16, |
+# - __mips16_call_stub_df_2, |
+# - __mips16_ret_df. |
+# Additionally, if PIC code is supported, it contains the following PIC thunks: |
+# - .pic.__mips16_call_stub_df_2, |
+# - .pic.__mips16_ret_df, |
+# - .pic.sinblah, |
+# - .pic.sinblah16, |
+# - .pic.sinfrob, |
+# - .pic.sinfrob16. |
+set testname mips16-thunks-sin |
+ |
+set_src_and_obj mips16-thunks-sinmain |
+set options [list debug nowarnings additional_flags=-mips16] |
+set objfiles ${objfile} |
+gdb_compile ${srcfile} ${objfile} object ${options} |
+ |
+set_src_and_obj mips16-thunks-sin |
+set options [list debug nowarnings additional_flags=-mno-mips16] |
+lappend objfiles ${objfile} |
+gdb_compile ${srcfile} ${objfile} object ${options} |
+ |
+set_src_and_obj mips16-thunks-sinmips16 |
+set options [list debug nowarnings additional_flags=-mips16] |
+lappend objfiles ${objfile} |
+gdb_compile ${srcfile} ${objfile} object ${options} |
+ |
+set_src_and_obj mips16-thunks-sinfrob |
+set options [list \ |
+ debug nowarnings additional_flags=-mno-mips16 ${picflag}] |
+lappend objfiles ${objfile} |
+gdb_compile ${srcfile} ${objfile} object ${options} |
+ |
+set_src_and_obj mips16-thunks-sinfrob16 |
+set options [list \ |
+ debug nowarnings additional_flags=-mips16 ${picflag}] |
+lappend objfiles ${objfile} |
+gdb_compile ${srcfile} ${objfile} object ${options} |
+ |
+set binfile ${objdir}/${subdir}/${testname} |
+set options [list debug nowarnings] |
+gdb_compile ${objfiles} ${binfile} executable ${options} |
+clean_restart ${testname} |
+if ![runto_main] then { |
+ fail "running test program, MIPS16 thunk tests aborted" |
+ return |
+} |
+ |
+# Build some useful regular expressions out of a list of functions FUNCS |
+# to be used to match against backtraces. |
+proc build_frames_re { funcs } { |
+ upvar anyframe anyframe |
+ upvar frames frames |
+ upvar frame frame |
+ upvar func func |
+ |
+ set fid 0 |
+ set argsandsource " +\\\(.*\\\) +at +\[^\r\n\]+\r\n" |
+ set addrin "(?:\[^ \]+ +in +)?" |
+ set anyframe "#${fid} +${addrin}(\[^ \]+)${argsandsource}" |
+ set frame "#${fid} +${addrin}${func}${argsandsource}" |
+ set frames "$frame" |
+ foreach f [lrange $funcs 1 end] { |
+ incr fid |
+ append frames "#${fid} +${addrin}${f}${argsandsource}" |
+ } |
+} |
+ |
+# Single-step through the function that is at the head of function list |
+# FUNCS until a different function (frame) is reached. Before each step |
+# check the backtrace against FUNCS. ID is used for reporting, to tell |
+# apart different calls to this procedure for the same function. If |
+# successful, then return the name of the function we have stopped in. |
+proc step_through { id funcs } { |
+ global gdb_prompt |
+ |
+ set func [lindex $funcs 0] |
+ build_frames_re "$funcs" |
+ |
+ set msg "single-stepping through \"${func}\" ($id)" |
+ |
+ # Arbitrarily limit the maximium number of steps made to avoid looping |
+ # indefinitely in the case something goes wrong, increase as (if) |
+ # necessary. |
+ set count 8 |
+ while { $count > 0 } { |
+ if { [gdb_test_multiple "backtrace" "$msg (backtrace)" { |
+ -re "${frames}$gdb_prompt $" { |
+ if { [gdb_test_multiple "step" "$msg (step)" { |
+ -re "$gdb_prompt $" { |
+ if { [gdb_test_multiple "frame" "$msg (frame)" { |
+ -re "${frame}.*$gdb_prompt $" { |
+ } |
+ -re "${anyframe}.*$gdb_prompt $" { |
+ pass "$msg" |
+ return $expect_out(1,string) |
+ } |
+ }] != 0 } then { |
+ return "" |
+ } |
+ } |
+ }] != 0 } then { |
+ return "" |
+ } |
+ } |
+ }] != 0 } then { |
+ return "" |
+ } |
+ incr count -1 |
+ } |
+ fail "$msg (too many steps)" |
+ return "" |
+} |
+ |
+# Finish the current function that must be one that is at the head of |
+# function list FUNCS. Before that check the backtrace against FUNCS. |
+# ID is used for reporting, to tell apart different calls to this |
+# procedure for the same function. If successful, then return the name |
+# of the function we have stopped in. |
+proc finish_through { id funcs } { |
+ global gdb_prompt |
+ |
+ set func [lindex $funcs 0] |
+ build_frames_re "$funcs" |
+ |
+ set msg "finishing \"${func}\" ($id)" |
+ |
+ gdb_test_multiple "backtrace" "$msg (backtrace)" { |
+ -re "${frames}$gdb_prompt $" { |
+ gdb_test_multiple "finish" "$msg (finish)" { |
+ -re "Run till exit from ${frame}.*$gdb_prompt $" { |
+ gdb_test_multiple "frame" "$msg (frame)" { |
+ -re "${anyframe}.*$gdb_prompt $" { |
+ pass "$msg" |
+ return $expect_out(1,string) |
+ } |
+ } |
+ } |
+ } |
+ } |
+ } |
+ return "" |
+} |
+ |
+# Report PASS if VAL is equal to EXP, otherwise report FAIL, using MSG. |
+proc pass_if_eq { val exp msg } { |
+ if [string equal "$val" "$exp"] then { |
+ pass "$msg" |
+ } else { |
+ fail "$msg" |
+ } |
+} |
+ |
+# Check if FUNC is equal to WANT. If not, then assume that we have stepped |
+# into a library call. In this case finish it, then step out of the caller. |
+# ID is used for reporting, to tell apart different calls to this procedure |
+# for the same function. If successful, then return the name of the |
+# function we have stopped in. |
+proc finish_if_ne { id func want funcs } { |
+ if ![string equal "$func" "$want"] then { |
+ set call "$func" |
+ set want [lindex $funcs 0] |
+ set func [finish_through "$id" [linsert $funcs 0 "$func"]] |
+ pass_if_eq "$func" "$want" "\"${call}\" finishing to \"${want}\" ($id)" |
+ set func [step_through "$id" $funcs] |
+ } |
+ return "$func" |
+} |
+ |
+# Now single-step through the program, making sure all thunks are correctly |
+# stepped over and omitted from backtraces. |
+ |
+set id 1 |
+set func [step_through $id [list main]] |
+pass_if_eq "$func" sinfrob16 "stepping from \"main\" into \"sinfrob16\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinfrob16 main]] |
+set func [finish_if_ne $id "$func" main [list sinfrob16 main]] |
+pass_if_eq "$func" main "stepping from \"sinfrob16\" back to \"main\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list main]] |
+pass_if_eq "$func" sinfrob "stepping from \"main\" into \"sinfrob\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinfrob main]] |
+set func [finish_if_ne $id "$func" main [list sinfrob main]] |
+pass_if_eq "$func" main "stepping from \"sinfrob\" back to \"main\" ($id)" |
+ |
+# 5 |
+incr id |
+set func [step_through $id [list main]] |
+pass_if_eq "$func" sinhelper "stepping from \"main\" into \"sinhelper\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinhelper main]] |
+set func [finish_if_ne $id "$func" sinfrob16 [list sinhelper main]] |
+pass_if_eq "$func" sinfrob16 \ |
+ "stepping from \"sinhelper\" into \"sinfrob16\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinfrob16 sinhelper main]] |
+set func [finish_if_ne $id "$func" sinhelper [list sinfrob16 sinhelper main]] |
+pass_if_eq "$func" sinhelper \ |
+ "stepping from \"sinfrob16\" back to \"sinhelper\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinhelper main]] |
+pass_if_eq "$func" sinfrob "stepping from \"sinhelper\" into \"sinfrob\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinfrob sinhelper main]] |
+set func [finish_if_ne $id "$func" sinhelper [list sinfrob sinhelper main]] |
+pass_if_eq "$func" sinhelper \ |
+ "stepping from \"sinfrob\" back to \"sinhelper\" ($id)" |
+ |
+# 10 |
+incr id |
+set func [step_through $id [list sinhelper main]] |
+pass_if_eq "$func" sinmips16 \ |
+ "stepping from \"sinhelper\" into \"sinmips16\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinmips16 sinhelper main]] |
+set func [finish_if_ne $id "$func" sinfrob16 [list sinmips16 sinhelper main]] |
+pass_if_eq "$func" sinfrob16 \ |
+ "stepping from \"sinmips16\" into \"sinfrob16\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinfrob16 sinmips16 sinhelper main]] |
+set func [finish_if_ne $id "$func" sinmips16 \ |
+ [list sinfrob16 sinmips16 sinhelper main]] |
+pass_if_eq "$func" sinmips16 \ |
+ "stepping from \"sinfrob16\" back to \"sinmips16\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinmips16 sinhelper main]] |
+pass_if_eq "$func" sinfrob "stepping from \"sinmips16\" into \"sinfrob\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinfrob sinmips16 sinhelper main]] |
+set func [finish_if_ne $id "$func" sinhelper \ |
+ [list sinfrob sinmips16 sinhelper main]] |
+pass_if_eq "$func" sinmips16 \ |
+ "stepping from \"sinfrob\" back to \"sinmips16\" ($id)" |
+ |
+# 15 |
+incr id |
+set func [step_through $id [list sinmips16 sinhelper main]] |
+pass_if_eq "$func" sinfrob16 \ |
+ "stepping from \"sinmips16\" into \"sinfrob16\" (indirectly) ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinfrob16 sinmips16 sinhelper main]] |
+set func [finish_if_ne $id "$func" sinmips16 \ |
+ [list sinfrob16 sinmips16 sinhelper main]] |
+pass_if_eq "$func" sinmips16 \ |
+ "stepping from \"sinfrob16\" back to \"sinmips16\" (indirectly) ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinmips16 sinhelper main]] |
+pass_if_eq "$func" sinfrob \ |
+ "stepping from \"sinmips16\" into \"sinfrob\" (indirectly) ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinfrob sinmips16 sinhelper main]] |
+set func [finish_if_ne $id "$func" sinhelper \ |
+ [list sinfrob sinmips16 sinhelper main]] |
+pass_if_eq "$func" sinmips16 \ |
+ "stepping from \"sinfrob\" back to \"sinmips16\" (indirectly) ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinmips16 sinhelper main]] |
+pass_if_eq "$func" sinhelper \ |
+ "stepping from \"sinmips16\" back to \"sinhelper\" ($id)" |
+ |
+# 20 |
+incr id |
+set func [step_through $id [list sinhelper main]] |
+pass_if_eq "$func" main "stepping from \"sinhelper\" back to \"main\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list main]] |
+pass_if_eq "$func" sinblah "stepping from \"main\" into \"sinblah\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinblah main]] |
+set func [finish_if_ne $id "$func" main [list sinblah main]] |
+pass_if_eq "$func" main "stepping from \"sinblah\" back to \"main\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list main]] |
+pass_if_eq "$func" sinblah16 "stepping from \"main\" into \"sinblah16\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinblah16 main]] |
+set func [finish_if_ne $id "$func" main [list sinblah16 main]] |
+pass_if_eq "$func" main "stepping from \"sinblah16\" back to \"main\" ($id)" |
+ |
+# 25 |
+incr id |
+set func [step_through $id [list main]] |
+pass_if_eq "$func" lsinhelper \ |
+ "stepping from \"main\" into \"lsinhelper\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list lsinhelper main]] |
+set func [finish_if_ne $id "$func" sinblah [list lsinhelper main]] |
+pass_if_eq "$func" sinblah \ |
+ "stepping from \"lsinhelper\" into \"sinblah\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinblah lsinhelper main]] |
+set func [finish_if_ne $id "$func" lsinhelper [list sinblah lsinhelper main]] |
+pass_if_eq "$func" lsinhelper \ |
+ "stepping from \"sinblah\" back to \"lsinhelper\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list lsinhelper main]] |
+pass_if_eq "$func" sinblah16 \ |
+ "stepping from \"lsinhelper\" into \"sinblah16\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinblah16 lsinhelper main]] |
+set func [finish_if_ne $id "$func" lsinhelper [list sinblah16 lsinhelper main]] |
+pass_if_eq "$func" lsinhelper \ |
+ "stepping from \"sinblah16\" back to \"lsinhelper\" ($id)" |
+ |
+# 30 |
+incr id |
+set func [step_through $id [list lsinhelper main]] |
+pass_if_eq "$func" lsinmips16 \ |
+ "stepping from \"lsinhelper\" into \"lsinmips16\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list lsinmips16 lsinhelper main]] |
+set func [finish_if_ne $id "$func" sinblah [list lsinmips16 lsinhelper main]] |
+pass_if_eq "$func" sinblah \ |
+ "stepping from \"lsinmips16\" into \"sinblah\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinblah lsinmips16 lsinhelper main]] |
+set func [finish_if_ne $id "$func" lsinmips16 \ |
+ [list sinblah lsinmips16 lsinhelper main]] |
+pass_if_eq "$func" lsinmips16 \ |
+ "stepping from \"sinblah\" back to \"lsinmips16\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list lsinmips16 lsinhelper main]] |
+pass_if_eq "$func" sinblah16 \ |
+ "stepping from \"lsinmips16\" into \"sinblah16\" ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinblah16 lsinmips16 lsinhelper main]] |
+set func [finish_if_ne $id "$func" lsinhelper \ |
+ [list sinblah16 lsinmips16 lsinhelper main]] |
+pass_if_eq "$func" lsinmips16 \ |
+ "stepping from \"sinblah16\" back to \"lsinmips16\" ($id)" |
+ |
+# 35 |
+incr id |
+set func [step_through $id [list lsinmips16 lsinhelper main]] |
+pass_if_eq "$func" sinblah \ |
+ "stepping from \"lsinmips16\" into \"sinblah\" (indirectly) ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinblah lsinmips16 lsinhelper main]] |
+set func [finish_if_ne $id "$func" lsinmips16 \ |
+ [list sinblah lsinmips16 lsinhelper main]] |
+pass_if_eq "$func" lsinmips16 \ |
+ "stepping from \"sinblah\" back to \"lsinmips16\" (indirectly) ($id)" |
+ |
+incr id |
+set func [step_through $id [list lsinmips16 lsinhelper main]] |
+pass_if_eq "$func" sinblah16 \ |
+ "stepping from \"lsinmips16\" into \"sinblah16\" (indirectly) ($id)" |
+ |
+incr id |
+set func [step_through $id [list sinblah16 lsinmips16 lsinhelper main]] |
+set func [finish_if_ne $id "$func" lsinhelper \ |
+ [list sinblah16 lsinmips16 lsinhelper main]] |
+pass_if_eq "$func" lsinmips16 \ |
+ "stepping from \"sinblah16\" back to \"lsinmips16\" (indirectly) ($id)" |
+ |
+incr id |
+set func [step_through $id [list lsinmips16 lsinhelper main]] |
+pass_if_eq "$func" lsinhelper \ |
+ "stepping from \"lsinmips16\" back to \"lsinhelper\" ($id)" |
+ |
+# 40 |
+incr id |
+set func [step_through $id [list lsinhelper main]] |
+pass_if_eq "$func" main "stepping from \"lsinhelper\" back to \"main\" ($id)" |