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