# Copyright 2009-2023 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 . # This file is part of the gdb testsuite. # Test amd64 displaced stepping. require is_x86_64_m64_target set newline "\[\r\n\]*" set opts {debug nopie} standard_testfile .S -signal.c if { [prepare_for_testing "failed to prepare" $testfile "$srcfile $srcfile2" $opts] } { return -1 } gdb_test "set displaced-stepping on" "" gdb_test "show displaced-stepping" ".* displaced stepping .* is on.*" if {![runto_main]} { return 0 } ########################################## # Test call/ret. gdb_test "break test_call" \ "Breakpoint.*at.* file .*$srcfile, line.*" gdb_test "break test_call_end" \ "Breakpoint.*at.* file .*$srcfile, line.*" gdb_test "break test_ret" \ "Breakpoint.*at.* file .*$srcfile, line.*" gdb_test "break test_ret_end" \ "Breakpoint.*at.* file .*$srcfile, line.*" gdb_test "continue" \ "Continuing.*Breakpoint.*, test_call ().*" \ "continue to test_call" gdb_test "continue" \ "Continuing.*Breakpoint.*, test_call_end ().*" \ "continue to test_call_end" gdb_test "continue" \ "Continuing.*Breakpoint.*, test_ret ().*" \ "continue to test_ret" gdb_test "continue" \ "Continuing.*Breakpoint.*, test_ret_end ().*" \ "continue to test_ret_end" ########################################## # Test abs-jmp/rep-ret. gdb_test "break test_abs_jmp" \ "Breakpoint.*at.* file .*$srcfile, line.*" gdb_test "break test_abs_jmp_end" \ "Breakpoint.*at.* file .*$srcfile, line.*" gdb_test "break test_rep_ret" \ "Breakpoint.*at.* file .*$srcfile, line.*" gdb_test "break test_rep_ret_end" \ "Breakpoint.*at.* file .*$srcfile, line.*" gdb_test "continue" \ "Continuing.*Breakpoint.*, test_abs_jmp ().*" \ "continue to test_abs_jmp" gdb_test "continue" \ "Continuing.*Breakpoint.*, test_abs_jmp_end ().*" \ "continue to test_abs_jmp_end" gdb_test "continue" \ "Continuing.*Breakpoint.*, test_rep_ret ().*" \ "continue to test_rep_ret" gdb_test "continue" \ "Continuing.*Breakpoint.*, test_rep_ret_end ().*" \ "continue to test_rep_ret_end" ########################################## # Test syscall. gdb_test "break test_syscall" \ "Breakpoint.*at.* file .*$srcfile, line.*" gdb_test "break test_syscall_end" \ "Breakpoint.*at.* file .*$srcfile, line.*" gdb_test "continue" \ "Continuing.*Breakpoint.*, test_syscall ().*" \ "continue to test_syscall" gdb_test "continue" \ "Continuing.*Breakpoint.*, test_syscall_end ().*" \ "continue to test_syscall_end" ########################################## # int3 (with prefixes) # These don't occur in normal code, but gdb should still DTRT. gdb_test "break test_int3" \ "Breakpoint.*at.* file .*$srcfile, line.*" gdb_test "break test_int3_end" \ "Breakpoint.*at.* file .*$srcfile, line.*" gdb_test "continue" \ "Continuing.*Breakpoint.*, test_int3 ().*" \ "continue to test_int3" gdb_test "continue" \ "Continuing.*Breakpoint.*, test_int3_end ().*" \ "continue to test_int3_end" ########################################## # Test rip-relative. # GDB picks a spare register to hold the rip-relative address. # Exercise all the possibilities (rax-rdi, sans rsp). # The order must much the order in srcfile. set rip_regs { "rax" "rbx" "rcx" "rdx" "rbp" "rsi" "rdi" } # Assign val to all specified regs. proc set_regs { regs val } { global gdb_prompt foreach reg ${regs} { # Use send_gdb/gdb_expect so that these aren't logged as pass/fail. send_gdb "set \$${reg} = ${val}\n" gdb_expect 10 { -re "$gdb_prompt $" { verbose "Setting ${reg} to ${val}." 2 } timeout { warning "Couldn't set ${reg} to ${val}." } } } } # Verify all REGS equal VAL, except EXCEPT_REG which equals # EXCEPT_REG_VAL. # # It is fine for EXCEPT_REG to be the empty string, in which case no # register will be checked for EXCEPT_REG_VAL. proc_with_prefix verify_regs { regs val except_reg except_reg_val } { global newline foreach reg ${regs} { set expected ${val} if { "${reg}" == "${except_reg}" } { set expected ${except_reg_val} } # The cast to (int) is because RBP is printed as a pointer. gdb_test "p (int) \$${reg}" " = ${expected}${newline}" "${reg} expected value" } } # Run the rip-relative tests. # # TEST_START_LABEL and TEST_END_LABEL are two labels that delimit the # test in the srcfile. # # REG is either the name of a register which is the destination # location (when testing the add instruction), otherwise REG should be # the empty string, when testing the 'jmpq*' instruction. # # SIGNAL_MODES is a list which always contains 'off' and optionally # might also contain 'on'. The 'on' value is only included if the # target supports sending SIGALRM to the inferior. The test is # repeated for each signal mode. With signal mode 'on' we send a # signal to the inferior while it is performing a displaced step. proc rip_test { reg test_start_label test_end_label signal_modes } { global srcfile rip_regs gdb_test "break ${test_start_label}" \ "Breakpoint.*at.* file .*$srcfile, line.*" gdb_test "break ${test_end_label}" \ "Breakpoint.*at.* file .*$srcfile, line.*" foreach_with_prefix send_signal $signal_modes { if {$send_signal eq [lindex $signal_modes 0]} { # The first time through we can just continue to the # breakpoint. gdb_test "continue" \ "Continuing.*Breakpoint.*, ${test_start_label} ().*" \ "continue to ${test_start_label}" } else { # For the second time through the test we need to jump # back to the beginning. gdb_test "jump ${test_start_label}" \ "Breakpoint.*, ${test_start_label} ().*" \ "jump back to ${test_start_label}" } set_regs ${rip_regs} 0 if {$send_signal} { # The signal sending tests require that the signal appear to # arrive from an outside source, i.e. we can't use GDB's 'signal' # command to deliver it. # # The signal must arrive while GDB is processing the displaced # step instruction. # # If we use 'signal' to send the signal GDB doesn't actually do # the displaced step, but instead just delivers the signal. set inferior_pid [get_inferior_pid] # Ensure that $inferior_pid refers to a single process. gdb_assert {[expr $inferior_pid > 0]} \ "check for a sane inferior pid" if {$inferior_pid > 0} { remote_exec target "kill -ALRM $inferior_pid" } } gdb_test "continue" \ "Continuing.*Breakpoint.*, ${test_end_label} ().*" \ "continue to ${test_end_label}" verify_regs ${rip_regs} 0 ${reg} 42 } } if {![target_info exists gdb,nosignals] && ![istarget "*-*-mingw*"]} { # Only targets that support SIGALRM can run the signal tests. set signal_modes { off on } } else { set signal_modes { off } } # The rip-relative add instructions. There's a test writing to # each register in RIP_REGS in turn. foreach reg ${rip_regs} { with_test_prefix "add into ${reg}" { rip_test $reg "test_rip_${reg}" "test_rip_${reg}_end" $signal_modes } } # Now test the rip-relative 'jmpq*' instruction. with_test_prefix "rip-relative jmpq*" { rip_test "" "test_jmp" "test_jmp_end" $signal_modes } ########################################## # Done, run program to exit. gdb_continue_to_end "amd64-disp-step"