diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index d11d8320799..acd69ed6a6c 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -3371,14 +3371,21 @@ 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, bool completed_p)
 {
-  aarch64_displaced_step_copy_insn_closure *dsc
-    = (aarch64_displaced_step_copy_insn_closure *) dsc_;
+  CORE_ADDR pc = regcache_read_pc (regs);
 
-  ULONGEST pc;
+  /* If the displaced instruction didn't complete successfully then all we
+     need to do is restore the program counter.  */
+  if (!completed_p)
+    {
+      pc = from + (pc - to);
+      regcache_write_pc (regs, pc);
+      return;
+    }
 
-  regcache_cooked_read_unsigned (regs, AARCH64_PC_REGNUM, &pc);
+  aarch64_displaced_step_copy_insn_closure *dsc
+    = (aarch64_displaced_step_copy_insn_closure *) dsc_;
 
   displaced_debug_printf ("PC after stepping: %s (was %s).",
 			  paddress (gdbarch, pc), paddress (gdbarch, to));
diff --git a/gdb/aarch64-tdep.h b/gdb/aarch64-tdep.h
index ae38327ffab..505e050ba48 100644
--- a/gdb/aarch64-tdep.h
+++ b/gdb/aarch64-tdep.h
@@ -142,7 +142,7 @@ displaced_step_copy_insn_closure_up
 void aarch64_displaced_step_fixup (struct gdbarch *gdbarch,
 				   displaced_step_copy_insn_closure *dsc,
 				   CORE_ADDR from, CORE_ADDR to,
-				   struct regcache *regs);
+				   struct regcache *regs, bool completed_p);
 
 bool aarch64_displaced_step_hw_singlestep (struct gdbarch *gdbarch);
 
diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index 228b7518cb0..8d345257e12 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -1690,7 +1690,7 @@ 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, bool completed_p)
 {
   amd64_displaced_step_copy_insn_closure *dsc
     = (amd64_displaced_step_copy_insn_closure *) dsc_;
@@ -1725,14 +1725,14 @@ 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 (!completed_p
+      || (!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);
+      CORE_ADDR pc = regcache_read_pc (regs);
 
       /* A signal trampoline system call changes the %rip, resuming
 	 execution of the main program after the signal handler has
@@ -1749,24 +1749,23 @@ 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
 	  /* 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)
+	     Presumably this is a kernel bug.  Fixup ensures its a nop, we
+	     add one to the length for it.  */
+	  && (pc < to || 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));
 	}
     }
@@ -1779,7 +1778,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 (completed_p && amd64_call_p (insn_details))
     {
       ULONGEST rsp;
       ULONGEST retaddr;
diff --git a/gdb/amd64-tdep.h b/gdb/amd64-tdep.h
index 929b4b8bdc5..31bf7f2f96f 100644
--- a/gdb/amd64-tdep.h
+++ b/gdb/amd64-tdep.h
@@ -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);
+   CORE_ADDR from, CORE_ADDR to, struct regcache *regs, bool completed_p);
 
 /* Initialize the ABI for amd64.  Uses DEFAULT_TDESC as fallback
    tdesc, if INFO does not specify one.  */
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 803596d0fe6..5ba6c8bd867 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -8651,8 +8651,32 @@ 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, bool completed_p)
 {
+  /* 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, things that should be fixed by
+     this function were left in an unfixed 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 displaced step executed or not, which requires each
+     architecture to be considered individually.
+
+     Until this architecture is updated, this block replicates the old
+     behaviour; we just restore the program counter register, and leave
+     everything else unfixed.  */
+  if (!completed_p)
+    {
+      CORE_ADDR pc = regcache_read_pc (regs);
+      pc = from + (pc - to);
+      regcache_write_pc (regs, pc);
+      return;
+    }
+
   arm_displaced_step_copy_insn_closure *dsc
     = (arm_displaced_step_copy_insn_closure *) dsc_;
 
diff --git a/gdb/arm-tdep.h b/gdb/arm-tdep.h
index a8d21c44ba4..cb0e8ce959b 100644
--- a/gdb/arm-tdep.h
+++ b/gdb/arm-tdep.h
@@ -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 *);
+				      CORE_ADDR, CORE_ADDR,
+				      struct regcache *, bool);
 
 /* Return the bit mask in ARM_PS_REGNUM that indicates Thumb mode.  */
 extern int arm_psr_thumb_bit (struct gdbarch *);
