@@ -3367,16 +3367,23 @@ aarch64_displaced_step_copy_insn (struct gdbarch *gdbarch,
void
aarch64_displaced_step_fixup (struct gdbarch *gdbarch,
struct displaced_step_copy_insn_closure *dsc_,
- CORE_ADDR from, CORE_ADDR to,
- struct regcache *regs)
+ struct regcache *regs,
+ CORE_ADDR from, CORE_ADDR to, CORE_ADDR pc)
{
+ /* If the PC at which the inferior stopped is still the TO address then
+ the displaced instruction was never executed. All we need to do in
+ this case is restore the program-counter to its original location and
+ we're done. */
+ if (pc == to)
+ {
+ pc = from + (pc - to);
+ regcache_write_pc (regs, pc);
+ return;
+ }
+
aarch64_displaced_step_copy_insn_closure *dsc
= (aarch64_displaced_step_copy_insn_closure *) dsc_;
- ULONGEST pc;
-
- regcache_cooked_read_unsigned (regs, AARCH64_PC_REGNUM, &pc);
-
displaced_debug_printf ("PC after stepping: %s (was %s).",
paddress (gdbarch, pc), paddress (gdbarch, to));
@@ -141,8 +141,9 @@ displaced_step_copy_insn_closure_up
void aarch64_displaced_step_fixup (struct gdbarch *gdbarch,
displaced_step_copy_insn_closure *dsc,
+ struct regcache *regs,
CORE_ADDR from, CORE_ADDR to,
- struct regcache *regs);
+ CORE_ADDR pc);
bool aarch64_displaced_step_hw_singlestep (struct gdbarch *gdbarch);
@@ -1692,9 +1692,11 @@ amd64_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr)
void
amd64_displaced_step_fixup (struct gdbarch *gdbarch,
struct displaced_step_copy_insn_closure *dsc_,
- CORE_ADDR from, CORE_ADDR to,
- struct regcache *regs)
+ struct regcache *regs,
+ CORE_ADDR from, CORE_ADDR to, CORE_ADDR pc)
{
+ bool displaced_step_completed = pc != to;
+
amd64_displaced_step_copy_insn_closure *dsc
= (amd64_displaced_step_copy_insn_closure *) dsc_;
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
@@ -1728,15 +1730,13 @@ amd64_displaced_step_fixup (struct gdbarch *gdbarch,
the displaced instruction; make it relative to the original insn.
Well, signal handler returns don't need relocation either, but we use the
value of %rip to recognize those; see below. */
- if (! amd64_absolute_jmp_p (insn_details)
- && ! amd64_absolute_call_p (insn_details)
- && ! amd64_ret_p (insn_details))
+ if (!displaced_step_completed
+ || (!amd64_absolute_jmp_p (insn_details)
+ && !amd64_absolute_call_p (insn_details)
+ && !amd64_ret_p (insn_details)))
{
- ULONGEST orig_rip;
int insn_len;
- regcache_cooked_read_unsigned (regs, AMD64_RIP_REGNUM, &orig_rip);
-
/* A signal trampoline system call changes the %rip, resuming
execution of the main program after the signal handler has
returned. That makes them like 'return' instructions; we
@@ -1752,24 +1752,24 @@ amd64_displaced_step_fixup (struct gdbarch *gdbarch,
it unrelocated. Goodness help us if there are PC-relative
system calls. */
if (amd64_syscall_p (insn_details, &insn_len)
- && orig_rip != to + insn_len
+ && pc != to + insn_len
/* GDB can get control back after the insn after the syscall.
Presumably this is a kernel bug.
Fixup ensures its a nop, we add one to the length for it. */
- && orig_rip != to + insn_len + 1)
+ && pc != to + insn_len + 1)
displaced_debug_printf ("syscall changed %%rip; not relocating");
else
{
- ULONGEST rip = orig_rip - insn_offset;
+ CORE_ADDR rip = pc - insn_offset;
/* If we just stepped over a breakpoint insn, we don't backup
the pc on purpose; this is to match behaviour without
stepping. */
- regcache_cooked_write_unsigned (regs, AMD64_RIP_REGNUM, rip);
+ regcache_write_pc (regs, rip);
displaced_debug_printf ("relocated %%rip from %s to %s",
- paddress (gdbarch, orig_rip),
+ paddress (gdbarch, pc),
paddress (gdbarch, rip));
}
}
@@ -1782,7 +1782,7 @@ amd64_displaced_step_fixup (struct gdbarch *gdbarch,
/* If the instruction was a call, the return address now atop the
stack is the address following the copied instruction. We need
to make it the address following the original instruction. */
- if (amd64_call_p (insn_details))
+ if (displaced_step_completed && amd64_call_p (insn_details))
{
ULONGEST rsp;
ULONGEST retaddr;
@@ -93,7 +93,7 @@ extern displaced_step_copy_insn_closure_up amd64_displaced_step_copy_insn
struct regcache *regs);
extern void amd64_displaced_step_fixup
(struct gdbarch *gdbarch, displaced_step_copy_insn_closure *closure,
- CORE_ADDR from, CORE_ADDR to, struct regcache *regs);
+ struct regcache *regs, CORE_ADDR from, CORE_ADDR to, CORE_ADDR pc);
/* Initialize the ABI for amd64. Uses DEFAULT_TDESC as fallback
tdesc, if INFO does not specify one. */
@@ -8649,9 +8649,32 @@ arm_displaced_init_closure (struct gdbarch *gdbarch, CORE_ADDR from,
void
arm_displaced_step_fixup (struct gdbarch *gdbarch,
struct displaced_step_copy_insn_closure *dsc_,
- CORE_ADDR from, CORE_ADDR to,
- struct regcache *regs)
+ struct regcache *regs,
+ CORE_ADDR from, CORE_ADDR to, CORE_ADDR pc)
{
+ /* The following block exists as a temporary measure while displaced
+ stepping is fixed architecture at a time within GDB.
+
+ In an earlier implementation of displaced stepping, if GDB thought the
+ displaced instruction had not been executed then this fix up function
+ was never called. As a consequence, state that should be fixed by
+ this function was left in a corrupted state.
+
+ However, it's not as simple as always calling this function; this
+ function needs to be updated to decide what should be fixed up based
+ on whether the function executed or not, which requires each
+ architecture to be considered individually.
+
+ Until that is done for this architecture, this block replicates the
+ code that used to exist in the caller; if the displaced instruction
+ was not executed, fix up the program counter only. */
+ if (pc == to)
+ {
+ pc = from + (pc - to);
+ regcache_write_pc (regs, pc);
+ return;
+ }
+
arm_displaced_step_copy_insn_closure *dsc
= (arm_displaced_step_copy_insn_closure *) dsc_;
@@ -296,7 +296,8 @@ int arm_frame_is_thumb (frame_info_ptr frame);
extern void arm_displaced_step_fixup (struct gdbarch *,
displaced_step_copy_insn_closure *,
- CORE_ADDR, CORE_ADDR, struct regcache *);
+ struct regcache *,
+ CORE_ADDR, CORE_ADDR, CORE_ADDR);
/* Return the bit mask in ARM_PS_REGNUM that indicates Thumb mode. */
extern int arm_psr_thumb_bit (struct gdbarch *);
@@ -263,23 +263,6 @@ write_memory_ptid (ptid_t ptid, CORE_ADDR memaddr,
write_memory (memaddr, myaddr, len);
}
-static bool
-displaced_step_instruction_executed_successfully (gdbarch *arch,
- gdb_signal signal)
-{
- if (signal != GDB_SIGNAL_TRAP)
- return false;
-
- if (target_stopped_by_watchpoint ())
- {
- if (gdbarch_have_nonsteppable_watchpoint (arch)
- || target_have_steppable_watchpoint ())
- return false;
- }
-
- return true;
-}
-
displaced_step_finish_status
displaced_step_buffers::finish (gdbarch *arch, thread_info *thread,
gdb_signal sig)
@@ -327,25 +310,13 @@ displaced_step_buffers::finish (gdbarch *arch, thread_info *thread,
regcache *rc = get_thread_regcache (thread);
- bool instruction_executed_successfully
- = displaced_step_instruction_executed_successfully (arch, sig);
+ CORE_ADDR pc = regcache_read_pc (rc);
+ gdbarch_displaced_step_fixup (arch, copy_insn_closure.get (), rc,
+ buffer->original_pc, buffer->addr, pc);
- if (instruction_executed_successfully)
- {
- gdbarch_displaced_step_fixup (arch, copy_insn_closure.get (),
- buffer->original_pc,
- buffer->addr, rc);
- return DISPLACED_STEP_FINISH_STATUS_OK;
- }
- else
- {
- /* Since the instruction didn't complete, all we can do is relocate the
- PC. */
- CORE_ADDR pc = regcache_read_pc (rc);
- pc = buffer->original_pc + (pc - buffer->addr);
- regcache_write_pc (rc, pc);
- return DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED;
- }
+ return (pc == buffer->addr
+ ? DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED
+ : DISPLACED_STEP_FINISH_STATUS_OK);
}
const displaced_step_copy_insn_closure *
@@ -1826,9 +1826,9 @@ gdbarch_software_single_step routine, and true otherwise.
Method(
comment="""
-Fix up the state resulting from successfully single-stepping a
-displaced instruction, to give the result we would have gotten from
-stepping the instruction in its original location.
+Fix up the state after attempting to single-step a displaced
+instruction, to give the result we would have gotten from stepping the
+instruction in its original location.
REGS is the register state resulting from single-stepping the
displaced instruction.
@@ -1836,20 +1836,28 @@ displaced instruction.
CLOSURE is the result from the matching call to
gdbarch_displaced_step_copy_insn.
-If you provide gdbarch_displaced_step_copy_insn.but not this
-function, then GDB assumes that no fixup is needed after
-single-stepping the instruction.
+FROM is the address where the instruction was original located, TO is
+the address of the displaced buffer where the instruction was copied
+to for stepping, and PC is the address at which the inferior stopped
+after stepping.
For a general explanation of displaced stepping and how GDB uses it,
see the comments in infrun.c.
+
+This function will be called both when the single-step succeeded, and
+in the case where the single-step didn't succeed, for example, if the
+inferior was interrupted by a signal. Within the function it is
+possible to use PC and TO to determine if the instruction was stepped
+or not.
""",
type="void",
name="displaced_step_fixup",
params=[
("struct displaced_step_copy_insn_closure *", "closure"),
+ ("struct regcache *", "regs"),
("CORE_ADDR", "from"),
("CORE_ADDR", "to"),
- ("struct regcache *", "regs"),
+ ("CORE_ADDR", "pc"),
],
predicate=False,
predefault="NULL",
@@ -1068,9 +1068,9 @@ typedef bool (gdbarch_displaced_step_hw_singlestep_ftype) (struct gdbarch *gdbar
extern bool gdbarch_displaced_step_hw_singlestep (struct gdbarch *gdbarch);
extern void set_gdbarch_displaced_step_hw_singlestep (struct gdbarch *gdbarch, gdbarch_displaced_step_hw_singlestep_ftype *displaced_step_hw_singlestep);
-/* Fix up the state resulting from successfully single-stepping a
- displaced instruction, to give the result we would have gotten from
- stepping the instruction in its original location.
+/* Fix up the state after attempting to single-step a displaced
+ instruction, to give the result we would have gotten from stepping the
+ instruction in its original location.
REGS is the register state resulting from single-stepping the
displaced instruction.
@@ -1078,15 +1078,22 @@ extern void set_gdbarch_displaced_step_hw_singlestep (struct gdbarch *gdbarch, g
CLOSURE is the result from the matching call to
gdbarch_displaced_step_copy_insn.
- If you provide gdbarch_displaced_step_copy_insn.but not this
- function, then GDB assumes that no fixup is needed after
- single-stepping the instruction.
+ FROM is the address where the instruction was original located, TO is
+ the address of the displaced buffer where the instruction was copied
+ to for stepping, and PC is the address at which the inferior stopped
+ after stepping.
For a general explanation of displaced stepping and how GDB uses it,
- see the comments in infrun.c. */
+ see the comments in infrun.c.
+
+ This function will be called both when the single-step succeeded, and
+ in the case where the single-step didn't succeed, for example, if the
+ inferior was interrupted by a signal. Within the function it is
+ possible to use PC and TO to determine if the instruction was stepped
+ or not. */
-typedef void (gdbarch_displaced_step_fixup_ftype) (struct gdbarch *gdbarch, struct displaced_step_copy_insn_closure *closure, CORE_ADDR from, CORE_ADDR to, struct regcache *regs);
-extern void gdbarch_displaced_step_fixup (struct gdbarch *gdbarch, struct displaced_step_copy_insn_closure *closure, CORE_ADDR from, CORE_ADDR to, struct regcache *regs);
+typedef void (gdbarch_displaced_step_fixup_ftype) (struct gdbarch *gdbarch, struct displaced_step_copy_insn_closure *closure, struct regcache *regs, CORE_ADDR from, CORE_ADDR to, CORE_ADDR pc);
+extern void gdbarch_displaced_step_fixup (struct gdbarch *gdbarch, struct displaced_step_copy_insn_closure *closure, struct regcache *regs, CORE_ADDR from, CORE_ADDR to, CORE_ADDR pc);
extern void set_gdbarch_displaced_step_fixup (struct gdbarch *gdbarch, gdbarch_displaced_step_fixup_ftype *displaced_step_fixup);
/* Prepare THREAD for it to displaced step the instruction at its current PC.
@@ -4045,13 +4045,13 @@ set_gdbarch_displaced_step_hw_singlestep (struct gdbarch *gdbarch,
}
void
-gdbarch_displaced_step_fixup (struct gdbarch *gdbarch, struct displaced_step_copy_insn_closure *closure, CORE_ADDR from, CORE_ADDR to, struct regcache *regs)
+gdbarch_displaced_step_fixup (struct gdbarch *gdbarch, struct displaced_step_copy_insn_closure *closure, struct regcache *regs, CORE_ADDR from, CORE_ADDR to, CORE_ADDR pc)
{
gdb_assert (gdbarch != NULL);
gdb_assert (gdbarch->displaced_step_fixup != NULL);
if (gdbarch_debug >= 2)
gdb_printf (gdb_stdlog, "gdbarch_displaced_step_fixup called\n");
- gdbarch->displaced_step_fixup (gdbarch, closure, from, to, regs);
+ gdbarch->displaced_step_fixup (gdbarch, closure, regs, from, to, pc);
}
void
@@ -842,9 +842,11 @@ i386_displaced_step_copy_insn (struct gdbarch *gdbarch,
void
i386_displaced_step_fixup (struct gdbarch *gdbarch,
struct displaced_step_copy_insn_closure *closure_,
- CORE_ADDR from, CORE_ADDR to,
- struct regcache *regs)
+ struct regcache *regs,
+ CORE_ADDR from, CORE_ADDR to, CORE_ADDR pc)
{
+ bool displaced_step_completed = pc != to;
+
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
/* The offset we applied to the instruction's address.
@@ -886,15 +888,13 @@ i386_displaced_step_fixup (struct gdbarch *gdbarch,
the displaced instruction; make it relative. Well, signal
handler returns don't need relocation either, but we use the
value of %eip to recognize those; see below. */
- if (! i386_absolute_jmp_p (insn)
- && ! i386_absolute_call_p (insn)
- && ! i386_ret_p (insn))
+ if (!displaced_step_completed
+ || (!i386_absolute_jmp_p (insn)
+ && !i386_absolute_call_p (insn)
+ && !i386_ret_p (insn)))
{
- ULONGEST orig_eip;
int insn_len;
- regcache_cooked_read_unsigned (regs, I386_EIP_REGNUM, &orig_eip);
-
/* A signal trampoline system call changes the %eip, resuming
execution of the main program after the signal handler has
returned. That makes them like 'return' instructions; we
@@ -910,25 +910,25 @@ i386_displaced_step_fixup (struct gdbarch *gdbarch,
it unrelocated. Goodness help us if there are PC-relative
system calls. */
if (i386_syscall_p (insn, &insn_len)
- && orig_eip != to + (insn - insn_start) + insn_len
+ && pc != to + (insn - insn_start) + insn_len
/* GDB can get control back after the insn after the syscall.
Presumably this is a kernel bug.
i386_displaced_step_copy_insn ensures its a nop,
we add one to the length for it. */
- && orig_eip != to + (insn - insn_start) + insn_len + 1)
+ && pc != to + (insn - insn_start) + insn_len + 1)
displaced_debug_printf ("syscall changed %%eip; not relocating");
else
{
- ULONGEST eip = (orig_eip - insn_offset) & 0xffffffffUL;
+ ULONGEST eip = (pc - insn_offset) & 0xffffffffUL;
/* If we just stepped over a breakpoint insn, we don't backup
the pc on purpose; this is to match behaviour without
stepping. */
- regcache_cooked_write_unsigned (regs, I386_EIP_REGNUM, eip);
+ regcache_write_pc (regs, eip);
displaced_debug_printf ("relocated %%eip from %s to %s",
- paddress (gdbarch, orig_eip),
+ paddress (gdbarch, pc),
paddress (gdbarch, eip));
}
}
@@ -941,7 +941,7 @@ i386_displaced_step_fixup (struct gdbarch *gdbarch,
/* If the instruction was a call, the return address now atop the
stack is the address following the copied instruction. We need
to make it the address following the original instruction. */
- if (i386_call_p (insn))
+ if (displaced_step_completed && i386_call_p (insn))
{
ULONGEST esp;
ULONGEST retaddr;
@@ -448,7 +448,7 @@ extern displaced_step_copy_insn_closure_up i386_displaced_step_copy_insn
struct regcache *regs);
extern void i386_displaced_step_fixup
(struct gdbarch *gdbarch, displaced_step_copy_insn_closure *closure,
- CORE_ADDR from, CORE_ADDR to, regcache *regs);
+ regcache *regs, CORE_ADDR from, CORE_ADDR to, CORE_ADDR pc);
/* Initialize a basic ELF architecture variant. */
extern void i386_elf_init_abi (struct gdbarch_info, struct gdbarch *);
@@ -950,9 +950,20 @@ ppc_displaced_step_copy_insn (struct gdbarch *gdbarch,
static void
ppc_displaced_step_fixup (struct gdbarch *gdbarch,
struct displaced_step_copy_insn_closure *closure_,
- CORE_ADDR from, CORE_ADDR to,
- struct regcache *regs)
+ struct regcache *regs,
+ CORE_ADDR from, CORE_ADDR to, CORE_ADDR pc)
{
+ /* If the PC at which the inferior stopped is still the TO address then
+ the displaced instruction was never executed. All we need to do in
+ this case is restore the program-counter to its original location and
+ we're done. */
+ if (pc == to)
+ {
+ pc = from + (pc - to);
+ regcache_write_pc (regs, pc);
+ return;
+ }
+
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
/* Our closure is a copy of the instruction. */
ppc_displaced_step_copy_insn_closure *closure
@@ -481,9 +481,20 @@ s390_displaced_step_copy_insn (struct gdbarch *gdbarch,
static void
s390_displaced_step_fixup (struct gdbarch *gdbarch,
displaced_step_copy_insn_closure *closure_,
- CORE_ADDR from, CORE_ADDR to,
- struct regcache *regs)
+ struct regcache *regs,
+ CORE_ADDR from, CORE_ADDR to, CORE_ADDR pc)
{
+ /* If the PC at which the inferior stopped is still the TO address then
+ the displaced instruction was never executed. All we need to do in
+ this case is restore the program-counter to its original location and
+ we're done. */
+ if (pc == to)
+ {
+ pc = from + (pc - to);
+ regcache_write_pc (regs, pc);
+ return;
+ }
+
/* Our closure is a copy of the instruction. */
s390_displaced_step_copy_insn_closure *closure
= (s390_displaced_step_copy_insn_closure *) closure_;
@@ -496,7 +507,6 @@ s390_displaced_step_fixup (struct gdbarch *gdbarch,
int i2, d2;
/* Get current PC and addressing mode bit. */
- CORE_ADDR pc = regcache_read_pc (regs);
ULONGEST amode = 0;
if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
new file mode 100644
@@ -0,0 +1,30 @@
+/* This file is part of GDB, the GNU debugger.
+
+ Copyright 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 <http://www.gnu.org/licenses/>. */
+
+#include <signal.h>
+#include <stdio.h>
+
+static void
+sigalrm_handler (int sig)
+{
+}
+
+void
+setup_signal_handler ()
+{
+ signal(SIGALRM, sigalrm_handler);
+}
@@ -23,6 +23,10 @@
main:
nop
+ callq setup_signal_handler
+
+ nop
+
/***********************************************/
/* test call/ret */
@@ -135,6 +139,14 @@ test_rip_rdi:
test_rip_rdi_end:
nop
+ .global test_jmp
+test_jmp:
+ jmpq *jmp_dest(%rip)
+ nop
+ .global test_jmp_end
+test_jmp_end:
+ nop
+
/* skip over test data */
jmp done
@@ -142,6 +154,9 @@ test_rip_rdi_end:
answer: .8byte 42
+jmp_dest:
+ .8byte test_jmp_end
+
/***********************************************/
/* all done */
@@ -17,15 +17,16 @@
# Test amd64 displaced stepping.
+load_lib gdb-python.exp
require is_x86_64_m64_target
set newline "\[\r\n\]*"
set opts {debug nopie}
-standard_testfile .S
+standard_testfile .S -signal.c
-if { [prepare_for_testing "failed to prepare" $testfile $srcfile $opts] } {
+if { [prepare_for_testing "failed to prepare" $testfile "$srcfile $srcfile2" $opts] } {
return -1
}
@@ -154,9 +155,13 @@ proc set_regs { regs val } {
}
}
-# Verify all REGS equal VAL, except REG which equals REG_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 verify_regs { test_name regs val except_reg except_reg_val } {
+proc_with_prefix verify_regs { regs val except_reg except_reg_val } {
global newline
foreach reg ${regs} {
@@ -165,36 +170,101 @@ proc verify_regs { test_name regs val except_reg except_reg_val } {
set expected ${except_reg_val}
}
# The cast to (int) is because RBP is printed as a pointer.
- gdb_test "p (int) \$${reg}" " = ${expected}${newline}" "${test_name} ${reg} expected value"
+ gdb_test "p (int) \$${reg}" " = ${expected}${newline}" "${reg} expected value"
}
}
-proc rip_test { reg } {
+# 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 destiation
+# 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 GDB
+# supports Python. The test is repeated for each signal mode. With
+# signal mode 'on' GDB uses Python to have a signal sent to the
+# inferior.
+proc rip_test { reg test_start_label test_end_label signal_modes } {
global srcfile rip_regs
- set test_start_label "test_rip_${reg}"
- set test_end_label "test_rip_${reg}_end"
-
gdb_test "break ${test_start_label}" \
"Breakpoint.*at.* file .*$srcfile, line.*"
gdb_test "break ${test_end_label}" \
"Breakpoint.*at.* file .*$srcfile, line.*"
- gdb_test "continue" \
- "Continuing.*Breakpoint.*, ${test_start_label} ().*" \
- "continue to ${test_start_label}"
+ 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
- set_regs ${rip_regs} 0
+ if {$send_signal} {
+ gdb_test_no_output "python signal_inferior()" \
+ "send signal"
+ }
+
+ gdb_test "continue" \
+ "Continuing.*Breakpoint.*, ${test_end_label} ().*" \
+ "continue to ${test_end_label}"
- gdb_test "continue" \
- "Continuing.*Breakpoint.*, ${test_end_label} ().*" \
- "continue to ${test_end_label}"
+ verify_regs ${rip_regs} 0 ${reg} 42
+ }
+}
- verify_regs "test rip w/${reg}" ${rip_regs} 0 ${reg} 42
+if {[allow_python_tests] && ![is_remote target]} {
+ # 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.
+ #
+ # By having Python ask the OS to deliver us a signal we will
+ # (hopefully) see the signal while processing the displaced step
+ # instruction.
+ #
+ # Obviously non of this will work if the target is remote.
+ gdb_test_multiline "Create function to send SIGALRM" \
+ "python" "" \
+ "import os, signal" "" \
+ "def signal_inferior():" "" \
+ " os.kill(gdb.selected_inferior().pid, signal.SIGALRM)" "" \
+ "end" ""
+
+ set signal_modes { off on }
+} else {
+ set signal_modes { off }
}
+# The the rip-relative add instructions. There's a test writing to
+# each register in RIP_REGS in turn.
foreach reg ${rip_regs} {
- rip_test $reg
+ 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
}
##########################################