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