diff --git a/gdb/displaced-stepping.c b/gdb/displaced-stepping.c
index 534e031a88e..466b61230c1 100644
--- a/gdb/displaced-stepping.c
+++ b/gdb/displaced-stepping.c
@@ -257,22 +257,13 @@ displaced_step_buffers::finish (gdbarch *arch, thread_info *thread,
   bool instruction_executed_successfully
     = displaced_step_instruction_executed_successfully (arch, sig);
 
-  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;
-    }
+  gdbarch_displaced_step_fixup (arch, copy_insn_closure.get (),
+				buffer->original_pc, buffer->addr,
+				rc, instruction_executed_successfully);
+
+  return (instruction_executed_successfully
+	  ? DISPLACED_STEP_FINISH_STATUS_OK
+	  : DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED);
 }
 
 const displaced_step_copy_insn_closure *
diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
index a3fc0b9272b..b4d3beeaba2 100644
--- a/gdb/gdbarch-gen.h
+++ b/gdb/gdbarch-gen.h
@@ -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, CORE_ADDR from, CORE_ADDR to, struct regcache *regs, bool completed_p);
+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, bool completed_p);
 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.
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index b676e346fd0..7b40499b533 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -4057,13 +4057,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, CORE_ADDR from, CORE_ADDR to, struct regcache *regs, bool completed_p)
 {
   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, from, to, regs, completed_p);
 }
 
 void
diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
index 2b1a2b4f602..8165cb5df62 100644
--- a/gdb/gdbarch_components.py
+++ b/gdb/gdbarch_components.py
@@ -1771,9 +1771,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.
@@ -1781,9 +1781,18 @@ 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.
+
+COMPLETED_P is true if GDB stopped as a result of the requested step
+having completed (e.g. the inferior stopped with SIGTRAP), otherwise
+COMPLETED_P is false and GDB stopped for some other reason.  In the
+case where a single instruction is expanded to multiple replacement
+instructions for stepping then it may be necessary to read the current
+program counter from REGS in order to decide how far through the
+series of replacement instructions the inferior got before stopping,
+this may impact what will need fixing up in this function.
 
 For a general explanation of displaced stepping and how GDB uses it,
 see the comments in infrun.c.
@@ -1795,6 +1804,7 @@ see the comments in infrun.c.
         ("CORE_ADDR", "from"),
         ("CORE_ADDR", "to"),
         ("struct regcache *", "regs"),
