# This testcase is part of GDB, the GNU debugger. # Copyright 2011-2016 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 . if { ![support_displaced_stepping] } { unsupported "displaced stepping" return -1 } set syscall_insn "" # Define the syscall instruction for each target. if { [istarget "i\[34567\]86-*-linux*"] || [istarget "x86_64-*-linux*"] } { set syscall_insn "\[ \t\](int|syscall|sysenter)\[ \t\]" } elseif { [istarget "aarch64*-*-linux*"] || [istarget "arm*-*-linux*"] } { set syscall_insn "\[ \t\](swi|svc)\[ \t\]" } else { return -1 } proc disp_step_cross_syscall { syscall } { with_test_prefix "$syscall" { global syscall_insn global gdb_prompt set testfile "disp-step-$syscall" if [prepare_for_testing ${testfile}.exp ${testfile} ${testfile}.c {debug}] { untested ${testfile}.exp return -1 } if { ![runto main] } then { fail "run to main ($syscall)" return } # Delete the breakpoint on main. gdb_test_no_output "delete break 1" gdb_test "break marker" "Breakpoint.*at.* file .*${testfile}.c, line.*" gdb_test_no_output "set displaced-stepping off" set syscall_bp 0 gdb_test_multiple "break $syscall" "break $syscall" { -re "Breakpoint (\[0-9\]*) at .*$gdb_prompt $" { set syscall_bp $expect_out(1,string) pass "break $syscall" } } gdb_test "continue" "Continuing\\..*Breakpoint \[0-9\]+, (.* in |__libc_|)$syscall \\(\\).*" \ "continue to $syscall (1st time)" # Hit the breakpoint on $syscall for the first time. In this time, we will let PLT # resolution done, and the number single steps we will do later will be # reduced. gdb_test "continue" "Continuing\\..*Breakpoint \[0-9\]+, (.* in |__libc_|)$syscall \\(\\).*" \ "continue to $syscall (2nd time)" # Hit the breakpoint on $syscall for the second time. In this time, the address # of syscall insn and next insn of syscall are recorded. gdb_test "display/i \$pc" ".*" # Single step until we see a syscall insn or we reach the # upper bound of loop iterations. set msg "find syscall insn in $syscall" set steps 0 set max_steps 1000 gdb_test_multiple "stepi" $msg { -re ".*$syscall_insn.*$gdb_prompt $" { pass $msg } -re "x/i .*=>.*\r\n$gdb_prompt $" { incr steps if {$steps == $max_steps} { fail $msg } else { send_gdb "stepi\n" exp_continue } } } if {$steps == $max_steps} { return -1 } set syscall_insn_addr [get_hexadecimal_valueof "\$pc" "0"] if {[gdb_test "stepi" "x/i .*=>.*" "stepi $syscall insn"] != 0} { return -1 } set syscall_insn_next_addr [get_hexadecimal_valueof "\$pc" "0"] gdb_test "continue" "Continuing\\..*Breakpoint \[0-9\]+, (.* in |__libc_|)$syscall \\(\\).*" \ "continue to $syscall (3rd time)" # Hit the breakpoint on $syscall for the third time. In this time, we'll set # breakpoint on the syscall insn we recorded previously, and single step over it. set syscall_insn_bp 0 gdb_test_multiple "break \*$syscall_insn_addr" "break on syscall insn" { -re "Breakpoint (\[0-9\]*) at .*$gdb_prompt $" { set syscall_insn_bp $expect_out(1,string) pass "break on syscall insns" } } gdb_test_no_output "delete $syscall_bp" "delete break $syscall" gdb_test "continue" "Continuing\\..*Breakpoint \[0-9\]+, .*" \ "continue to syscall insn $syscall" gdb_test_no_output "set displaced-stepping on" # Check the address of next instruction of syscall. if {[gdb_test "stepi" "x/i .*=>.*" "single step over $syscall"] != 0} { return -1 } set syscall_insn_next_addr_found [get_hexadecimal_valueof "\$pc" "0"] set test "single step over $syscall final pc" if {$syscall_insn_next_addr != 0 && $syscall_insn_next_addr == $syscall_insn_next_addr_found} { pass $test } else { fail $test } # Delete breakpoint syscall insns to avoid interference to other syscalls. gdb_test_no_output "delete $syscall_insn_bp" "delete break $syscall insn" gdb_test "continue" "Continuing\\..*Breakpoint \[0-9\]+, marker \\(\\) at.*" \ "continue to marker ($syscall)" } } disp_step_cross_syscall "fork" disp_step_cross_syscall "vfork"