+        ("bool", "completed_p")
     ],
     predicate=False,
     predefault="NULL",
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index e93479c35a3..1ab9fc0e87d 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -843,7 +843,7 @@ 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, bool completed_p)
 {
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
 
@@ -886,14 +886,14 @@ 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 (!completed_p
+      || (!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);
+      CORE_ADDR pc = regcache_read_pc (regs);
 
       /* A signal trampoline system call changes the %eip, resuming
 	 execution of the main program after the signal handler has
@@ -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 (completed_p && i386_call_p (insn))
     {
       ULONGEST esp;
       ULONGEST retaddr;
diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h
index 371bce72369..642ac89b240 100644
--- a/gdb/i386-tdep.h
+++ b/gdb/i386-tdep.h
@@ -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);
+   CORE_ADDR from, CORE_ADDR to, regcache *regs, bool completed_p);
 
 /* Initialize a basic ELF architecture variant.  */
 extern void i386_elf_init_abi (struct gdbarch_info, struct gdbarch *);
diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index 8e37c3d5183..d7464a9d814 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -952,8 +952,18 @@ 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, bool completed_p)
 {
+  /* If the displaced instruction didn't complete successfully then all we
+     need to do is restore the program counter.  */
+  if (!completed_p)
+    {
+      CORE_ADDR pc = regcache_read_pc (regs);
+      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
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
index 081a8b68867..d2d2f7e4afb 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -482,8 +482,19 @@ 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, bool completed_p)
 {
+  CORE_ADDR pc = regcache_read_pc (regs);
+
+  /* If the displaced instruction didn't complete successfully then all we
+     need to do is restore the program counter.  */
+  if (!completed_p)
+    {
+      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)
diff --git a/gdb/testsuite/gdb.arch/amd64-disp-step-self-call-alarm.c b/gdb/testsuite/gdb.arch/amd64-disp-step-self-call-alarm.c
new file mode 100644
index 00000000000..aec3d294b15
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-disp-step-self-call-alarm.c
@@ -0,0 +1,24 @@
+/* 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 <unistd.h>
+
+void
+setup_alarm (void)
+{
+  alarm (300);
+}
diff --git a/gdb/testsuite/gdb.arch/amd64-disp-step-self-call.S b/gdb/testsuite/gdb.arch/amd64-disp-step-self-call.S
new file mode 100644
index 00000000000..a227ba21917
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-disp-step-self-call.S
@@ -0,0 +1,50 @@
+/* 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 <http://www.gnu.org/licenses/>.
+
+   This file is part of the gdb testsuite.
+   It tests displaced stepping over various insns that require special
+   handling.  */
+
+	.text
+
+	.global main
+main:
+	nop
+
+	callq	setup_alarm
+
+	nop
+
+/***********************************************/
+
+/* test call/ret */
+
+	.global test_call
+test_call:
+	call test_call
+	nop
+	.global test_ret_end
+test_ret_end:
+	nop
+
+/***********************************************/
+
+/* all done */
+
+done:
+	mov $0,%rdi
+	call exit
+	hlt
+
diff --git a/gdb/testsuite/gdb.arch/amd64-disp-step-self-call.exp b/gdb/testsuite/gdb.arch/amd64-disp-step-self-call.exp
new file mode 100644
index 00000000000..9f625b65b1f
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-disp-step-self-call.exp
@@ -0,0 +1,81 @@
+# 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/>.
+
+# Test amd64 displaced stepping over a call instruction that calls to
+# itself.  This is pretty unlikely to be seen in the wild, but does
+# test a corner case of our displaced step handling.
+
+require is_x86_64_m64_target
+
+set newline "\[\r\n\]*"
+
+set opts {debug nopie}
+standard_testfile .S -alarm.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
+}
+
+# Proceed to the test function.
+gdb_breakpoint "test_call"
+gdb_continue_to_breakpoint "test_call"
+
+# Get the current stack pointer value.
+set sp [get_hexadecimal_valueof "\$sp" "*UNKNOWN*"]
+
+# Get the address of the next instruction.
+set next_insn_addr ""
+gdb_test_multiple "x/2i \$pc" "get address of next insn" {
+    -re "\r\n=> $hex \[^\r\n\]+\r\n" {
+	exp_continue
+    }
+    -re "^   ($hex) \[^\r\n\]+\r\n" {
+	set next_insn_addr $expect_out(1,string)
+	exp_continue
+    }
+    -re "^$::gdb_prompt $" {
+	gdb_assert {![string equal $next_insn_addr ""]} \
+	    $gdb_test_name
+    }
+}
+
+# Clear the slot on the stack and confirm it was set to zero.
+set sp [expr $sp - 0x8]
+gdb_test_no_output "set {unsigned long long} $sp = 0"
+set zero_val 0x[format %016x 0]
+gdb_test "x/1gx 0x[format %x $sp]" "$hex:\\s+${zero_val}" \
+    "check return address slot was set to zero"
+
+# Single step.
+gdb_test "stepi" \
+    "Breakpoint $decimal, test_call \\(\\) at .*"
+
+# Check stack pointer was updated to the expected value
+set new_sp [get_hexadecimal_valueof "\$sp" "*UNKNOWN*" \
+	       "get stack pointer after step"]
+gdb_assert {[expr $sp == $new_sp]} \
+    "check stack pointer was updated as expected"
+
+# Check the contents of the stack were updated to the expected value.
+set next_insn_addr 0x[format %016X $next_insn_addr]
+gdb_test "x/1gx 0x[format %x $sp]" "$hex:\\s+$next_insn_addr" \
+    "check return address was updated correctly"
diff --git a/gdb/testsuite/gdb.arch/amd64-disp-step-signal.c b/gdb/testsuite/gdb.arch/amd64-disp-step-signal.c
new file mode 100644
index 00000000000..c968146624a
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-disp-step-signal.c
@@ -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);
+}
diff --git a/gdb/testsuite/gdb.arch/amd64-disp-step.S b/gdb/testsuite/gdb.arch/amd64-disp-step.S
index b25e292bdf0..bf73778cf43 100644
--- a/gdb/testsuite/gdb.arch/amd64-disp-step.S
+++ b/gdb/testsuite/gdb.arch/amd64-disp-step.S
@@ -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 */
diff --git a/gdb/testsuite/gdb.arch/amd64-disp-step.exp b/gdb/testsuite/gdb.arch/amd64-disp-step.exp
index 2aee1e05774..09ba56e490e 100644
--- a/gdb/testsuite/gdb.arch/amd64-disp-step.exp
+++ b/gdb/testsuite/gdb.arch/amd64-disp-step.exp
@@ -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
 }
 
 ##########################################
diff --git a/gdb/testsuite/gdb.arch/i386-disp-step-self-call-alarm.c b/gdb/testsuite/gdb.arch/i386-disp-step-self-call-alarm.c
new file mode 100644
index 00000000000..aec3d294b15
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/i386-disp-step-self-call-alarm.c
@@ -0,0 +1,24 @@
+/* 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 <unistd.h>
+
+void
+setup_alarm (void)
+{
+  alarm (300);
+}
diff --git a/gdb/testsuite/gdb.arch/i386-disp-step-self-call.S b/gdb/testsuite/gdb.arch/i386-disp-step-self-call.S
new file mode 100644
index 00000000000..0b4255c36eb
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/i386-disp-step-self-call.S
@@ -0,0 +1,50 @@
+/* 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 <http://www.gnu.org/licenses/>.
+
+   This file is part of the gdb testsuite.
+   It tests displaced stepping over various insns that require special
+   handling.  */
+
+	.text
+
+	.global main
+main:
+	nop
+
+	call	setup_alarm
+
+	nop
+
+/***********************************************/
+
+/* test call/ret */
+
+	.global test_call
+test_call:
+	call test_call
+	nop
+	.global test_ret_end
+test_ret_end:
+	nop
+
+/***********************************************/
+
+/* all done */
+
+done:
+	pushl $0
+	call exit
+	hlt
+
diff --git a/gdb/testsuite/gdb.arch/i386-disp-step-self-call.exp b/gdb/testsuite/gdb.arch/i386-disp-step-self-call.exp
new file mode 100644
index 00000000000..d676e62e15c
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/i386-disp-step-self-call.exp
@@ -0,0 +1,81 @@
+# 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/>.
+
+# Test i386 displaced stepping over a call instruction that calls to
+# itself.  This is pretty unlikely to be seen in the wild, but does
+# test a corner case of our displaced step handling.
+
+require is_x86_like_target
+
+set newline "\[\r\n\]*"
+
+set opts {debug nopie}
+standard_testfile .S -alarm.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
+}
+
+# Proceed to the test function.
+gdb_breakpoint "test_call"
+gdb_continue_to_breakpoint "test_call"
+
+# Get the current stack pointer value.
+set sp [get_hexadecimal_valueof "\$sp" "*UNKNOWN*"]
+
+# Get the address of the next instruction.
+set next_insn_addr ""
+gdb_test_multiple "x/2i \$pc" "get address of next insn" {
+    -re "\r\n=> $hex \[^\r\n\]+\r\n" {
+	exp_continue
+    }
+    -re "^   ($hex) \[^\r\n\]+\r\n" {
+	set next_insn_addr $expect_out(1,string)
+	exp_continue
+    }
+    -re "^$::gdb_prompt $" {
+	gdb_assert {![string equal $next_insn_addr ""]} \
+	    $gdb_test_name
+    }
+}
+
+# Clear the slot on the stack and confirm it was set to zero.
+set sp [expr $sp - 0x4]
+gdb_test_no_output "set {unsigned long long} $sp = 0"
+set zero_val 0x[format %08x 0]
+gdb_test "x/1wx 0x[format %x $sp]" "$hex:\\s+${zero_val}" \
+    "check return address slot was set to zero"
+
+# Single step.
+gdb_test "stepi" \
+    "Breakpoint $decimal, test_call \\(\\) at .*"
+
+# Check stack pointer was updated to the expected value
+set new_sp [get_hexadecimal_valueof "\$sp" "*UNKNOWN*" \
+	       "get stack pointer after step"]
+gdb_assert {[expr $sp == $new_sp]} \
+    "check stack pointer was updated as expected"
+
+# Check the contents of the stack were updated to the expected value.
+set next_insn_addr 0x[format %08X $next_insn_addr]
+gdb_test "x/1wx 0x[format %x $sp]" "$hex:\\s+$next_insn_addr" \
+    "check return address was updated correctly"
