public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 0/3] AMD64 Displaced Stepping Fix
@ 2023-02-23 15:46 Andrew Burgess
  2023-02-23 15:46 ` [PATCH 1/3] gdb: more debug output for displaced stepping Andrew Burgess
                   ` (3 more replies)
  0 siblings, 4 replies; 36+ messages in thread
From: Andrew Burgess @ 2023-02-23 15:46 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

I started looking at PR gdb/22921, which turns out to be a bug in our
displaced stepping implementation.

Patch #1 adds some extra debug output that I found super useful.

Patch #2 is a minor cleanup.

Patch #3 fixes the amd64 bug, but does change the displaced stepping
logic a little (specifically how we fixup after displaced stepping),
so this is what needs to most reviewing.

All feedback welcome.

Thanks,
Andrew

---

Andrew Burgess (3):
  gdb: more debug output for displaced stepping
  gdb: remove gdbarch_displaced_step_fixup_p
  gdb: fix reg corruption from displaced stepping on amd64

 gdb/aarch64-tdep.c                            |  19 ++-
 gdb/aarch64-tdep.h                            |   3 +-
 gdb/amd64-tdep.c                              |  28 ++---
 gdb/amd64-tdep.h                              |   2 +-
 gdb/arm-tdep.c                                |  27 ++++-
 gdb/arm-tdep.h                                |   3 +-
 gdb/displaced-stepping.c                      | 113 ++++++++++++------
 gdb/displaced-stepping.h                      |   3 +
 gdb/gdbarch-components.py                     |  26 ++--
 gdb/gdbarch-gen.h                             |  25 ++--
 gdb/gdbarch.c                                 |  18 +--
 gdb/i386-tdep.c                               |  28 ++---
 gdb/i386-tdep.h                               |   2 +-
 gdb/infrun.c                                  |  35 ------
 gdb/infrun.h                                  |   3 -
 gdb/rs6000-tdep.c                             |  15 ++-
 gdb/s390-tdep.c                               |  16 ++-
 .../gdb.arch/amd64-disp-step-signal.c         |  30 +++++
 gdb/testsuite/gdb.arch/amd64-disp-step.S      |  15 +++
 gdb/testsuite/gdb.arch/amd64-disp-step.exp    | 106 +++++++++++++---
 20 files changed, 348 insertions(+), 169 deletions(-)
 create mode 100644 gdb/testsuite/gdb.arch/amd64-disp-step-signal.c


base-commit: 5e39600a691e3ba76acf6ab94edb24844c2e82b7
-- 
2.25.4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCH 1/3] gdb: more debug output for displaced stepping
  2023-02-23 15:46 [PATCH 0/3] AMD64 Displaced Stepping Fix Andrew Burgess
@ 2023-02-23 15:46 ` Andrew Burgess
  2023-02-23 15:46 ` [PATCH 2/3] gdb: remove gdbarch_displaced_step_fixup_p Andrew Burgess
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 36+ messages in thread
From: Andrew Burgess @ 2023-02-23 15:46 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

While investigating a displaced stepping issue I wanted an easy way to
see what GDB thought the original instruction was, and what
instruction GDB replaced that with when performing the displaced step.

We do print out the address that is being stepped, so I can track down
the original instruction.

And we do print out the bytes of the new instruction, so I can figure
out what the replacement instruction was, but it's not really easy.

In this commit, within displaced_step_buffers::prepare, I add new
debug output which disassembles both the original instruction, and the
replacement instruction, and prints both these instructions, along
with the bytes that make these instructions, to the debug output.

This new debug improved on some debug that was already present in
infrun.c, however, the old infrun.c debug (a) didn't actually
disassemble the instruction, it just printed the bytes, and (b) there
was an assumption that all instructions are 4-bytes long, which
clearly isn't correct.

I think it makes more sense to have all this debug in the
displaced-stepping.c file, so I've removed the infrun.c code, and
added my new debug in displaced-stepping.c.

Here's an example of what the output looks like on x86-64 (this is
with 'set debug displaced on').  The two interesting lines contain the
strings 'original insn' and 'replacement insn':

  (gdb) step
  [displaced] displaced_step_prepare_throw: displaced-stepping 2073893.2073893.0 now
  [displaced] prepare: selected buffer at 0x401052
  [displaced] prepare: saved 0x401052: 1e fa 31 ed 49 89 d1 5e 48 89 e2 48 83 e4 f0 50
  [displaced] prepare:    original insn 0x401030: ff 25 e2 2f 00 00	jmp    *0x2fe2(%rip)        # 0x404018 <puts@got.plt>
  [displaced] fixup_riprel: %rip-relative addressing used.
  [displaced] fixup_riprel: using temp reg 2, old value 0x7ffff7f8a578, new value 0x401036
  [displaced] amd64_displaced_step_copy_insn: copy 0x401030->0x401052: ff a1 e2 2f 00 00 68 00 00 00 00 e9 e0 ff ff ff
  [displaced] prepare: replacement insn 0x401052: ff a1 e2 2f 00 00	jmp    *0x2fe2(%rcx)
  [displaced] displaced_step_prepare_throw: prepared successfully thread=2073893.2073893.0, original_pc=0x401030, displaced_pc=0x401052
  [displaced] finish: restored 2073893.2073893.0 0x401052
  [displaced] amd64_displaced_step_fixup: fixup (0x401030, 0x401052), insn = 0xff 0xa1 ...
  [displaced] amd64_displaced_step_fixup: restoring reg 2 to 0x7ffff7f8a578
  0x00007ffff7e402c0 in puts () from /lib64/libc.so.6
  (gdb)

One final note.  For many targets that support displaced stepping (in
fact all targets except ARM) the replacement instruction is always a
single instruction.  But on ARM the replacement could actually be a
series of instructions.  The debug code tries to handle this by
disassembling the entire displaced stepping buffer.  Obviously this
might actually print more than is necessary, but there's (currently)
no easy way to know how many instructions to disassemble; that
knowledge is all locked in the architecture specific code.  Still I
don't think it really hurts, if someone is looking at this debug then
hopefully they known what to expect.
---
 gdb/displaced-stepping.c | 72 ++++++++++++++++++++++++++++++++++++++++
 gdb/displaced-stepping.h |  3 ++
 gdb/infrun.c             | 35 -------------------
 gdb/infrun.h             |  3 --
 4 files changed, 75 insertions(+), 38 deletions(-)

diff --git a/gdb/displaced-stepping.c b/gdb/displaced-stepping.c
index 06b32a80f6a..7c9610ee728 100644
--- a/gdb/displaced-stepping.c
+++ b/gdb/displaced-stepping.c
@@ -28,6 +28,7 @@
 #include "inferior.h"
 #include "regcache.h"
 #include "target/target.h"
+#include "disasm.h"
 
 /* Default destructor for displaced_step_copy_insn_closure.  */
 
@@ -43,6 +44,24 @@ show_debug_displaced (struct ui_file *file, int from_tty,
   gdb_printf (file, _("Displace stepping debugging is %s.\n"), value);
 }
 
+/* See displaced-stepping.h.  */
+
+std::string
+displaced_step_dump_bytes (const gdb_byte *buf, size_t len)
+{
+  std::string ret;
+
+  for (size_t i = 0; i < len; i++)
+    {
+      if (i == 0)
+	ret += string_printf ("%02x", buf[i]);
+      else
+	ret += string_printf (" %02x", buf[i]);
+    }
+
+  return ret;
+}
+
 displaced_step_prepare_status
 displaced_step_buffers::prepare (thread_info *thread, CORE_ADDR &displaced_pc)
 {
@@ -125,6 +144,26 @@ displaced_step_buffers::prepare (thread_info *thread, CORE_ADDR &displaced_pc)
 			  displaced_step_dump_bytes
 			  (buffer->saved_copy.data (), len).c_str ());
 
+  /* Display the instruction we are going to copy.  */
+  if (debug_displaced)
+    {
+      string_file tmp_stream;
+      int dislen = gdb_print_insn (arch, buffer->original_pc, &tmp_stream,
+				   nullptr);
+
+      gdb::byte_vector insn_buf (dislen);
+      read_memory (buffer->original_pc, insn_buf.data (), insn_buf.size ());
+
+      std::string insn_bytes
+	= displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ());
+      /* Extra spaces at the start of this debug line ensure alignment with
+	 the 'replacement insn' line below.  */
+      displaced_debug_printf ("   original insn %s: %s\t%s",
+			      paddress (arch, buffer->original_pc),
+			      insn_bytes.c_str (),
+			      tmp_stream.string ().c_str ());
+    }
+
   /* Save this in a local variable first, so it's released if code below
      throws.  */
   displaced_step_copy_insn_closure_up copy_insn_closure
@@ -139,6 +178,39 @@ displaced_step_buffers::prepare (thread_info *thread, CORE_ADDR &displaced_pc)
       return DISPLACED_STEP_PREPARE_STATUS_CANT;
     }
 
+  /* Display the new displaced instruction(s).  */
+  if (debug_displaced)
+    {
+      string_file tmp_stream;
+      CORE_ADDR addr = buffer->addr;
+
+      /* If displaced stepping is going to use h/w single step then we know
+	 that the replacement instruction can only be a single instruction,
+	 in that case set the end address at the next byte.
+
+	 Otherwise the displaced stepping copy instruction routine could
+	 have generated multiple instructions, and all we know is that they
+	 must fit within the LEN bytes of the buffer.  */
+      CORE_ADDR end
+	= addr + (gdbarch_displaced_step_hw_singlestep (arch) ? 1 : len);
+
+      while (addr < end)
+	{
+	  int dislen = gdb_print_insn (arch, addr, &tmp_stream, nullptr);
+
+	  gdb::byte_vector insn_buf (dislen);
+	  read_memory (buffer->addr, insn_buf.data (), insn_buf.size ());
+
+	  std::string insn_bytes
+	    = displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ());
+	  std::string insn_str = tmp_stream.release ();
+	  displaced_debug_printf ("replacement insn %s: %s\t%s",
+				  paddress (arch, addr), insn_bytes.c_str (),
+				  insn_str.c_str ());
+	  addr += dislen;
+	}
+    }
+
   /* This marks the buffer as being in use.  */
   buffer->current_thread = thread;
 
diff --git a/gdb/displaced-stepping.h b/gdb/displaced-stepping.h
index e154927ad92..b3c5cdb1109 100644
--- a/gdb/displaced-stepping.h
+++ b/gdb/displaced-stepping.h
@@ -207,4 +207,7 @@ struct displaced_step_buffers
   std::vector<displaced_step_buffer> m_buffers;
 };
 
+/* Dump LEN bytes at BUF in hex to a string and return it.  */
+extern std::string displaced_step_dump_bytes (const gdb_byte *buf, size_t len);
+
 #endif /* DISPLACED_STEPPING_H */
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 3629db0443a..0867d86cd78 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -1655,24 +1655,6 @@ displaced_step_reset (displaced_step_thread_state *displaced)
 
 using displaced_step_reset_cleanup = FORWARD_SCOPE_EXIT (displaced_step_reset);
 
-/* See infrun.h.  */
-
-std::string
-displaced_step_dump_bytes (const gdb_byte *buf, size_t len)
-{
-  std::string ret;
-
-  for (size_t i = 0; i < len; i++)
-    {
-      if (i == 0)
-	ret += string_printf ("%02x", buf[i]);
-      else
-	ret += string_printf (" %02x", buf[i]);
-    }
-
-  return ret;
-}
-
 /* Prepare to single-step, using displaced stepping.
 
    Note that we cannot use displaced stepping when we have a signal to
@@ -2614,23 +2596,6 @@ resume_1 (enum gdb_signal sig)
 	step = false;
     }
 
-  if (debug_displaced
-      && tp->control.trap_expected
-      && use_displaced_stepping (tp)
-      && !step_over_info_valid_p ())
-    {
-      struct regcache *resume_regcache = get_thread_regcache (tp);
-      struct gdbarch *resume_gdbarch = resume_regcache->arch ();
-      CORE_ADDR actual_pc = regcache_read_pc (resume_regcache);
-      gdb_byte buf[4];
-
-      read_memory (actual_pc, buf, sizeof (buf));
-      displaced_debug_printf ("run %s: %s",
-			      paddress (resume_gdbarch, actual_pc),
-			      displaced_step_dump_bytes
-				(buf, sizeof (buf)).c_str ());
-    }
-
   if (tp->control.may_range_step)
     {
       /* If we're resuming a thread with the PC out of the step
diff --git a/gdb/infrun.h b/gdb/infrun.h
index 43fd1b44f5a..419beb20ddb 100644
--- a/gdb/infrun.h
+++ b/gdb/infrun.h
@@ -263,9 +263,6 @@ extern void update_signals_program_target (void);
    $_exitsignal.  */
 extern void clear_exit_convenience_vars (void);
 
-/* Dump LEN bytes at BUF in hex to a string and return it.  */
-extern std::string displaced_step_dump_bytes (const gdb_byte *buf, size_t len);
-
 extern void update_observer_mode (void);
 
 extern void signal_catch_update (const unsigned int *);
-- 
2.25.4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCH 2/3] gdb: remove gdbarch_displaced_step_fixup_p
  2023-02-23 15:46 [PATCH 0/3] AMD64 Displaced Stepping Fix Andrew Burgess
  2023-02-23 15:46 ` [PATCH 1/3] gdb: more debug output for displaced stepping Andrew Burgess
@ 2023-02-23 15:46 ` Andrew Burgess
  2023-02-23 15:46 ` [PATCH 3/3] gdb: fix reg corruption from displaced stepping on amd64 Andrew Burgess
  2023-03-16 16:39 ` [PATCHv2 0/4] AMD64 Displaced Stepping Fix Andrew Burgess
  3 siblings, 0 replies; 36+ messages in thread
From: Andrew Burgess @ 2023-02-23 15:46 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

The comment on the gdbarch_displaced_step_fixup gdbarch method
indicates that this method is optional and that GDB will perform some
default if this method is not supplied.  As such we define a predicate
gdbarch_displaced_step_fixup_p.

It may have been true at one point that the fixup method was optional,
but it is no longer true.  If this method is not defined and GDB tries
to complete a displaced step, then GDB is going to crash.

Additionally the gdbarch_displaced_step_fixup_p predicate is not used
anywhere in GDB.

In this commit I have removed the gdbarch_displaced_step_fixup_p
predicate, and I have updated the validation check for the
gdbarch_displaced_step_fixup method; if the
gdbarch_displaced_step_copy_insn method is defined then the fixup
method must also be defined.

I believe I've manually checked all the current places where
gdbarch_displaced_step_copy_insn is defined and they all also define
the fixup method, so this change should cause no problems for anyone.

There should be no user visible changes after this commit.
---
 gdb/gdbarch-components.py |  4 ++--
 gdb/gdbarch-gen.h         |  2 --
 gdb/gdbarch.c             | 14 ++------------
 3 files changed, 4 insertions(+), 16 deletions(-)

diff --git a/gdb/gdbarch-components.py b/gdb/gdbarch-components.py
index 76ad2832d8a..06be4b57051 100644
--- a/gdb/gdbarch-components.py
+++ b/gdb/gdbarch-components.py
@@ -1851,9 +1851,9 @@ see the comments in infrun.c.
         ("CORE_ADDR", "to"),
         ("struct regcache *", "regs"),
     ],
-    predicate=True,
+    predicate=False,
     predefault="NULL",
-    invalid=True,
+    invalid="(gdbarch->displaced_step_copy_insn == nullptr) != (gdbarch->displaced_step_fixup == nullptr)",
 )
 
 Method(
diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
index 32b2d96fbe0..ac63e84b191 100644
--- a/gdb/gdbarch-gen.h
+++ b/gdb/gdbarch-gen.h
@@ -1085,8 +1085,6 @@ extern void set_gdbarch_displaced_step_hw_singlestep (struct gdbarch *gdbarch, g
    For a general explanation of displaced stepping and how GDB uses it,
    see the comments in infrun.c. */
 
-extern bool gdbarch_displaced_step_fixup_p (struct gdbarch *gdbarch);
-
 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);
 extern void set_gdbarch_displaced_step_fixup (struct gdbarch *gdbarch, gdbarch_displaced_step_fixup_ftype *displaced_step_fixup);
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index 04fcc92f8f9..4958d3c5fc6 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -447,7 +447,8 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of max_insn_length, has predicate.  */
   /* Skip verify of displaced_step_copy_insn, has predicate.  */
   /* Skip verify of displaced_step_hw_singlestep, invalid_p == 0 */
-  /* Skip verify of displaced_step_fixup, has predicate.  */
+  if ((gdbarch->displaced_step_copy_insn == nullptr) != (gdbarch->displaced_step_fixup == nullptr))
+    log.puts ("\n\tdisplaced_step_fixup");
   /* Skip verify of displaced_step_prepare, has predicate.  */
   if ((! gdbarch->displaced_step_finish) != (! gdbarch->displaced_step_prepare))
     log.puts ("\n\tdisplaced_step_finish");
@@ -1087,9 +1088,6 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
   gdb_printf (file,
 	      "gdbarch_dump: displaced_step_hw_singlestep = <%s>\n",
 	      host_address_to_string (gdbarch->displaced_step_hw_singlestep));
-  gdb_printf (file,
-	      "gdbarch_dump: gdbarch_displaced_step_fixup_p() = %d\n",
-	      gdbarch_displaced_step_fixup_p (gdbarch));
   gdb_printf (file,
 	      "gdbarch_dump: displaced_step_fixup = <%s>\n",
 	      host_address_to_string (gdbarch->displaced_step_fixup));
@@ -4046,19 +4044,11 @@ set_gdbarch_displaced_step_hw_singlestep (struct gdbarch *gdbarch,
   gdbarch->displaced_step_hw_singlestep = displaced_step_hw_singlestep;
 }
 
-bool
-gdbarch_displaced_step_fixup_p (struct gdbarch *gdbarch)
-{
-  gdb_assert (gdbarch != NULL);
-  return gdbarch->displaced_step_fixup != NULL;
-}
-
 void
 gdbarch_displaced_step_fixup (struct gdbarch *gdbarch, struct displaced_step_copy_insn_closure *closure, CORE_ADDR from, CORE_ADDR to, struct regcache *regs)
 {
   gdb_assert (gdbarch != NULL);
   gdb_assert (gdbarch->displaced_step_fixup != NULL);
-  /* Do not check predicate: gdbarch->displaced_step_fixup != NULL, allow call.  */
   if (gdbarch_debug >= 2)
     gdb_printf (gdb_stdlog, "gdbarch_displaced_step_fixup called\n");
   gdbarch->displaced_step_fixup (gdbarch, closure, from, to, regs);
-- 
2.25.4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCH 3/3] gdb: fix reg corruption from displaced stepping on amd64
  2023-02-23 15:46 [PATCH 0/3] AMD64 Displaced Stepping Fix Andrew Burgess
  2023-02-23 15:46 ` [PATCH 1/3] gdb: more debug output for displaced stepping Andrew Burgess
  2023-02-23 15:46 ` [PATCH 2/3] gdb: remove gdbarch_displaced_step_fixup_p Andrew Burgess
@ 2023-02-23 15:46 ` Andrew Burgess
  2023-03-16 16:39 ` [PATCHv2 0/4] AMD64 Displaced Stepping Fix Andrew Burgess
  3 siblings, 0 replies; 36+ messages in thread
From: Andrew Burgess @ 2023-02-23 15:46 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit aims to address a problem that exists with the current
approach to displaced stepping, and was identified in PR gdb/22921.

Displaced stepping is currently supported on AArch64, ARM, amd64,
i386, rs6000 (ppc), and s390.  Of these, I believe there is a problem
with the current approach which will impact amd64 and ARM, and can
lead to random register corruption when the inferior makes use of
asynchronous signals and GDB is using displaced stepping.

The problem can be found in displaced_step_buffers::finish in
displaced-stepping.c, and is this; after GDB tries to perform a
displaced step, and the inferior stops, GDB classifies the stop into
one of two states, either the displaced step succeeded, or the
displaced step failed.

If the displaced step succeeded then gdbarch_displaced_step_fixup is
called, which has the job of fixing up the state of the current
inferior as if the step had not been performed in a displaced manor.
This all seems just fine.

However, if the displaced step is considered to have not completed
then GDB doesn't call gdbarch_displaced_step_fixup, instead GDB
remains in displaced_step_buffers::finish and just performs a minimal
fixup which involves adjusting the program counter back to its
original value.

The problem here is that for amd64 and ARM setting up for a displaced
step can involve changing the values in some temporary registers.  If
the displaced step succeeds then this is fine; after the step the
temporary registers are restored to their original values in the
architecture specific code.

But if the displaced step does not succeed then the temporary
registers are never restored, and they retain their modified values.

In this context a temporary register is simply any register that is
not otherwise used by the instruction being stepped that the
architecture specific code considers safe to borrow for the lifetime
of the instruction being stepped.

In the bug PR gdb/22921, the amd64 instruction being stepped is
an rip-relative instruction like this:

  jmp    *0x2fe2(%rip)

When we displaced step this instruction we borrow a register, and
modify the instruction to something like:

  jmp    *0x2fe2(%rcx)

with %rcx having its value adjusted to contain the original %rip
value.

Now if the displaced step does not succeed then %rcx will be left with
a corrupted value.  Obviously corrupting any register is bad; in the
bug report this problem was spotted because %rcx is used as a function
argument register.

And finally, why might a displaced step not succeed?  Asynchronous
signals provides one reason.  GDB sets up for the displaced step and,
at that precise moment, the OS delivers a signal (SIGALRM in the
test), the signal stops the inferior at the address of the displaced
instruction.  GDB cancels the displaced instruction, handles the
signal, and then tries again with the displaced step.  But it is that
first cancellation of the displaced step that causes the problem; in
that case GDB (correctly) sees the displaced step as having not
completed, and so does not perform the architecture specific fixup,
leaving the register corrupted.

The reason why I think AArch64, rs600, i386, and s390 are not effected
by this problem is that I don't believe these architectures make use
of any temporary registers, so when a displaced step is not completed
successfully, the minimal fix up is sufficient.

On amd64 we use at most one temporary register.

On ARM, looking at arm_displaced_step_copy_insn_closure, we could
modify up to 16 temporary registers, and the instruction being
displaced stepped could be expanded to multiple replacement
instructions, which increases the chances of this bug triggering.

This commit only aims to address the issue on amd64 for now, though I
believe that the approach I'm proposing here might be applicable for
ARM too.

What I propose is that we always call gdbarch_displaced_step_fixup.

We will now pass an extra argument to gdbarch_displaced_step_fixup,
this is the program-counter value at which the inferior stopped.

Using this program-counter value GDB can determine if the displaced
stepped instruction completed or not.  Or (in the future) for ARM, how
far through the displaced step sequence the inferior got before being
interrupted.

It is then possible for the architecture specific code in GDB to
conditionally roll back some or all of the instructions effects,
i.e. on amd64 we can roll back the temporary register value in all
cases.

In order to move all architectures to this new API, I have moved the
minimal roll-back version of the code inside the architecture specific
fixup functions for AArch64, rs600, s390, and ARM.  For all of these
except ARM I think this is good enough, as no temporaries are used all
that's needed is the program counter restore anyway.

For ARM the minimal code is no worse than what we had before, though I
do consider this architectures displaced-stepping broken.

For amd64 and i386 I make use of the program counter to conditionally
roll back the inferior state.  For i386 this is not strictly
necessary, as no temporaries are used.  However, the structure of
i386_displaced_step_fixup is so similar to amd64_displaced_step_fixup
that is seems a shame to take a different approach in these two
functions.

One thing to note is that previously we determined if the displaced
step had completed based on how the inferior stopped.  Stopping with a
SIGTRAP we assumed meant that we had hit the breakpoint at the end of
the displaced instruction.  Anything else indicated the displaced
instruction had not completed successfully.

After this patch GDB now relies entirely on the program-counter value
to make this decision.  If the program-counter points that the start
of the displaced instruction buffer (i.e. it hasn't changed) then the
displaced step didn't complete, otherwise (for amd64 and i386, which
always replace with a single instruction), we assume the displaced
step did complete.

I've updated the gdb.arch/amd64-disp-step.exp test to cover the
'jmpq*' instruction that was causing problems in the original bug, and
also added support for testing the displaced step in the presence of
asynchronous signal delivery.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=22921
---
 gdb/aarch64-tdep.c                            |  19 +++-
 gdb/aarch64-tdep.h                            |   3 +-
 gdb/amd64-tdep.c                              |  28 ++---
 gdb/amd64-tdep.h                              |   2 +-
 gdb/arm-tdep.c                                |  27 ++++-
 gdb/arm-tdep.h                                |   3 +-
 gdb/displaced-stepping.c                      |  41 +------
 gdb/gdbarch-components.py                     |  22 ++--
 gdb/gdbarch-gen.h                             |  25 +++--
 gdb/gdbarch.c                                 |   4 +-
 gdb/i386-tdep.c                               |  28 ++---
 gdb/i386-tdep.h                               |   2 +-
 gdb/rs6000-tdep.c                             |  15 ++-
 gdb/s390-tdep.c                               |  16 ++-
 .../gdb.arch/amd64-disp-step-signal.c         |  30 +++++
 gdb/testsuite/gdb.arch/amd64-disp-step.S      |  15 +++
 gdb/testsuite/gdb.arch/amd64-disp-step.exp    | 106 +++++++++++++++---
 17 files changed, 270 insertions(+), 116 deletions(-)
 create mode 100644 gdb/testsuite/gdb.arch/amd64-disp-step-signal.c

diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 5b1b9921f87..46c977140a7 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -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));
 
diff --git a/gdb/aarch64-tdep.h b/gdb/aarch64-tdep.h
index ae38327ffab..801a151913b 100644
--- a/gdb/aarch64-tdep.h
+++ b/gdb/aarch64-tdep.h
@@ -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);
 
diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index 0ae09dc7bfb..c2aad2f7161 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -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;
diff --git a/gdb/amd64-tdep.h b/gdb/amd64-tdep.h
index 929b4b8bdc5..58953259765 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);
+   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.  */
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 03df41a64b3..70c3e94f322 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -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_;
 
diff --git a/gdb/arm-tdep.h b/gdb/arm-tdep.h
index a8d21c44ba4..ab31d3e2e9c 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 *);
+				      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 *);
diff --git a/gdb/displaced-stepping.c b/gdb/displaced-stepping.c
index 7c9610ee728..d4128485d3f 100644
--- a/gdb/displaced-stepping.c
+++ b/gdb/displaced-stepping.c
@@ -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 *
diff --git a/gdb/gdbarch-components.py b/gdb/gdbarch-components.py
index 06be4b57051..bb36220774b 100644
--- a/gdb/gdbarch-components.py
+++ b/gdb/gdbarch-components.py
@@ -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",
diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
index ac63e84b191..e5d33924e90 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, 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.
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index 4958d3c5fc6..c1cbf1cbb74 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -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
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 476d17f1efd..2caf24daac2 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -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;
diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h
index 371bce72369..1244b098a21 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);
+   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 *);
diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index 592b447948e..560955644aa 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -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
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
index cab1757c5ab..f1ed69ee0a4 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -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)
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
 }
 
 ##########################################
-- 
2.25.4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCHv2 0/4] AMD64 Displaced Stepping Fix
  2023-02-23 15:46 [PATCH 0/3] AMD64 Displaced Stepping Fix Andrew Burgess
                   ` (2 preceding siblings ...)
  2023-02-23 15:46 ` [PATCH 3/3] gdb: fix reg corruption from displaced stepping on amd64 Andrew Burgess
@ 2023-03-16 16:39 ` Andrew Burgess
  2023-03-16 16:39   ` [PATCHv2 1/4] gdb: more debug output for displaced stepping Andrew Burgess
                     ` (4 more replies)
  3 siblings, 5 replies; 36+ messages in thread
From: Andrew Burgess @ 2023-03-16 16:39 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

I started looking at PR gdb/22921, which turns out to be a bug in our
displaced stepping implementation.

Patch #1 adds some extra debug output that I found super useful.

Patch #2 is a minor cleanup.

Patch #3 fixes the amd64 bug, but does change the displaced stepping
logic a little (specifically how we fixup after displaced stepping),
so this is what needs to most reviewing.

Patch #4 is new in V2, and removes some code that is no longer needed
after patch #3.

In V2:

  - Rebased to current HEAD of master,

  - Added patch #4.

Thanks,
Andrew

---

Andrew Burgess (4):
  gdb: more debug output for displaced stepping
  gdb: remove gdbarch_displaced_step_fixup_p
  gdb: fix reg corruption from displaced stepping on amd64
  gdb: remove redundant signal passing

 gdb/aarch64-tdep.c                            |  19 ++-
 gdb/aarch64-tdep.h                            |   3 +-
 gdb/amd64-tdep.c                              |  28 ++---
 gdb/amd64-tdep.h                              |   2 +-
 gdb/arm-tdep.c                                |  27 +++-
 gdb/arm-tdep.h                                |   3 +-
 gdb/displaced-stepping.c                      | 116 ++++++++++++------
 gdb/displaced-stepping.h                      |   6 +-
 gdb/gdbarch-gen.h                             |  29 +++--
 gdb/gdbarch.c                                 |  22 +---
 gdb/gdbarch_components.py                     |  27 ++--
 gdb/i386-tdep.c                               |  28 ++---
 gdb/i386-tdep.h                               |   2 +-
 gdb/infrun.c                                  |  51 +-------
 gdb/infrun.h                                  |   3 -
 gdb/linux-tdep.c                              |   4 +-
 gdb/linux-tdep.h                              |   2 +-
 gdb/rs6000-tdep.c                             |  20 ++-
 gdb/s390-tdep.c                               |  16 ++-
 .../gdb.arch/amd64-disp-step-signal.c         |  30 +++++
 gdb/testsuite/gdb.arch/amd64-disp-step.S      |  15 +++
 gdb/testsuite/gdb.arch/amd64-disp-step.exp    | 106 +++++++++++++---
 22 files changed, 366 insertions(+), 193 deletions(-)
 create mode 100644 gdb/testsuite/gdb.arch/amd64-disp-step-signal.c


base-commit: 5a9affd7b875ac183a66ce41f3f226819f0790ed
-- 
2.25.4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCHv2 1/4] gdb: more debug output for displaced stepping
  2023-03-16 16:39 ` [PATCHv2 0/4] AMD64 Displaced Stepping Fix Andrew Burgess
@ 2023-03-16 16:39   ` Andrew Burgess
  2023-03-21 12:29     ` Pedro Alves
  2023-03-21 14:45     ` [PATCHv2 1/4] gdb: more debug output for displaced stepping Simon Marchi
  2023-03-16 16:39   ` [PATCHv2 2/4] gdb: remove gdbarch_displaced_step_fixup_p Andrew Burgess
                     ` (3 subsequent siblings)
  4 siblings, 2 replies; 36+ messages in thread
From: Andrew Burgess @ 2023-03-16 16:39 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

While investigating a displaced stepping issue I wanted an easy way to
see what GDB thought the original instruction was, and what
instruction GDB replaced that with when performing the displaced step.

We do print out the address that is being stepped, so I can track down
the original instruction.

And we do print out the bytes of the new instruction, so I can figure
out what the replacement instruction was, but it's not really easy.

In this commit, within displaced_step_buffers::prepare, I add new
debug output which disassembles both the original instruction, and the
replacement instruction, and prints both these instructions, along
with the bytes that make these instructions, to the debug output.

This new debug improved on some debug that was already present in
infrun.c, however, the old infrun.c debug (a) didn't actually
disassemble the instruction, it just printed the bytes, and (b) there
was an assumption that all instructions are 4-bytes long, which
clearly isn't correct.

I think it makes more sense to have all this debug in the
displaced-stepping.c file, so I've removed the infrun.c code, and
added my new debug in displaced-stepping.c.

Here's an example of what the output looks like on x86-64 (this is
with 'set debug displaced on').  The two interesting lines contain the
strings 'original insn' and 'replacement insn':

  (gdb) step
  [displaced] displaced_step_prepare_throw: displaced-stepping 2073893.2073893.0 now
  [displaced] prepare: selected buffer at 0x401052
  [displaced] prepare: saved 0x401052: 1e fa 31 ed 49 89 d1 5e 48 89 e2 48 83 e4 f0 50
  [displaced] prepare:    original insn 0x401030: ff 25 e2 2f 00 00	jmp    *0x2fe2(%rip)        # 0x404018 <puts@got.plt>
  [displaced] fixup_riprel: %rip-relative addressing used.
  [displaced] fixup_riprel: using temp reg 2, old value 0x7ffff7f8a578, new value 0x401036
  [displaced] amd64_displaced_step_copy_insn: copy 0x401030->0x401052: ff a1 e2 2f 00 00 68 00 00 00 00 e9 e0 ff ff ff
  [displaced] prepare: replacement insn 0x401052: ff a1 e2 2f 00 00	jmp    *0x2fe2(%rcx)
  [displaced] displaced_step_prepare_throw: prepared successfully thread=2073893.2073893.0, original_pc=0x401030, displaced_pc=0x401052
  [displaced] finish: restored 2073893.2073893.0 0x401052
  [displaced] amd64_displaced_step_fixup: fixup (0x401030, 0x401052), insn = 0xff 0xa1 ...
  [displaced] amd64_displaced_step_fixup: restoring reg 2 to 0x7ffff7f8a578
  0x00007ffff7e402c0 in puts () from /lib64/libc.so.6
  (gdb)

One final note.  For many targets that support displaced stepping (in
fact all targets except ARM) the replacement instruction is always a
single instruction.  But on ARM the replacement could actually be a
series of instructions.  The debug code tries to handle this by
disassembling the entire displaced stepping buffer.  Obviously this
might actually print more than is necessary, but there's (currently)
no easy way to know how many instructions to disassemble; that
knowledge is all locked in the architecture specific code.  Still I
don't think it really hurts, if someone is looking at this debug then
hopefully they known what to expect.
---
 gdb/displaced-stepping.c | 72 ++++++++++++++++++++++++++++++++++++++++
 gdb/displaced-stepping.h |  3 ++
 gdb/infrun.c             | 35 -------------------
 gdb/infrun.h             |  3 --
 4 files changed, 75 insertions(+), 38 deletions(-)

diff --git a/gdb/displaced-stepping.c b/gdb/displaced-stepping.c
index 9f98ea8c35b..5b94b2f068d 100644
--- a/gdb/displaced-stepping.c
+++ b/gdb/displaced-stepping.c
@@ -28,6 +28,7 @@
 #include "inferior.h"
 #include "regcache.h"
 #include "target/target.h"
+#include "disasm.h"
 
 /* Default destructor for displaced_step_copy_insn_closure.  */
 
@@ -43,6 +44,24 @@ show_debug_displaced (struct ui_file *file, int from_tty,
   gdb_printf (file, _("Displace stepping debugging is %s.\n"), value);
 }
 
+/* See displaced-stepping.h.  */
+
+std::string
+displaced_step_dump_bytes (const gdb_byte *buf, size_t len)
+{
+  std::string ret;
+
+  for (size_t i = 0; i < len; i++)
+    {
+      if (i == 0)
+	ret += string_printf ("%02x", buf[i]);
+      else
+	ret += string_printf (" %02x", buf[i]);
+    }
+
+  return ret;
+}
+
 displaced_step_prepare_status
 displaced_step_buffers::prepare (thread_info *thread, CORE_ADDR &displaced_pc)
 {
@@ -125,6 +144,26 @@ displaced_step_buffers::prepare (thread_info *thread, CORE_ADDR &displaced_pc)
 			  displaced_step_dump_bytes
 			  (buffer->saved_copy.data (), len).c_str ());
 
+  /* Display the instruction we are going to copy.  */
+  if (debug_displaced)
+    {
+      string_file tmp_stream;
+      int dislen = gdb_print_insn (arch, buffer->original_pc, &tmp_stream,
+				   nullptr);
+
+      gdb::byte_vector insn_buf (dislen);
+      read_memory (buffer->original_pc, insn_buf.data (), insn_buf.size ());
+
+      std::string insn_bytes
+	= displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ());
+      /* Extra spaces at the start of this debug line ensure alignment with
+	 the 'replacement insn' line below.  */
+      displaced_debug_printf ("   original insn %s: %s\t%s",
+			      paddress (arch, buffer->original_pc),
+			      insn_bytes.c_str (),
+			      tmp_stream.string ().c_str ());
+    }
+
   /* Save this in a local variable first, so it's released if code below
      throws.  */
   displaced_step_copy_insn_closure_up copy_insn_closure
@@ -139,6 +178,39 @@ displaced_step_buffers::prepare (thread_info *thread, CORE_ADDR &displaced_pc)
       return DISPLACED_STEP_PREPARE_STATUS_CANT;
     }
 
+  /* Display the new displaced instruction(s).  */
+  if (debug_displaced)
+    {
+      string_file tmp_stream;
+      CORE_ADDR addr = buffer->addr;
+
+      /* If displaced stepping is going to use h/w single step then we know
+	 that the replacement instruction can only be a single instruction,
+	 in that case set the end address at the next byte.
+
+	 Otherwise the displaced stepping copy instruction routine could
+	 have generated multiple instructions, and all we know is that they
+	 must fit within the LEN bytes of the buffer.  */
+      CORE_ADDR end
+	= addr + (gdbarch_displaced_step_hw_singlestep (arch) ? 1 : len);
+
+      while (addr < end)
+	{
+	  int dislen = gdb_print_insn (arch, addr, &tmp_stream, nullptr);
+
+	  gdb::byte_vector insn_buf (dislen);
+	  read_memory (buffer->addr, insn_buf.data (), insn_buf.size ());
+
+	  std::string insn_bytes
+	    = displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ());
+	  std::string insn_str = tmp_stream.release ();
+	  displaced_debug_printf ("replacement insn %s: %s\t%s",
+				  paddress (arch, addr), insn_bytes.c_str (),
+				  insn_str.c_str ());
+	  addr += dislen;
+	}
+    }
+
   /* This marks the buffer as being in use.  */
   buffer->current_thread = thread;
 
diff --git a/gdb/displaced-stepping.h b/gdb/displaced-stepping.h
index e154927ad92..b3c5cdb1109 100644
--- a/gdb/displaced-stepping.h
+++ b/gdb/displaced-stepping.h
@@ -207,4 +207,7 @@ struct displaced_step_buffers
   std::vector<displaced_step_buffer> m_buffers;
 };
 
+/* Dump LEN bytes at BUF in hex to a string and return it.  */
+extern std::string displaced_step_dump_bytes (const gdb_byte *buf, size_t len);
+
 #endif /* DISPLACED_STEPPING_H */
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 33aa0c8794b..e73e8f16902 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -1724,24 +1724,6 @@ displaced_step_reset (displaced_step_thread_state *displaced)
 
 using displaced_step_reset_cleanup = FORWARD_SCOPE_EXIT (displaced_step_reset);
 
-/* See infrun.h.  */
-
-std::string
-displaced_step_dump_bytes (const gdb_byte *buf, size_t len)
-{
-  std::string ret;
-
-  for (size_t i = 0; i < len; i++)
-    {
-      if (i == 0)
-	ret += string_printf ("%02x", buf[i]);
-      else
-	ret += string_printf (" %02x", buf[i]);
-    }
-
-  return ret;
-}
-
 /* Prepare to single-step, using displaced stepping.
 
    Note that we cannot use displaced stepping when we have a signal to
@@ -2710,23 +2692,6 @@ resume_1 (enum gdb_signal sig)
 	step = false;
     }
 
-  if (debug_displaced
-      && tp->control.trap_expected
-      && use_displaced_stepping (tp)
-      && !step_over_info_valid_p ())
-    {
-      struct regcache *resume_regcache = get_thread_regcache (tp);
-      struct gdbarch *resume_gdbarch = resume_regcache->arch ();
-      CORE_ADDR actual_pc = regcache_read_pc (resume_regcache);
-      gdb_byte buf[4];
-
-      read_memory (actual_pc, buf, sizeof (buf));
-      displaced_debug_printf ("run %s: %s",
-			      paddress (resume_gdbarch, actual_pc),
-			      displaced_step_dump_bytes
-				(buf, sizeof (buf)).c_str ());
-    }
-
   if (tp->control.may_range_step)
     {
       /* If we're resuming a thread with the PC out of the step
diff --git a/gdb/infrun.h b/gdb/infrun.h
index 5219063586d..9b3c8962939 100644
--- a/gdb/infrun.h
+++ b/gdb/infrun.h
@@ -270,9 +270,6 @@ extern void update_signals_program_target (void);
    $_exitsignal.  */
 extern void clear_exit_convenience_vars (void);
 
-/* Dump LEN bytes at BUF in hex to a string and return it.  */
-extern std::string displaced_step_dump_bytes (const gdb_byte *buf, size_t len);
-
 extern void update_observer_mode (void);
 
 extern void signal_catch_update (const unsigned int *);
-- 
2.25.4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCHv2 2/4] gdb: remove gdbarch_displaced_step_fixup_p
  2023-03-16 16:39 ` [PATCHv2 0/4] AMD64 Displaced Stepping Fix Andrew Burgess
  2023-03-16 16:39   ` [PATCHv2 1/4] gdb: more debug output for displaced stepping Andrew Burgess
@ 2023-03-16 16:39   ` Andrew Burgess
  2023-03-21 13:10     ` Pedro Alves
  2023-03-16 16:39   ` [PATCHv2 3/4] gdb: fix reg corruption from displaced stepping on amd64 Andrew Burgess
                     ` (2 subsequent siblings)
  4 siblings, 1 reply; 36+ messages in thread
From: Andrew Burgess @ 2023-03-16 16:39 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

The comment on the gdbarch_displaced_step_fixup gdbarch method
indicates that this method is optional and that GDB will perform some
default if this method is not supplied.  As such we define a predicate
gdbarch_displaced_step_fixup_p.

It may have been true at one point that the fixup method was optional,
but it is no longer true.  If this method is not defined and GDB tries
to complete a displaced step, then GDB is going to crash.

Additionally the gdbarch_displaced_step_fixup_p predicate is not used
anywhere in GDB.

In this commit I have removed the gdbarch_displaced_step_fixup_p
predicate, and I have updated the validation check for the
gdbarch_displaced_step_fixup method; if the
gdbarch_displaced_step_copy_insn method is defined then the fixup
method must also be defined.

I believe I've manually checked all the current places where
gdbarch_displaced_step_copy_insn is defined and they all also define
the fixup method, so this change should cause no problems for anyone.

There should be no user visible changes after this commit.
---
 gdb/gdbarch-gen.h         |  2 --
 gdb/gdbarch.c             | 14 ++------------
 gdb/gdbarch_components.py |  3 ++-
 3 files changed, 4 insertions(+), 15 deletions(-)

diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
index 76d12a15317..a3fc0b9272b 100644
--- a/gdb/gdbarch-gen.h
+++ b/gdb/gdbarch-gen.h
@@ -1085,8 +1085,6 @@ extern void set_gdbarch_displaced_step_hw_singlestep (struct gdbarch *gdbarch, g
    For a general explanation of displaced stepping and how GDB uses it,
    see the comments in infrun.c. */
 
-extern bool gdbarch_displaced_step_fixup_p (struct gdbarch *gdbarch);
-
 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);
 extern void set_gdbarch_displaced_step_fixup (struct gdbarch *gdbarch, gdbarch_displaced_step_fixup_ftype *displaced_step_fixup);
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index b4763aa6bf4..b676e346fd0 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -446,7 +446,8 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of max_insn_length, has predicate.  */
   /* Skip verify of displaced_step_copy_insn, has predicate.  */
   /* Skip verify of displaced_step_hw_singlestep, invalid_p == 0 */
-  /* Skip verify of displaced_step_fixup, has predicate.  */
+  if ((gdbarch->displaced_step_copy_insn == nullptr) != (gdbarch->displaced_step_fixup == nullptr))
+    log.puts ("\n\tdisplaced_step_fixup");
   /* Skip verify of displaced_step_prepare, has predicate.  */
   if ((! gdbarch->displaced_step_finish) != (! gdbarch->displaced_step_prepare))
     log.puts ("\n\tdisplaced_step_finish");
@@ -1090,9 +1091,6 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
   gdb_printf (file,
 	      "gdbarch_dump: displaced_step_hw_singlestep = <%s>\n",
 	      host_address_to_string (gdbarch->displaced_step_hw_singlestep));
-  gdb_printf (file,
-	      "gdbarch_dump: gdbarch_displaced_step_fixup_p() = %d\n",
-	      gdbarch_displaced_step_fixup_p (gdbarch));
   gdb_printf (file,
 	      "gdbarch_dump: displaced_step_fixup = <%s>\n",
 	      host_address_to_string (gdbarch->displaced_step_fixup));
@@ -4058,19 +4056,11 @@ set_gdbarch_displaced_step_hw_singlestep (struct gdbarch *gdbarch,
   gdbarch->displaced_step_hw_singlestep = displaced_step_hw_singlestep;
 }
 
-bool
-gdbarch_displaced_step_fixup_p (struct gdbarch *gdbarch)
-{
-  gdb_assert (gdbarch != NULL);
-  return gdbarch->displaced_step_fixup != NULL;
-}
-
 void
 gdbarch_displaced_step_fixup (struct gdbarch *gdbarch, struct displaced_step_copy_insn_closure *closure, CORE_ADDR from, CORE_ADDR to, struct regcache *regs)
 {
   gdb_assert (gdbarch != NULL);
   gdb_assert (gdbarch->displaced_step_fixup != NULL);
-  /* Do not check predicate: gdbarch->displaced_step_fixup != NULL, allow call.  */
   if (gdbarch_debug >= 2)
     gdb_printf (gdb_stdlog, "gdbarch_displaced_step_fixup called\n");
   gdbarch->displaced_step_fixup (gdbarch, closure, from, to, regs);
diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
index 92c501d2a72..2b1a2b4f602 100644
--- a/gdb/gdbarch_components.py
+++ b/gdb/gdbarch_components.py
@@ -1796,8 +1796,9 @@ see the comments in infrun.c.
         ("CORE_ADDR", "to"),
         ("struct regcache *", "regs"),
     ],
-    predicate=True,
+    predicate=False,
     predefault="NULL",
+    invalid="(gdbarch->displaced_step_copy_insn == nullptr) != (gdbarch->displaced_step_fixup == nullptr)",
 )
 
 Method(
-- 
2.25.4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCHv2 3/4] gdb: fix reg corruption from displaced stepping on amd64
  2023-03-16 16:39 ` [PATCHv2 0/4] AMD64 Displaced Stepping Fix Andrew Burgess
  2023-03-16 16:39   ` [PATCHv2 1/4] gdb: more debug output for displaced stepping Andrew Burgess
  2023-03-16 16:39   ` [PATCHv2 2/4] gdb: remove gdbarch_displaced_step_fixup_p Andrew Burgess
@ 2023-03-16 16:39   ` Andrew Burgess
  2023-03-21 13:23     ` Pedro Alves
  2023-03-16 16:39   ` [PATCHv2 4/4] gdb: remove redundant signal passing Andrew Burgess
  2023-03-27 12:32   ` [PATCHv3 0/3] AMD64 Displaced Stepping Fix Andrew Burgess
  4 siblings, 1 reply; 36+ messages in thread
From: Andrew Burgess @ 2023-03-16 16:39 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit aims to address a problem that exists with the current
approach to displaced stepping, and was identified in PR gdb/22921.

Displaced stepping is currently supported on AArch64, ARM, amd64,
i386, rs6000 (ppc), and s390.  Of these, I believe there is a problem
with the current approach which will impact amd64 and ARM, and can
lead to random register corruption when the inferior makes use of
asynchronous signals and GDB is using displaced stepping.

The problem can be found in displaced_step_buffers::finish in
displaced-stepping.c, and is this; after GDB tries to perform a
displaced step, and the inferior stops, GDB classifies the stop into
one of two states, either the displaced step succeeded, or the
displaced step failed.

If the displaced step succeeded then gdbarch_displaced_step_fixup is
called, which has the job of fixing up the state of the current
inferior as if the step had not been performed in a displaced manor.
This all seems just fine.

However, if the displaced step is considered to have not completed
then GDB doesn't call gdbarch_displaced_step_fixup, instead GDB
remains in displaced_step_buffers::finish and just performs a minimal
fixup which involves adjusting the program counter back to its
original value.

The problem here is that for amd64 and ARM setting up for a displaced
step can involve changing the values in some temporary registers.  If
the displaced step succeeds then this is fine; after the step the
temporary registers are restored to their original values in the
architecture specific code.

But if the displaced step does not succeed then the temporary
registers are never restored, and they retain their modified values.

In this context a temporary register is simply any register that is
not otherwise used by the instruction being stepped that the
architecture specific code considers safe to borrow for the lifetime
of the instruction being stepped.

In the bug PR gdb/22921, the amd64 instruction being stepped is
an rip-relative instruction like this:

  jmp    *0x2fe2(%rip)

When we displaced step this instruction we borrow a register, and
modify the instruction to something like:

  jmp    *0x2fe2(%rcx)

with %rcx having its value adjusted to contain the original %rip
value.

Now if the displaced step does not succeed then %rcx will be left with
a corrupted value.  Obviously corrupting any register is bad; in the
bug report this problem was spotted because %rcx is used as a function
argument register.

And finally, why might a displaced step not succeed?  Asynchronous
signals provides one reason.  GDB sets up for the displaced step and,
at that precise moment, the OS delivers a signal (SIGALRM in the
test), the signal stops the inferior at the address of the displaced
instruction.  GDB cancels the displaced instruction, handles the
signal, and then tries again with the displaced step.  But it is that
first cancellation of the displaced step that causes the problem; in
that case GDB (correctly) sees the displaced step as having not
completed, and so does not perform the architecture specific fixup,
leaving the register corrupted.

The reason why I think AArch64, rs600, i386, and s390 are not effected
by this problem is that I don't believe these architectures make use
of any temporary registers, so when a displaced step is not completed
successfully, the minimal fix up is sufficient.

On amd64 we use at most one temporary register.

On ARM, looking at arm_displaced_step_copy_insn_closure, we could
modify up to 16 temporary registers, and the instruction being
displaced stepped could be expanded to multiple replacement
instructions, which increases the chances of this bug triggering.

This commit only aims to address the issue on amd64 for now, though I
believe that the approach I'm proposing here might be applicable for
ARM too.

What I propose is that we always call gdbarch_displaced_step_fixup.

We will now pass an extra argument to gdbarch_displaced_step_fixup,
this is the program-counter value at which the inferior stopped.

Using this program-counter value GDB can determine if the displaced
stepped instruction completed or not.  Or (in the future) for ARM, how
far through the displaced step sequence the inferior got before being
interrupted.

It is then possible for the architecture specific code in GDB to
conditionally roll back some or all of the instructions effects,
i.e. on amd64 we can roll back the temporary register value in all
cases.

In order to move all architectures to this new API, I have moved the
minimal roll-back version of the code inside the architecture specific
fixup functions for AArch64, rs600, s390, and ARM.  For all of these
except ARM I think this is good enough, as no temporaries are used all
that's needed is the program counter restore anyway.

For ARM the minimal code is no worse than what we had before, though I
do consider this architectures displaced-stepping broken.

For amd64 and i386 I make use of the program counter to conditionally
roll back the inferior state.  For i386 this is not strictly
necessary, as no temporaries are used.  However, the structure of
i386_displaced_step_fixup is so similar to amd64_displaced_step_fixup
that is seems a shame to take a different approach in these two
functions.

One thing to note is that previously we determined if the displaced
step had completed based on how the inferior stopped.  Stopping with a
SIGTRAP we assumed meant that we had hit the breakpoint at the end of
the displaced instruction.  Anything else indicated the displaced
instruction had not completed successfully.

After this patch GDB now relies entirely on the program-counter value
to make this decision.  If the program-counter points that the start
of the displaced instruction buffer (i.e. it hasn't changed) then the
displaced step didn't complete, otherwise (for amd64 and i386, which
always replace with a single instruction), we assume the displaced
step did complete.

I've updated the gdb.arch/amd64-disp-step.exp test to cover the
'jmpq*' instruction that was causing problems in the original bug, and
also added support for testing the displaced step in the presence of
asynchronous signal delivery.

Finally, after this commit the 'sig' argument passed to
displaced_step_buffers::finish is redundant.  This redundancy can be
chased all the way back to displaced_step_finish in infrun.c, so to
strip it out is a significant change in its own right.  So I'm
deferring that until the next commit.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=22921
---
 gdb/aarch64-tdep.c                            |  19 +++-
 gdb/aarch64-tdep.h                            |   3 +-
 gdb/amd64-tdep.c                              |  28 ++---
 gdb/amd64-tdep.h                              |   2 +-
 gdb/arm-tdep.c                                |  27 ++++-
 gdb/arm-tdep.h                                |   3 +-
 gdb/displaced-stepping.c                      |  41 +------
 gdb/gdbarch-gen.h                             |  25 +++--
 gdb/gdbarch.c                                 |   4 +-
 gdb/gdbarch_components.py                     |  22 ++--
 gdb/i386-tdep.c                               |  28 ++---
 gdb/i386-tdep.h                               |   2 +-
 gdb/rs6000-tdep.c                             |  15 ++-
 gdb/s390-tdep.c                               |  16 ++-
 .../gdb.arch/amd64-disp-step-signal.c         |  30 +++++
 gdb/testsuite/gdb.arch/amd64-disp-step.S      |  15 +++
 gdb/testsuite/gdb.arch/amd64-disp-step.exp    | 106 +++++++++++++++---
 17 files changed, 270 insertions(+), 116 deletions(-)
 create mode 100644 gdb/testsuite/gdb.arch/amd64-disp-step-signal.c

diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 5b1b9921f87..46c977140a7 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -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));
 
diff --git a/gdb/aarch64-tdep.h b/gdb/aarch64-tdep.h
index ae38327ffab..801a151913b 100644
--- a/gdb/aarch64-tdep.h
+++ b/gdb/aarch64-tdep.h
@@ -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);
 
diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index 81665e52d29..0a5a2209d12 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -1689,9 +1689,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);
@@ -1725,15 +1727,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
@@ -1749,24 +1749,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));
 	}
     }
@@ -1779,7 +1779,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;
diff --git a/gdb/amd64-tdep.h b/gdb/amd64-tdep.h
index 929b4b8bdc5..58953259765 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);
+   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.  */
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 883f8be296b..97e875bbbd3 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -8647,9 +8647,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_;
 
diff --git a/gdb/arm-tdep.h b/gdb/arm-tdep.h
index a8d21c44ba4..ab31d3e2e9c 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 *);
+				      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 *);
diff --git a/gdb/displaced-stepping.c b/gdb/displaced-stepping.c
index 5b94b2f068d..70e5ee28553 100644
--- a/gdb/displaced-stepping.c
+++ b/gdb/displaced-stepping.c
@@ -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 *
diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
index a3fc0b9272b..7464d52658c 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, 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.
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index b676e346fd0..9046b28f884 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, 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
diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
index 2b1a2b4f602..1d58f57c64f 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,20 +1781,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",
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 96c04c1a3d6..1231da8e92c 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -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;
diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h
index 371bce72369..1244b098a21 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);
+   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 *);
diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index 9859a7d8b85..e5a7ad500b0 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -951,9 +951,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
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
index cab1757c5ab..f1ed69ee0a4 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -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)
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
 }
 
 ##########################################
-- 
2.25.4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCHv2 4/4] gdb: remove redundant signal passing
  2023-03-16 16:39 ` [PATCHv2 0/4] AMD64 Displaced Stepping Fix Andrew Burgess
                     ` (2 preceding siblings ...)
  2023-03-16 16:39   ` [PATCHv2 3/4] gdb: fix reg corruption from displaced stepping on amd64 Andrew Burgess
@ 2023-03-16 16:39   ` Andrew Burgess
  2023-03-27 12:32   ` [PATCHv3 0/3] AMD64 Displaced Stepping Fix Andrew Burgess
  4 siblings, 0 replies; 36+ messages in thread
From: Andrew Burgess @ 2023-03-16 16:39 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

After the previous commit, this commit removes a redundant gdb_signal
argument that is passed to displaced_step_finish (in infrun.c) and
then all the way down to displaced_step_buffers::finish.

The argument is not used anywhere anymore, so there should be no user
visible changes after this commit.
---
 gdb/displaced-stepping.c  |  3 +--
 gdb/displaced-stepping.h  |  3 +--
 gdb/gdbarch-gen.h         |  4 ++--
 gdb/gdbarch.c             |  4 ++--
 gdb/gdbarch_components.py |  2 +-
 gdb/infrun.c              | 16 ++++++----------
 gdb/linux-tdep.c          |  4 ++--
 gdb/linux-tdep.h          |  2 +-
 gdb/rs6000-tdep.c         |  5 ++---
 9 files changed, 18 insertions(+), 25 deletions(-)

diff --git a/gdb/displaced-stepping.c b/gdb/displaced-stepping.c
index 70e5ee28553..21d0caf86fe 100644
--- a/gdb/displaced-stepping.c
+++ b/gdb/displaced-stepping.c
@@ -264,8 +264,7 @@ write_memory_ptid (ptid_t ptid, CORE_ADDR memaddr,
 }
 
 displaced_step_finish_status
-displaced_step_buffers::finish (gdbarch *arch, thread_info *thread,
-				gdb_signal sig)
+displaced_step_buffers::finish (gdbarch *arch, thread_info *thread)
 {
   gdb_assert (thread->displaced_step_state.in_progress ());
 
diff --git a/gdb/displaced-stepping.h b/gdb/displaced-stepping.h
index b3c5cdb1109..981933f9921 100644
--- a/gdb/displaced-stepping.h
+++ b/gdb/displaced-stepping.h
@@ -167,8 +167,7 @@ struct displaced_step_buffers
   displaced_step_prepare_status prepare (thread_info *thread,
 					 CORE_ADDR &displaced_pc);
 
-  displaced_step_finish_status finish (gdbarch *arch, thread_info *thread,
-				       gdb_signal sig);
+  displaced_step_finish_status finish (gdbarch *arch, thread_info *thread);
 
   const displaced_step_copy_insn_closure *
     copy_insn_closure_by_addr (CORE_ADDR addr);
diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
index 7464d52658c..bdad86927de 100644
--- a/gdb/gdbarch-gen.h
+++ b/gdb/gdbarch-gen.h
@@ -1108,8 +1108,8 @@ extern void set_gdbarch_displaced_step_prepare (struct gdbarch *gdbarch, gdbarch
 
 /* Clean up after a displaced step of THREAD. */
 
-typedef displaced_step_finish_status (gdbarch_displaced_step_finish_ftype) (struct gdbarch *gdbarch, thread_info *thread, gdb_signal sig);
-extern displaced_step_finish_status gdbarch_displaced_step_finish (struct gdbarch *gdbarch, thread_info *thread, gdb_signal sig);
+typedef displaced_step_finish_status (gdbarch_displaced_step_finish_ftype) (struct gdbarch *gdbarch, thread_info *thread);
+extern displaced_step_finish_status gdbarch_displaced_step_finish (struct gdbarch *gdbarch, thread_info *thread);
 extern void set_gdbarch_displaced_step_finish (struct gdbarch *gdbarch, gdbarch_displaced_step_finish_ftype *displaced_step_finish);
 
 /* Return the closure associated to the displaced step buffer that is at ADDR. */
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index 9046b28f884..eca328afd21 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -4098,13 +4098,13 @@ set_gdbarch_displaced_step_prepare (struct gdbarch *gdbarch,
 }
 
 displaced_step_finish_status
-gdbarch_displaced_step_finish (struct gdbarch *gdbarch, thread_info *thread, gdb_signal sig)
+gdbarch_displaced_step_finish (struct gdbarch *gdbarch, thread_info *thread)
 {
   gdb_assert (gdbarch != NULL);
   gdb_assert (gdbarch->displaced_step_finish != NULL);
   if (gdbarch_debug >= 2)
     gdb_printf (gdb_stdlog, "gdbarch_displaced_step_finish called\n");
-  return gdbarch->displaced_step_finish (gdbarch, thread, sig);
+  return gdbarch->displaced_step_finish (gdbarch, thread);
 }
 
 void
diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
index 1d58f57c64f..77a3bfa4976 100644
--- a/gdb/gdbarch_components.py
+++ b/gdb/gdbarch_components.py
@@ -1827,7 +1827,7 @@ Clean up after a displaced step of THREAD.
 """,
     type="displaced_step_finish_status",
     name="displaced_step_finish",
-    params=[("thread_info *", "thread"), ("gdb_signal", "sig")],
+    params=[("thread_info *", "thread")],
     predefault="NULL",
     invalid="(! gdbarch->displaced_step_finish) != (! gdbarch->displaced_step_prepare)",
 )
diff --git a/gdb/infrun.c b/gdb/infrun.c
index e73e8f16902..754c13b0f3d 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -1877,7 +1877,7 @@ displaced_step_prepare (thread_info *thread)
    DISPLACED_STEP_FINISH_STATUS_OK as well.  */
 
 static displaced_step_finish_status
-displaced_step_finish (thread_info *event_thread, enum gdb_signal signal)
+displaced_step_finish (thread_info *event_thread)
 {
   displaced_step_thread_state *displaced = &event_thread->displaced_step_state;
 
@@ -1899,7 +1899,7 @@ displaced_step_finish (thread_info *event_thread, enum gdb_signal signal)
   /* Do the fixup, and release the resources acquired to do the displaced
      step. */
   return gdbarch_displaced_step_finish (displaced->get_original_gdbarch (),
-					event_thread, signal);
+					event_thread);
 }
 
 /* Data to be passed around while handling an event.  This data is
@@ -5087,7 +5087,7 @@ handle_one (const wait_one_event &event)
 	  /* We caught the event that we intended to catch, so
 	     there's no event to save as pending.  */
 
-	  if (displaced_step_finish (t, GDB_SIGNAL_0)
+	  if (displaced_step_finish (t)
 	      == DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED)
 	    {
 	      /* Add it back to the step-over queue.  */
@@ -5102,7 +5102,6 @@ handle_one (const wait_one_event &event)
 	}
       else
 	{
-	  enum gdb_signal sig;
 	  struct regcache *regcache;
 
 	  infrun_debug_printf
@@ -5113,10 +5112,7 @@ handle_one (const wait_one_event &event)
 	  /* Record for later.  */
 	  save_waitstatus (t, event.ws);
 
-	  sig = (event.ws.kind () == TARGET_WAITKIND_STOPPED
-		 ? event.ws.sig () : GDB_SIGNAL_0);
-
-	  if (displaced_step_finish (t, sig)
+	  if (displaced_step_finish (t)
 	      == DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED)
 	    {
 	      /* Add it back to the step-over queue.  */
@@ -5718,7 +5714,7 @@ handle_inferior_event (struct execution_control_state *ecs)
 	       has been done.  Perform cleanup for parent process here.  Note
 	       that this operation also cleans up the child process for vfork,
 	       because their pages are shared.  */
-	    displaced_step_finish (ecs->event_thread, GDB_SIGNAL_TRAP);
+	    displaced_step_finish (ecs->event_thread);
 	    /* Start a new step-over in another thread if there's one
 	       that needs it.  */
 	    start_step_over ();
@@ -6083,7 +6079,7 @@ resumed_thread_with_pending_status (struct thread_info *tp,
 static int
 finish_step_over (struct execution_control_state *ecs)
 {
-  displaced_step_finish (ecs->event_thread, ecs->event_thread->stop_signal ());
+  displaced_step_finish (ecs->event_thread);
 
   bool had_step_over_info = step_over_info_valid_p ();
 
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 3eaa5f33584..d0c2ef76576 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -2621,13 +2621,13 @@ linux_displaced_step_prepare (gdbarch *arch, thread_info *thread,
 /* See linux-tdep.h.  */
 
 displaced_step_finish_status
-linux_displaced_step_finish (gdbarch *arch, thread_info *thread, gdb_signal sig)
+linux_displaced_step_finish (gdbarch *arch, thread_info *thread)
 {
   linux_info *per_inferior = get_linux_inferior_data (thread->inf);
 
   gdb_assert (per_inferior->disp_step_bufs.has_value ());
 
-  return per_inferior->disp_step_bufs->finish (arch, thread, sig);
+  return per_inferior->disp_step_bufs->finish (arch, thread);
 }
 
 /* See linux-tdep.h.  */
diff --git a/gdb/linux-tdep.h b/gdb/linux-tdep.h
index 16e1b806b26..a6204801275 100644
--- a/gdb/linux-tdep.h
+++ b/gdb/linux-tdep.h
@@ -72,7 +72,7 @@ extern displaced_step_prepare_status linux_displaced_step_prepare
 /* Implementation of gdbarch_displaced_step_finish.  */
 
 extern displaced_step_finish_status linux_displaced_step_finish
-  (gdbarch *arch, thread_info *thread, gdb_signal sig);
+  (gdbarch *arch, thread_info *thread);
 
 /* Implementation of gdbarch_displaced_step_copy_insn_closure_by_addr.  */
 
diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index e5a7ad500b0..3ae1cbd6f58 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -1099,14 +1099,13 @@ ppc_displaced_step_prepare  (gdbarch *arch, thread_info *thread,
 /* Implementation of gdbarch_displaced_step_finish.  */
 
 static displaced_step_finish_status
-ppc_displaced_step_finish (gdbarch *arch, thread_info *thread,
-			   gdb_signal sig)
+ppc_displaced_step_finish (gdbarch *arch, thread_info *thread)
 {
   ppc_inferior_data *per_inferior = get_ppc_per_inferior (thread->inf);
 
   gdb_assert (per_inferior->disp_step_buf.has_value ());
 
-  return per_inferior->disp_step_buf->finish (arch, thread, sig);
+  return per_inferior->disp_step_buf->finish (arch, thread);
 }
 
 /* Implementation of gdbarch_displaced_step_restore_all_in_ptid.  */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCHv2 1/4] gdb: more debug output for displaced stepping
  2023-03-16 16:39   ` [PATCHv2 1/4] gdb: more debug output for displaced stepping Andrew Burgess
@ 2023-03-21 12:29     ` Pedro Alves
  2023-03-21 14:41       ` Simon Marchi
  2023-03-21 14:45     ` [PATCHv2 1/4] gdb: more debug output for displaced stepping Simon Marchi
  1 sibling, 1 reply; 36+ messages in thread
From: Pedro Alves @ 2023-03-21 12:29 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches, Simon Marchi

On 2023-03-16 4:39 p.m., Andrew Burgess via Gdb-patches wrote:
> While investigating a displaced stepping issue I wanted an easy way to
> see what GDB thought the original instruction was, and what
> instruction GDB replaced that with when performing the displaced step.
> 
> We do print out the address that is being stepped, so I can track down
> the original instruction.
> 
> And we do print out the bytes of the new instruction, so I can figure
> out what the replacement instruction was, but it's not really easy.
> 
> In this commit, within displaced_step_buffers::prepare, I add new
> debug output which disassembles both the original instruction, and the
> replacement instruction, and prints both these instructions, along
> with the bytes that make these instructions, to the debug output.
> 
> This new debug improved on some debug that was already present in
> infrun.c, however, the old infrun.c debug (a) didn't actually
> disassemble the instruction, it just printed the bytes, and (b) there
> was an assumption that all instructions are 4-bytes long, which
> clearly isn't correct.
> 
> I think it makes more sense to have all this debug in the
> displaced-stepping.c file, so I've removed the infrun.c code, and
> added my new debug in displaced-stepping.c.

A downside of that move is that AMD GPUs (amd-dbgapi-target.c) actually support
displaced stepping (though that part of the port hasn't been upstreamed yet), but
we don't use displaced_step_buffers there.  So we'll be losing the debug output
in that target.  Adding Simon who may want to look into that.

Pedro Alves

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCHv2 2/4] gdb: remove gdbarch_displaced_step_fixup_p
  2023-03-16 16:39   ` [PATCHv2 2/4] gdb: remove gdbarch_displaced_step_fixup_p Andrew Burgess
@ 2023-03-21 13:10     ` Pedro Alves
  2023-03-22 21:22       ` Andrew Burgess
  0 siblings, 1 reply; 36+ messages in thread
From: Pedro Alves @ 2023-03-21 13:10 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 2023-03-16 4:39 p.m., Andrew Burgess via Gdb-patches wrote:
> The comment on the gdbarch_displaced_step_fixup gdbarch method
> indicates that this method is optional and that GDB will perform some
> default if this method is not supplied.  As such we define a predicate
> gdbarch_displaced_step_fixup_p.
> 
> It may have been true at one point that the fixup method was optional,
> but it is no longer true.  If this method is not defined and GDB tries
> to complete a displaced step, then GDB is going to crash.
> 
> Additionally the gdbarch_displaced_step_fixup_p predicate is not used
> anywhere in GDB.
> 
> In this commit I have removed the gdbarch_displaced_step_fixup_p
> predicate, and I have updated the validation check for the
> gdbarch_displaced_step_fixup method; if the
> gdbarch_displaced_step_copy_insn method is defined then the fixup
> method must also be defined.
> 
> I believe I've manually checked all the current places where
> gdbarch_displaced_step_copy_insn is defined and they all also define
> the fixup method, so this change should cause no problems for anyone.
> 
> There should be no user visible changes after this commit.

Approved-By: Pedro Alves <pedro@palves.net>

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCHv2 3/4] gdb: fix reg corruption from displaced stepping on amd64
  2023-03-16 16:39   ` [PATCHv2 3/4] gdb: fix reg corruption from displaced stepping on amd64 Andrew Burgess
@ 2023-03-21 13:23     ` Pedro Alves
  0 siblings, 0 replies; 36+ messages in thread
From: Pedro Alves @ 2023-03-21 13:23 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 2023-03-16 4:39 p.m., Andrew Burgess via Gdb-patches wrote:
> This commit aims to address a problem that exists with the current
> approach to displaced stepping, and was identified in PR gdb/22921.
> 
> Displaced stepping is currently supported on AArch64, ARM, amd64,
> i386, rs6000 (ppc), and s390.  Of these, I believe there is a problem
> with the current approach which will impact amd64 and ARM, and can
> lead to random register corruption when the inferior makes use of
> asynchronous signals and GDB is using displaced stepping.
> 
> The problem can be found in displaced_step_buffers::finish in
> displaced-stepping.c, and is this; after GDB tries to perform a
> displaced step, and the inferior stops, GDB classifies the stop into
> one of two states, either the displaced step succeeded, or the
> displaced step failed.
> 
> If the displaced step succeeded then gdbarch_displaced_step_fixup is
> called, which has the job of fixing up the state of the current
> inferior as if the step had not been performed in a displaced manor.

typo: "manor" -> "manner"

> This all seems just fine.
> 
> However, if the displaced step is considered to have not completed
> then GDB doesn't call gdbarch_displaced_step_fixup, instead GDB
> remains in displaced_step_buffers::finish and just performs a minimal
> fixup which involves adjusting the program counter back to its
> original value.
> 
> The problem here is that for amd64 and ARM setting up for a displaced
> step can involve changing the values in some temporary registers.  If
> the displaced step succeeds then this is fine; after the step the
> temporary registers are restored to their original values in the
> architecture specific code.
> 
> But if the displaced step does not succeed then the temporary
> registers are never restored, and they retain their modified values.
> 
> In this context a temporary register is simply any register that is
> not otherwise used by the instruction being stepped that the
> architecture specific code considers safe to borrow for the lifetime
> of the instruction being stepped.
> 
> In the bug PR gdb/22921, the amd64 instruction being stepped is
> an rip-relative instruction like this:
> 
>   jmp    *0x2fe2(%rip)
> 
> When we displaced step this instruction we borrow a register, and
> modify the instruction to something like:
> 
>   jmp    *0x2fe2(%rcx)
> 
> with %rcx having its value adjusted to contain the original %rip
> value.
> 
> Now if the displaced step does not succeed then %rcx will be left with
> a corrupted value.  Obviously corrupting any register is bad; in the
> bug report this problem was spotted because %rcx is used as a function
> argument register.
> 
> And finally, why might a displaced step not succeed?  Asynchronous
> signals provides one reason.  GDB sets up for the displaced step and,
> at that precise moment, the OS delivers a signal (SIGALRM in the
> test), the signal stops the inferior at the address of the displaced
> instruction.  GDB cancels the displaced instruction, handles the
> signal, and then tries again with the displaced step.  But it is that
> first cancellation of the displaced step that causes the problem; in
> that case GDB (correctly) sees the displaced step as having not
> completed, and so does not perform the architecture specific fixup,
> leaving the register corrupted.
> 
> The reason why I think AArch64, rs600, i386, and s390 are not effected
> by this problem is that I don't believe these architectures make use
> of any temporary registers, so when a displaced step is not completed
> successfully, the minimal fix up is sufficient.
> 
> On amd64 we use at most one temporary register.
> 
> On ARM, looking at arm_displaced_step_copy_insn_closure, we could
> modify up to 16 temporary registers, and the instruction being
> displaced stepped could be expanded to multiple replacement
> instructions, which increases the chances of this bug triggering.
> 
> This commit only aims to address the issue on amd64 for now, though I
> believe that the approach I'm proposing here might be applicable for
> ARM too.
> 

Agree with everything so far.

> What I propose is that we always call gdbarch_displaced_step_fixup.
> 
> We will now pass an extra argument to gdbarch_displaced_step_fixup,
> this is the program-counter value at which the inferior stopped.
> 
> Using this program-counter value GDB can determine if the displaced
> stepped instruction completed or not.  

This switching to comparing PC I disagree with.  I don't think that it is correct
to use the PC to determine if the displaced stepped instruction completed.  We can
simply be stepping over a jump to self instruction, in which case the PC will not
have changed in either success or failure scenarios.


> Or (in the future) for ARM, how
> far through the displaced step sequence the inferior got before being
> interrupted.

OK, that justifies passing down the PC reguardless, though it could be left
to do until ARM is actually fixed.

> 
> It is then possible for the architecture specific code in GDB to
> conditionally roll back some or all of the instructions effects,
> i.e. on amd64 we can roll back the temporary register value in all
> cases.
> 
> In order to move all architectures to this new API, I have moved the
> minimal roll-back version of the code inside the architecture specific
> fixup functions for AArch64, rs600, s390, and ARM.  For all of these
> except ARM I think this is good enough, as no temporaries are used all
> that's needed is the program counter restore anyway.
> 
> For ARM the minimal code is no worse than what we had before, though I
> do consider this architectures displaced-stepping broken.
> 
> For amd64 and i386 I make use of the program counter to conditionally
> roll back the inferior state.  For i386 this is not strictly
> necessary, as no temporaries are used.  However, the structure of
> i386_displaced_step_fixup is so similar to amd64_displaced_step_fixup
> that is seems a shame to take a different approach in these two
> functions.
> 
> One thing to note is that previously we determined if the displaced
> step had completed based on how the inferior stopped.  Stopping with a
> SIGTRAP we assumed meant that we had hit the breakpoint at the end of
> the displaced instruction.  Anything else indicated the displaced
> instruction had not completed successfully.
> 
> After this patch GDB now relies entirely on the program-counter value
> to make this decision.  If the program-counter points that the start
> of the displaced instruction buffer (i.e. it hasn't changed) then the
> displaced step didn't complete, otherwise (for amd64 and i386, which
> always replace with a single instruction), we assume the displaced
> step did complete.

I disagree with this part of the change.

Pedro Alves

> 
> I've updated the gdb.arch/amd64-disp-step.exp test to cover the
> 'jmpq*' instruction that was causing problems in the original bug, and
> also added support for testing the displaced step in the presence of
> asynchronous signal delivery.
> 
> Finally, after this commit the 'sig' argument passed to
> displaced_step_buffers::finish is redundant.  This redundancy can be
> chased all the way back to displaced_step_finish in infrun.c, so to
> strip it out is a significant change in its own right.  So I'm
> deferring that until the next commit.
> 
> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=22921




^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCHv2 1/4] gdb: more debug output for displaced stepping
  2023-03-21 12:29     ` Pedro Alves
@ 2023-03-21 14:41       ` Simon Marchi
  2023-03-22 21:17         ` [PATCHv2 0/2] displaced stepping debug improvements Andrew Burgess
  0 siblings, 1 reply; 36+ messages in thread
From: Simon Marchi @ 2023-03-21 14:41 UTC (permalink / raw)
  To: Pedro Alves, Andrew Burgess, gdb-patches

On 3/21/23 08:29, Pedro Alves wrote:
> On 2023-03-16 4:39 p.m., Andrew Burgess via Gdb-patches wrote:
>> While investigating a displaced stepping issue I wanted an easy way to
>> see what GDB thought the original instruction was, and what
>> instruction GDB replaced that with when performing the displaced step.
>>
>> We do print out the address that is being stepped, so I can track down
>> the original instruction.
>>
>> And we do print out the bytes of the new instruction, so I can figure
>> out what the replacement instruction was, but it's not really easy.
>>
>> In this commit, within displaced_step_buffers::prepare, I add new
>> debug output which disassembles both the original instruction, and the
>> replacement instruction, and prints both these instructions, along
>> with the bytes that make these instructions, to the debug output.
>>
>> This new debug improved on some debug that was already present in
>> infrun.c, however, the old infrun.c debug (a) didn't actually
>> disassemble the instruction, it just printed the bytes, and (b) there
>> was an assumption that all instructions are 4-bytes long, which
>> clearly isn't correct.
>>
>> I think it makes more sense to have all this debug in the
>> displaced-stepping.c file, so I've removed the infrun.c code, and
>> added my new debug in displaced-stepping.c.
> 
> A downside of that move is that AMD GPUs (amd-dbgapi-target.c) actually support
> displaced stepping (though that part of the port hasn't been upstreamed yet), but
> we don't use displaced_step_buffers there.  So we'll be losing the debug output
> in that target.  Adding Simon who may want to look into that.

I think that given the current code base in master, Andrew's change
makes sense.  I like that we get rid of the complex condition in
resume_1 (to know when to print debug output), since the debug print is
now placed in the code path where the displaced step is actually
prepared.  But perhaps the same can be achieved by placing the prints in
displaced_step_prepare_throw, and everyone is happy?

Simon

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCHv2 1/4] gdb: more debug output for displaced stepping
  2023-03-16 16:39   ` [PATCHv2 1/4] gdb: more debug output for displaced stepping Andrew Burgess
  2023-03-21 12:29     ` Pedro Alves
@ 2023-03-21 14:45     ` Simon Marchi
  1 sibling, 0 replies; 36+ messages in thread
From: Simon Marchi @ 2023-03-21 14:45 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 3/16/23 12:39, Andrew Burgess via Gdb-patches wrote:
> While investigating a displaced stepping issue I wanted an easy way to
> see what GDB thought the original instruction was, and what
> instruction GDB replaced that with when performing the displaced step.
> 
> We do print out the address that is being stepped, so I can track down
> the original instruction.
> 
> And we do print out the bytes of the new instruction, so I can figure
> out what the replacement instruction was, but it's not really easy.
> 
> In this commit, within displaced_step_buffers::prepare, I add new
> debug output which disassembles both the original instruction, and the
> replacement instruction, and prints both these instructions, along
> with the bytes that make these instructions, to the debug output.
> 
> This new debug improved on some debug that was already present in
> infrun.c, however, the old infrun.c debug (a) didn't actually
> disassemble the instruction, it just printed the bytes, and (b) there
> was an assumption that all instructions are 4-bytes long, which
> clearly isn't correct.
> 
> I think it makes more sense to have all this debug in the
> displaced-stepping.c file, so I've removed the infrun.c code, and
> added my new debug in displaced-stepping.c.
> 
> Here's an example of what the output looks like on x86-64 (this is
> with 'set debug displaced on').  The two interesting lines contain the
> strings 'original insn' and 'replacement insn':
> 
>   (gdb) step
>   [displaced] displaced_step_prepare_throw: displaced-stepping 2073893.2073893.0 now
>   [displaced] prepare: selected buffer at 0x401052
>   [displaced] prepare: saved 0x401052: 1e fa 31 ed 49 89 d1 5e 48 89 e2 48 83 e4 f0 50
>   [displaced] prepare:    original insn 0x401030: ff 25 e2 2f 00 00	jmp    *0x2fe2(%rip)        # 0x404018 <puts@got.plt>
>   [displaced] fixup_riprel: %rip-relative addressing used.
>   [displaced] fixup_riprel: using temp reg 2, old value 0x7ffff7f8a578, new value 0x401036
>   [displaced] amd64_displaced_step_copy_insn: copy 0x401030->0x401052: ff a1 e2 2f 00 00 68 00 00 00 00 e9 e0 ff ff ff
>   [displaced] prepare: replacement insn 0x401052: ff a1 e2 2f 00 00	jmp    *0x2fe2(%rcx)
>   [displaced] displaced_step_prepare_throw: prepared successfully thread=2073893.2073893.0, original_pc=0x401030, displaced_pc=0x401052
>   [displaced] finish: restored 2073893.2073893.0 0x401052
>   [displaced] amd64_displaced_step_fixup: fixup (0x401030, 0x401052), insn = 0xff 0xa1 ...
>   [displaced] amd64_displaced_step_fixup: restoring reg 2 to 0x7ffff7f8a578
>   0x00007ffff7e402c0 in puts () from /lib64/libc.so.6
>   (gdb)
> 
> One final note.  For many targets that support displaced stepping (in
> fact all targets except ARM) the replacement instruction is always a
> single instruction.  But on ARM the replacement could actually be a
> series of instructions.  The debug code tries to handle this by
> disassembling the entire displaced stepping buffer.  Obviously this
> might actually print more than is necessary, but there's (currently)
> no easy way to know how many instructions to disassemble; that
> knowledge is all locked in the architecture specific code.  Still I
> don't think it really hurts, if someone is looking at this debug then
> hopefully they known what to expect.

The obvious idea (although maybe not obvious to implement) is for the
arch code to return the number of bytes it used in the buffer.

> @@ -43,6 +44,24 @@ show_debug_displaced (struct ui_file *file, int from_tty,
>    gdb_printf (file, _("Displace stepping debugging is %s.\n"), value);
>  }
>  
> +/* See displaced-stepping.h.  */
> +
> +std::string
> +displaced_step_dump_bytes (const gdb_byte *buf, size_t len)
> +{
> +  std::string ret;
> +
> +  for (size_t i = 0; i < len; i++)
> +    {
> +      if (i == 0)
> +	ret += string_printf ("%02x", buf[i]);
> +      else
> +	ret += string_printf (" %02x", buf[i]);
> +    }
> +
> +  return ret;
> +}

Kinda unrelated to your change, but if you feel like it: this function
isn't really specific to displaced stepping.  I could see it living in
some util file in gdbsupport, as "dump_bytes".  It could also take a
gdb::array_view.

Simon

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCHv2 0/2] displaced stepping debug improvements
  2023-03-21 14:41       ` Simon Marchi
@ 2023-03-22 21:17         ` Andrew Burgess
  2023-03-22 21:17           ` [PATCHv2 1/2] gdb: more debug output for displaced stepping Andrew Burgess
                             ` (2 more replies)
  0 siblings, 3 replies; 36+ messages in thread
From: Andrew Burgess @ 2023-03-22 21:17 UTC (permalink / raw)
  To: gdb-patches; +Cc: Pedro Alves, Simon Marchi, Andrew Burgess

This mini series is just an expansion of the first patch from the
parent series: improvements to the displaced stepping debug output.

In V2:

  - As Simon suggested, the debug has now moved to
    displaced_step_prepare_throw, so the (not yet upstreamed)
    displaced stepping for the AMD GPU target should still see the
    debug output,

  - The debug code now handles a failure to disassemble better:
    there's nothing worse than enabling debug to try and solve a
    problem, and having GDB crash in a different way.  If an
    instruction fails to disassemble GDB will now print a basic debug
    message and skip the rest of the debug output,

  - As suggested, I've moved the displaced_step_dump_bytes helper
    function into gdbsupport/ and given it a better name,

  - I have NOT tried to implement the improvement Simon suggested
    where the architecture backend tells GDB core how many bytes the
    replacement instruction(s) occupied.  This still means that in
    some cases we will disassemble the entire displaced step buffer
    unnecessarily, but I don't see that as a huge problem.  Fixing
    this just to reduce some debug output a little seems excessive.
    Let me know if you feel this is a blocker for this work being
    merged and I can take another look at it.

Thanks,
Andrew

---

Andrew Burgess (2):
  gdb: more debug output for displaced stepping
  gdb: move displaced_step_dump_bytes into gdbsupport (and rename)

 gdb/amd64-tdep.c           |   2 +-
 gdb/displaced-stepping.c   |   3 +-
 gdb/i386-tdep.c            |   2 +-
 gdb/infrun.c               | 101 ++++++++++++++++++++++++-------------
 gdb/infrun.h               |   3 --
 gdb/rs6000-tdep.c          |   2 +-
 gdb/s390-tdep.c            |   2 +-
 gdbsupport/array-view.h    |   1 +
 gdbsupport/common-utils.cc |  18 +++++++
 gdbsupport/common-utils.h  |  15 ++++++
 10 files changed, 105 insertions(+), 44 deletions(-)


base-commit: bf3f6c02d73f9823b8cb4f59524f29fbbfb6126d
-- 
2.25.4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCHv2 1/2] gdb: more debug output for displaced stepping
  2023-03-22 21:17         ` [PATCHv2 0/2] displaced stepping debug improvements Andrew Burgess
@ 2023-03-22 21:17           ` Andrew Burgess
  2023-03-22 21:17           ` [PATCHv2 2/2] gdb: move displaced_step_dump_bytes into gdbsupport (and rename) Andrew Burgess
  2023-03-27 12:35           ` [PATCHv2 0/2] displaced stepping debug improvements Andrew Burgess
  2 siblings, 0 replies; 36+ messages in thread
From: Andrew Burgess @ 2023-03-22 21:17 UTC (permalink / raw)
  To: gdb-patches; +Cc: Pedro Alves, Simon Marchi, Andrew Burgess

While investigating a displaced stepping issue I wanted an easy way to
see what GDB thought the original instruction was, and what
instruction GDB replaced that with when performing the displaced step.

We do print out the address that is being stepped, so I can track down
the original instruction, I just need to go find the information
myself.

And we do print out the bytes of the new instruction, so I can figure
out what the replacement instruction was, but it's not really easy.

Also, the code that prints the bytes of the replacement instruction
only prints 4 bytes, which clearly isn't always going to be correct.

In this commit I remove the existing code that prints the bytes of the
replacement instruction, and add two new blocks of code to
displaced_step_prepare_throw.  This new code prints the original
instruction, and the replacement instruction.  In each case we print
both the bytes that make up the instruction and the completely
disassembled instruction.

Here's an example of what the output looks like on x86-64 (this is
with 'set debug displaced on').  The two interesting lines contain the
strings 'original insn' and 'replacement insn':

  (gdb) step
  [displaced] displaced_step_prepare_throw: displaced-stepping 2892655.2892655.0 now
  [displaced] displaced_step_prepare_throw: original insn 0x401030: ff 25 e2 2f 00 00	jmp    *0x2fe2(%rip)        # 0x404018 <puts@got.plt>
  [displaced] prepare: selected buffer at 0x401052
  [displaced] prepare: saved 0x401052: 1e fa 31 ed 49 89 d1 5e 48 89 e2 48 83 e4 f0 50
  [displaced] fixup_riprel: %rip-relative addressing used.
  [displaced] fixup_riprel: using temp reg 2, old value 0x7ffff7f8a578, new value 0x401036
  [displaced] amd64_displaced_step_copy_insn: copy 0x401030->0x401052: ff a1 e2 2f 00 00 68 00 00 00 00 e9 e0 ff ff ff
  [displaced] displaced_step_prepare_throw: prepared successfully thread=2892655.2892655.0, original_pc=0x401030, displaced_pc=0x401052
  [displaced] displaced_step_prepare_throw: replacement insn 0x401052: ff a1 e2 2f 00 00	jmp    *0x2fe2(%rcx)
  [displaced] finish: restored 2892655.2892655.0 0x401052
  [displaced] amd64_displaced_step_fixup: fixup (0x401030, 0x401052), insn = 0xff 0xa1 ...
  [displaced] amd64_displaced_step_fixup: restoring reg 2 to 0x7ffff7f8a578
  0x00007ffff7e402c0 in puts () from /lib64/libc.so.6
  (gdb)

One final note.  For many targets that support displaced stepping (in
fact all targets except ARM) the replacement instruction is always a
single instruction.  But on ARM the replacement could actually be a
series of instructions.

The debug code tries to handle this by disassembling the entire
displaced stepping buffer.  Obviously this might actually print more
than is necessary, but there's (currently) no easy way to know how
many instructions to disassemble; that knowledge is all locked in the
architecture specific code.  Still I don't think it really hurts, if
someone is looking at this debug then hopefully they known what to
expect.

Obviously we can imagine schemes where the architecture specific
displaced stepping code could communicate back how many bytes its
replacement sequence was, and then our debug print code could use this
to limit the disassembly.  But this seems like a lot of effort just to
save printing a few additional instructions in some debug output.

I'm not proposing to do anything about this issue for now.
---
 gdb/infrun.c | 85 +++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 68 insertions(+), 17 deletions(-)

diff --git a/gdb/infrun.c b/gdb/infrun.c
index 5c9babb9104..8c56a9a4dfb 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -74,6 +74,7 @@
 #include "gdbsupport/common-debug.h"
 #include "gdbsupport/buildargv.h"
 #include "extension.h"
+#include "disasm.h"
 
 /* Prototypes for local functions */
 
@@ -1807,6 +1808,31 @@ displaced_step_prepare_throw (thread_info *tp)
   CORE_ADDR original_pc = regcache_read_pc (regcache);
   CORE_ADDR displaced_pc;
 
+  /* Display the instruction we are going to displaced step.  */
+  if (debug_displaced)
+    {
+      string_file tmp_stream;
+      int dislen = gdb_print_insn (gdbarch, original_pc, &tmp_stream,
+				   nullptr);
+
+      if (dislen > 0)
+	{
+	  gdb::byte_vector insn_buf (dislen);
+	  read_memory (original_pc, insn_buf.data (), insn_buf.size ());
+
+	  std::string insn_bytes
+	    = displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ());
+
+	  displaced_debug_printf ("original insn %s: %s \t %s",
+				  paddress (gdbarch, original_pc),
+				  insn_bytes.c_str (),
+				  tmp_stream.string ().c_str ());
+	}
+      else
+	displaced_debug_printf ("replacement insn %s: invalid length: %d",
+				paddress (gdbarch, original_pc), dislen);
+    }
+
   displaced_step_prepare_status status
     = gdbarch_displaced_step_prepare (gdbarch, tp, displaced_pc);
 
@@ -1845,6 +1871,48 @@ displaced_step_prepare_throw (thread_info *tp)
 			  paddress (gdbarch, original_pc),
 			  paddress (gdbarch, displaced_pc));
 
+  /* Display the new displaced instruction(s).  */
+  if (debug_displaced)
+    {
+      string_file tmp_stream;
+      CORE_ADDR addr = displaced_pc;
+
+      /* If displaced stepping is going to use h/w single step then we know
+	 that the replacement instruction can only be a single instruction,
+	 in that case set the end address at the next byte.
+
+	 Otherwise the displaced stepping copy instruction routine could
+	 have generated multiple instructions, and all we know is that they
+	 must fit within the LEN bytes of the buffer.  */
+      CORE_ADDR end
+	= addr + (gdbarch_displaced_step_hw_singlestep (gdbarch)
+		  ? 1 : gdbarch_displaced_step_buffer_length (gdbarch));
+
+      while (addr < end)
+	{
+	  int dislen = gdb_print_insn (gdbarch, addr, &tmp_stream, nullptr);
+	  if (dislen <= 0)
+	    {
+	      displaced_debug_printf
+		("replacement insn %s: invalid length: %d",
+		 paddress (gdbarch, addr), dislen);
+	      break;
+	    }
+
+	  gdb::byte_vector insn_buf (dislen);
+	  read_memory (addr, insn_buf.data (), insn_buf.size ());
+
+	  std::string insn_bytes
+	    = displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ());
+	  std::string insn_str = tmp_stream.release ();
+	  displaced_debug_printf ("replacement insn %s: %s \t %s",
+				  paddress (gdbarch, addr),
+				  insn_bytes.c_str (),
+				  insn_str.c_str ());
+	  addr += dislen;
+	}
+    }
+
   return DISPLACED_STEP_PREPARE_STATUS_OK;
 }
 
@@ -2711,23 +2779,6 @@ resume_1 (enum gdb_signal sig)
 	step = false;
     }
 
-  if (debug_displaced
-      && tp->control.trap_expected
-      && use_displaced_stepping (tp)
-      && !step_over_info_valid_p ())
-    {
-      struct regcache *resume_regcache = get_thread_regcache (tp);
-      struct gdbarch *resume_gdbarch = resume_regcache->arch ();
-      CORE_ADDR actual_pc = regcache_read_pc (resume_regcache);
-      gdb_byte buf[4];
-
-      read_memory (actual_pc, buf, sizeof (buf));
-      displaced_debug_printf ("run %s: %s",
-			      paddress (resume_gdbarch, actual_pc),
-			      displaced_step_dump_bytes
-				(buf, sizeof (buf)).c_str ());
-    }
-
   if (tp->control.may_range_step)
     {
       /* If we're resuming a thread with the PC out of the step
-- 
2.25.4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCHv2 2/2] gdb: move displaced_step_dump_bytes into gdbsupport (and rename)
  2023-03-22 21:17         ` [PATCHv2 0/2] displaced stepping debug improvements Andrew Burgess
  2023-03-22 21:17           ` [PATCHv2 1/2] gdb: more debug output for displaced stepping Andrew Burgess
@ 2023-03-22 21:17           ` Andrew Burgess
  2023-03-27 12:35           ` [PATCHv2 0/2] displaced stepping debug improvements Andrew Burgess
  2 siblings, 0 replies; 36+ messages in thread
From: Andrew Burgess @ 2023-03-22 21:17 UTC (permalink / raw)
  To: gdb-patches; +Cc: Pedro Alves, Simon Marchi, Andrew Burgess

It was pointed out during review of another patch that the function
displaced_step_dump_bytes really isn't specific to displaced stepping,
and should really get a more generic name and move into gdbsupport/.

This commit does just that.  The function is renamed to
bytes_to_string and is moved into gdbsupport/common-utils.{cc,h}.  The
function implementation doesn't really change. Much...

... I have updated the function to take an array view, which makes it
slightly easier to call in a couple of places where we already have a
gdb::bytes_vector.  I've then added an inline wrapper to convert a raw
pointer and length into an array view, which is used in places where
we don't easily have a gdb::bytes_vector (or similar).

Updated all users of displaced_step_dump_bytes.

There should be no user visible changes after this commit.
---
 gdb/amd64-tdep.c           |  2 +-
 gdb/displaced-stepping.c   |  3 +--
 gdb/i386-tdep.c            |  2 +-
 gdb/infrun.c               | 24 ++----------------------
 gdb/infrun.h               |  3 ---
 gdb/rs6000-tdep.c          |  2 +-
 gdb/s390-tdep.c            |  2 +-
 gdbsupport/array-view.h    |  1 +
 gdbsupport/common-utils.cc | 18 ++++++++++++++++++
 gdbsupport/common-utils.h  | 15 +++++++++++++++
 10 files changed, 41 insertions(+), 31 deletions(-)

diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index 81665e52d29..228b7518cb0 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -1526,7 +1526,7 @@ amd64_displaced_step_copy_insn (struct gdbarch *gdbarch,
 
   displaced_debug_printf ("copy %s->%s: %s",
 			  paddress (gdbarch, from), paddress (gdbarch, to),
-			  displaced_step_dump_bytes (buf, len).c_str ());
+			  bytes_to_string (buf, len).c_str ());
 
   /* This is a work around for a problem with g++ 4.8.  */
   return displaced_step_copy_insn_closure_up (dsc.release ());
diff --git a/gdb/displaced-stepping.c b/gdb/displaced-stepping.c
index 9f98ea8c35b..534e031a88e 100644
--- a/gdb/displaced-stepping.c
+++ b/gdb/displaced-stepping.c
@@ -122,8 +122,7 @@ displaced_step_buffers::prepare (thread_info *thread, CORE_ADDR &displaced_pc)
 
   displaced_debug_printf ("saved %s: %s",
 			  paddress (arch, buffer->addr),
-			  displaced_step_dump_bytes
-			  (buffer->saved_copy.data (), len).c_str ());
+			  bytes_to_string (buffer->saved_copy).c_str ());
 
   /* Save this in a local variable first, so it's released if code below
      throws.  */
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 96c04c1a3d6..e93479c35a3 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -830,7 +830,7 @@ i386_displaced_step_copy_insn (struct gdbarch *gdbarch,
 
   displaced_debug_printf ("%s->%s: %s",
 			  paddress (gdbarch, from), paddress (gdbarch, to),
-			  displaced_step_dump_bytes (buf, len).c_str ());
+			  bytes_to_string (buf, len).c_str ());
 
   /* This is a work around for a problem with g++ 4.8.  */
   return displaced_step_copy_insn_closure_up (closure.release ());
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 8c56a9a4dfb..b021f40ae6e 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -1725,24 +1725,6 @@ displaced_step_reset (displaced_step_thread_state *displaced)
 
 using displaced_step_reset_cleanup = FORWARD_SCOPE_EXIT (displaced_step_reset);
 
-/* See infrun.h.  */
-
-std::string
-displaced_step_dump_bytes (const gdb_byte *buf, size_t len)
-{
-  std::string ret;
-
-  for (size_t i = 0; i < len; i++)
-    {
-      if (i == 0)
-	ret += string_printf ("%02x", buf[i]);
-      else
-	ret += string_printf (" %02x", buf[i]);
-    }
-
-  return ret;
-}
-
 /* Prepare to single-step, using displaced stepping.
 
    Note that we cannot use displaced stepping when we have a signal to
@@ -1820,8 +1802,7 @@ displaced_step_prepare_throw (thread_info *tp)
 	  gdb::byte_vector insn_buf (dislen);
 	  read_memory (original_pc, insn_buf.data (), insn_buf.size ());
 
-	  std::string insn_bytes
-	    = displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ());
+	  std::string insn_bytes = bytes_to_string (insn_buf);
 
 	  displaced_debug_printf ("original insn %s: %s \t %s",
 				  paddress (gdbarch, original_pc),
@@ -1902,8 +1883,7 @@ displaced_step_prepare_throw (thread_info *tp)
 	  gdb::byte_vector insn_buf (dislen);
 	  read_memory (addr, insn_buf.data (), insn_buf.size ());
 
-	  std::string insn_bytes
-	    = displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ());
+	  std::string insn_bytes = bytes_to_string (insn_buf);
 	  std::string insn_str = tmp_stream.release ();
 	  displaced_debug_printf ("replacement insn %s: %s \t %s",
 				  paddress (gdbarch, addr),
diff --git a/gdb/infrun.h b/gdb/infrun.h
index 5219063586d..9b3c8962939 100644
--- a/gdb/infrun.h
+++ b/gdb/infrun.h
@@ -270,9 +270,6 @@ extern void update_signals_program_target (void);
    $_exitsignal.  */
 extern void clear_exit_convenience_vars (void);
 
-/* Dump LEN bytes at BUF in hex to a string and return it.  */
-extern std::string displaced_step_dump_bytes (const gdb_byte *buf, size_t len);
-
 extern void update_observer_mode (void);
 
 extern void signal_catch_update (const unsigned int *);
diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index 52dcc89b2df..8e37c3d5183 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -940,7 +940,7 @@ ppc_displaced_step_copy_insn (struct gdbarch *gdbarch,
 
   displaced_debug_printf ("copy %s->%s: %s",
 			  paddress (gdbarch, from), paddress (gdbarch, to),
-			  displaced_step_dump_bytes (buf, len).c_str ());
+			  bytes_to_string (buf, len).c_str ());
 
   /* This is a work around for a problem with g++ 4.8.  */
   return displaced_step_copy_insn_closure_up (closure.release ());
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
index cab1757c5ab..081a8b68867 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -469,7 +469,7 @@ s390_displaced_step_copy_insn (struct gdbarch *gdbarch,
 
   displaced_debug_printf ("copy %s->%s: %s",
 			  paddress (gdbarch, from), paddress (gdbarch, to),
-			  displaced_step_dump_bytes (buf, len).c_str ());
+			  bytes_to_string (buf, len).c_str ());
 
   /* This is a work around for a problem with g++ 4.8.  */
   return displaced_step_copy_insn_closure_up (closure.release ());
diff --git a/gdbsupport/array-view.h b/gdbsupport/array-view.h
index 3d8248b08b7..d07c8bc53fc 100644
--- a/gdbsupport/array-view.h
+++ b/gdbsupport/array-view.h
@@ -21,6 +21,7 @@
 #include "traits.h"
 #include <algorithm>
 #include <type_traits>
+#include "gdbsupport/gdb_assert.h"
 
 /* An array_view is an abstraction that provides a non-owning view
    over a sequence of contiguous objects.
diff --git a/gdbsupport/common-utils.cc b/gdbsupport/common-utils.cc
index e382fb28d8f..8e13d2f218b 100644
--- a/gdbsupport/common-utils.cc
+++ b/gdbsupport/common-utils.cc
@@ -445,3 +445,21 @@ hex2bin (const char *hex)
 
   return bin;
 }
+
+/* See gdbsupport/common-utils.h.  */
+
+std::string
+bytes_to_string (gdb::array_view<gdb_byte> bytes)
+{
+  std::string ret;
+
+  for (size_t i = 0; i < bytes.size (); i++)
+    {
+      if (i == 0)
+	ret += string_printf ("%02x", bytes[i]);
+      else
+	ret += string_printf (" %02x", bytes[i]);
+    }
+
+  return ret;
+}
diff --git a/gdbsupport/common-utils.h b/gdbsupport/common-utils.h
index 97dcb9fa8ce..8ee4549a43d 100644
--- a/gdbsupport/common-utils.h
+++ b/gdbsupport/common-utils.h
@@ -24,6 +24,7 @@
 #include <vector>
 #include "gdbsupport/byte-vector.h"
 #include "gdbsupport/gdb_unique_ptr.h"
+#include "gdbsupport/array-view.h"
 #include "poison.h"
 #include "gdb_string_view.h"
 
@@ -194,6 +195,20 @@ extern int hex2bin (const char *hex, gdb_byte *bin, int count);
 /* Like the above, but return a gdb::byte_vector.  */
 gdb::byte_vector hex2bin (const char *hex);
 
+/* Build a string containing the contents of BYTES.  Each byte is
+   represented as a 2 character hex string, with spaces separating each
+   individual byte.  */
+
+extern std::string bytes_to_string (gdb::array_view<gdb_byte> bytes);
+
+/* See bytes_to_string above.  This takes a BUFFER pointer and LENGTH
+   rather than an array view.  */
+
+static inline std::string bytes_to_string (gdb_byte *buffer, size_t length)
+{
+  return bytes_to_string ({buffer, length});
+}
+
 /* A fast hashing function.  This can be used to hash data in a fast way
    when the length is known.  If no fast hashing library is available, falls
    back to iterative_hash from libiberty.  START_VALUE can be set to
-- 
2.25.4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCHv2 2/4] gdb: remove gdbarch_displaced_step_fixup_p
  2023-03-21 13:10     ` Pedro Alves
@ 2023-03-22 21:22       ` Andrew Burgess
  0 siblings, 0 replies; 36+ messages in thread
From: Andrew Burgess @ 2023-03-22 21:22 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

Pedro Alves <pedro@palves.net> writes:

> On 2023-03-16 4:39 p.m., Andrew Burgess via Gdb-patches wrote:
>> The comment on the gdbarch_displaced_step_fixup gdbarch method
>> indicates that this method is optional and that GDB will perform some
>> default if this method is not supplied.  As such we define a predicate
>> gdbarch_displaced_step_fixup_p.
>> 
>> It may have been true at one point that the fixup method was optional,
>> but it is no longer true.  If this method is not defined and GDB tries
>> to complete a displaced step, then GDB is going to crash.
>> 
>> Additionally the gdbarch_displaced_step_fixup_p predicate is not used
>> anywhere in GDB.
>> 
>> In this commit I have removed the gdbarch_displaced_step_fixup_p
>> predicate, and I have updated the validation check for the
>> gdbarch_displaced_step_fixup method; if the
>> gdbarch_displaced_step_copy_insn method is defined then the fixup
>> method must also be defined.
>> 
>> I believe I've manually checked all the current places where
>> gdbarch_displaced_step_copy_insn is defined and they all also define
>> the fixup method, so this change should cause no problems for anyone.
>> 
>> There should be no user visible changes after this commit.
>
> Approved-By: Pedro Alves <pedro@palves.net>

Pushed just this patch for now.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCHv3 0/3] AMD64 Displaced Stepping Fix
  2023-03-16 16:39 ` [PATCHv2 0/4] AMD64 Displaced Stepping Fix Andrew Burgess
                     ` (3 preceding siblings ...)
  2023-03-16 16:39   ` [PATCHv2 4/4] gdb: remove redundant signal passing Andrew Burgess
@ 2023-03-27 12:32   ` Andrew Burgess
  2023-03-27 12:32     ` [PATCHv3 1/3] gdb: more debug output for displaced stepping Andrew Burgess
                       ` (4 more replies)
  4 siblings, 5 replies; 36+ messages in thread
From: Andrew Burgess @ 2023-03-27 12:32 UTC (permalink / raw)
  To: gdb-patches; +Cc: Pedro Alves, Simon Marchi, Andrew Burgess

In V3:

  - Change of direction since v2.  As Pedro pointed out, using the $pc
    value to decide if the displaced step succeeded is not good
    enough.  This new version switches back to using the
    target_waitstatus value.

  - The target_waitstatus is examined, and the result (a bool) is
    passed to each architecture's fixup routine.

  - Just like in v2, each fixup routine is updated, with amd64 being
    "fixed", while aarch64, ppc, and s390 (which aren't broken) just
    have an early return case added to handle the unsuccessful
    displaced step case.

  - As with v2, ARM is left broken, though this is no more broken than
    it was before this patch series.

  - As Simon suggested, the debug has now moved to
    displaced_step_prepare_throw, so the (not yet upstreamed)
    displaced stepping for the AMD GPU target should still see the
    debug output,

  - The debug code now handles a failure to disassemble better:
    there's nothing worse than enabling debug to try and solve a
    problem, and having GDB crash in a different way.  If an
    instruction fails to disassemble GDB will now print a basic debug
    message and skip the rest of the debug output,

  - As suggested, I've moved the displaced_step_dump_bytes helper
    function into gdbsupport/ and given it a better name,

  - I have NOT tried to implement the improvement Simon suggested
    where the architecture backend tells GDB core how many bytes the
    replacement instruction(s) occupied.  This still means that in
    some cases we will disassemble the entire displaced step buffer
    unnecessarily, but I don't see that as a huge problem.  Fixing
    this just to reduce some debug output a little seems excessive.
    Let me know if you feel this is a blocker for this work being
    merged and I can take another look at it.

Thanks,
Andrew

---

Andrew Burgess (3):
  gdb: more debug output for displaced stepping
  gdb: move displaced_step_dump_bytes into gdbsupport (and rename)
  gdb: fix reg corruption from displaced stepping on amd64

 gdb/aarch64-tdep.c                            |  17 ++-
 gdb/aarch64-tdep.h                            |   2 +-
 gdb/amd64-tdep.c                              |  29 +++--
 gdb/amd64-tdep.h                              |   2 +-
 gdb/arm-tdep.c                                |  26 ++++-
 gdb/arm-tdep.h                                |   3 +-
 gdb/displaced-stepping.c                      |  26 ++---
 gdb/gdbarch-gen.h                             |  25 +++--
 gdb/gdbarch.c                                 |   4 +-
 gdb/gdbarch_components.py                     |  22 +++-
 gdb/i386-tdep.c                               |  26 ++---
 gdb/i386-tdep.h                               |   2 +-
 gdb/infrun.c                                  | 101 +++++++++++------
 gdb/infrun.h                                  |   3 -
 gdb/rs6000-tdep.c                             |  14 ++-
 gdb/s390-tdep.c                               |  16 ++-
 .../amd64-disp-step-self-call-alarm.c         |  24 ++++
 .../gdb.arch/amd64-disp-step-self-call.S      |  50 +++++++++
 .../gdb.arch/amd64-disp-step-self-call.exp    |  81 +++++++++++++
 .../gdb.arch/amd64-disp-step-signal.c         |  30 +++++
 gdb/testsuite/gdb.arch/amd64-disp-step.S      |  15 +++
 gdb/testsuite/gdb.arch/amd64-disp-step.exp    | 106 +++++++++++++++---
 .../gdb.arch/i386-disp-step-self-call-alarm.c |  24 ++++
 .../gdb.arch/i386-disp-step-self-call.S       |  50 +++++++++
 .../gdb.arch/i386-disp-step-self-call.exp     |  81 +++++++++++++
 gdbsupport/array-view.h                       |   1 +
 gdbsupport/common-utils.cc                    |  18 +++
 gdbsupport/common-utils.h                     |  15 +++
 28 files changed, 679 insertions(+), 134 deletions(-)
 create mode 100644 gdb/testsuite/gdb.arch/amd64-disp-step-self-call-alarm.c
 create mode 100644 gdb/testsuite/gdb.arch/amd64-disp-step-self-call.S
 create mode 100644 gdb/testsuite/gdb.arch/amd64-disp-step-self-call.exp
 create mode 100644 gdb/testsuite/gdb.arch/amd64-disp-step-signal.c
 create mode 100644 gdb/testsuite/gdb.arch/i386-disp-step-self-call-alarm.c
 create mode 100644 gdb/testsuite/gdb.arch/i386-disp-step-self-call.S
 create mode 100644 gdb/testsuite/gdb.arch/i386-disp-step-self-call.exp


base-commit: 1770eca698ad0018cd504b9744306fc9928d9a86
-- 
2.25.4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCHv3 1/3] gdb: more debug output for displaced stepping
  2023-03-27 12:32   ` [PATCHv3 0/3] AMD64 Displaced Stepping Fix Andrew Burgess
@ 2023-03-27 12:32     ` Andrew Burgess
  2023-03-28 13:05       ` Simon Marchi
  2023-03-27 12:32     ` [PATCHv3 2/3] gdb: move displaced_step_dump_bytes into gdbsupport (and rename) Andrew Burgess
                       ` (3 subsequent siblings)
  4 siblings, 1 reply; 36+ messages in thread
From: Andrew Burgess @ 2023-03-27 12:32 UTC (permalink / raw)
  To: gdb-patches; +Cc: Pedro Alves, Simon Marchi, Andrew Burgess

While investigating a displaced stepping issue I wanted an easy way to
see what GDB thought the original instruction was, and what
instruction GDB replaced that with when performing the displaced step.

We do print out the address that is being stepped, so I can track down
the original instruction, I just need to go find the information
myself.

And we do print out the bytes of the new instruction, so I can figure
out what the replacement instruction was, but it's not really easy.

Also, the code that prints the bytes of the replacement instruction
only prints 4 bytes, which clearly isn't always going to be correct.

In this commit I remove the existing code that prints the bytes of the
replacement instruction, and add two new blocks of code to
displaced_step_prepare_throw.  This new code prints the original
instruction, and the replacement instruction.  In each case we print
both the bytes that make up the instruction and the completely
disassembled instruction.

Here's an example of what the output looks like on x86-64 (this is
with 'set debug displaced on').  The two interesting lines contain the
strings 'original insn' and 'replacement insn':

  (gdb) step
  [displaced] displaced_step_prepare_throw: displaced-stepping 2892655.2892655.0 now
  [displaced] displaced_step_prepare_throw: original insn 0x401030: ff 25 e2 2f 00 00	jmp    *0x2fe2(%rip)        # 0x404018 <puts@got.plt>
  [displaced] prepare: selected buffer at 0x401052
  [displaced] prepare: saved 0x401052: 1e fa 31 ed 49 89 d1 5e 48 89 e2 48 83 e4 f0 50
  [displaced] fixup_riprel: %rip-relative addressing used.
  [displaced] fixup_riprel: using temp reg 2, old value 0x7ffff7f8a578, new value 0x401036
  [displaced] amd64_displaced_step_copy_insn: copy 0x401030->0x401052: ff a1 e2 2f 00 00 68 00 00 00 00 e9 e0 ff ff ff
  [displaced] displaced_step_prepare_throw: prepared successfully thread=2892655.2892655.0, original_pc=0x401030, displaced_pc=0x401052
  [displaced] displaced_step_prepare_throw: replacement insn 0x401052: ff a1 e2 2f 00 00	jmp    *0x2fe2(%rcx)
  [displaced] finish: restored 2892655.2892655.0 0x401052
  [displaced] amd64_displaced_step_fixup: fixup (0x401030, 0x401052), insn = 0xff 0xa1 ...
  [displaced] amd64_displaced_step_fixup: restoring reg 2 to 0x7ffff7f8a578
  0x00007ffff7e402c0 in puts () from /lib64/libc.so.6
  (gdb)

One final note.  For many targets that support displaced stepping (in
fact all targets except ARM) the replacement instruction is always a
single instruction.  But on ARM the replacement could actually be a
series of instructions.

The debug code tries to handle this by disassembling the entire
displaced stepping buffer.  Obviously this might actually print more
than is necessary, but there's (currently) no easy way to know how
many instructions to disassemble; that knowledge is all locked in the
architecture specific code.  Still I don't think it really hurts, if
someone is looking at this debug then hopefully they known what to
expect.

Obviously we can imagine schemes where the architecture specific
displaced stepping code could communicate back how many bytes its
replacement sequence was, and then our debug print code could use this
to limit the disassembly.  But this seems like a lot of effort just to
save printing a few additional instructions in some debug output.

I'm not proposing to do anything about this issue for now.
---
 gdb/infrun.c | 85 +++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 68 insertions(+), 17 deletions(-)

diff --git a/gdb/infrun.c b/gdb/infrun.c
index 5c9babb9104..8c56a9a4dfb 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -74,6 +74,7 @@
 #include "gdbsupport/common-debug.h"
 #include "gdbsupport/buildargv.h"
 #include "extension.h"
+#include "disasm.h"
 
 /* Prototypes for local functions */
 
@@ -1807,6 +1808,31 @@ displaced_step_prepare_throw (thread_info *tp)
   CORE_ADDR original_pc = regcache_read_pc (regcache);
   CORE_ADDR displaced_pc;
 
+  /* Display the instruction we are going to displaced step.  */
+  if (debug_displaced)
+    {
+      string_file tmp_stream;
+      int dislen = gdb_print_insn (gdbarch, original_pc, &tmp_stream,
+				   nullptr);
+
+      if (dislen > 0)
+	{
+	  gdb::byte_vector insn_buf (dislen);
+	  read_memory (original_pc, insn_buf.data (), insn_buf.size ());
+
+	  std::string insn_bytes
+	    = displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ());
+
+	  displaced_debug_printf ("original insn %s: %s \t %s",
+				  paddress (gdbarch, original_pc),
+				  insn_bytes.c_str (),
+				  tmp_stream.string ().c_str ());
+	}
+      else
+	displaced_debug_printf ("replacement insn %s: invalid length: %d",
+				paddress (gdbarch, original_pc), dislen);
+    }
+
   displaced_step_prepare_status status
     = gdbarch_displaced_step_prepare (gdbarch, tp, displaced_pc);
 
@@ -1845,6 +1871,48 @@ displaced_step_prepare_throw (thread_info *tp)
 			  paddress (gdbarch, original_pc),
 			  paddress (gdbarch, displaced_pc));
 
+  /* Display the new displaced instruction(s).  */
+  if (debug_displaced)
+    {
+      string_file tmp_stream;
+      CORE_ADDR addr = displaced_pc;
+
+      /* If displaced stepping is going to use h/w single step then we know
+	 that the replacement instruction can only be a single instruction,
+	 in that case set the end address at the next byte.
+
+	 Otherwise the displaced stepping copy instruction routine could
+	 have generated multiple instructions, and all we know is that they
+	 must fit within the LEN bytes of the buffer.  */
+      CORE_ADDR end
+	= addr + (gdbarch_displaced_step_hw_singlestep (gdbarch)
+		  ? 1 : gdbarch_displaced_step_buffer_length (gdbarch));
+
+      while (addr < end)
+	{
+	  int dislen = gdb_print_insn (gdbarch, addr, &tmp_stream, nullptr);
+	  if (dislen <= 0)
+	    {
+	      displaced_debug_printf
+		("replacement insn %s: invalid length: %d",
+		 paddress (gdbarch, addr), dislen);
+	      break;
+	    }
+
+	  gdb::byte_vector insn_buf (dislen);
+	  read_memory (addr, insn_buf.data (), insn_buf.size ());
+
+	  std::string insn_bytes
+	    = displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ());
+	  std::string insn_str = tmp_stream.release ();
+	  displaced_debug_printf ("replacement insn %s: %s \t %s",
+				  paddress (gdbarch, addr),
+				  insn_bytes.c_str (),
+				  insn_str.c_str ());
+	  addr += dislen;
+	}
+    }
+
   return DISPLACED_STEP_PREPARE_STATUS_OK;
 }
 
@@ -2711,23 +2779,6 @@ resume_1 (enum gdb_signal sig)
 	step = false;
     }
 
-  if (debug_displaced
-      && tp->control.trap_expected
-      && use_displaced_stepping (tp)
-      && !step_over_info_valid_p ())
-    {
-      struct regcache *resume_regcache = get_thread_regcache (tp);
-      struct gdbarch *resume_gdbarch = resume_regcache->arch ();
-      CORE_ADDR actual_pc = regcache_read_pc (resume_regcache);
-      gdb_byte buf[4];
-
-      read_memory (actual_pc, buf, sizeof (buf));
-      displaced_debug_printf ("run %s: %s",
-			      paddress (resume_gdbarch, actual_pc),
-			      displaced_step_dump_bytes
-				(buf, sizeof (buf)).c_str ());
-    }
-
   if (tp->control.may_range_step)
     {
       /* If we're resuming a thread with the PC out of the step
-- 
2.25.4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCHv3 2/3] gdb: move displaced_step_dump_bytes into gdbsupport (and rename)
  2023-03-27 12:32   ` [PATCHv3 0/3] AMD64 Displaced Stepping Fix Andrew Burgess
  2023-03-27 12:32     ` [PATCHv3 1/3] gdb: more debug output for displaced stepping Andrew Burgess
@ 2023-03-27 12:32     ` Andrew Burgess
  2023-03-28 13:10       ` Simon Marchi
  2023-03-27 12:32     ` [PATCHv3 3/3] gdb: fix reg corruption from displaced stepping on amd64 Andrew Burgess
                       ` (2 subsequent siblings)
  4 siblings, 1 reply; 36+ messages in thread
From: Andrew Burgess @ 2023-03-27 12:32 UTC (permalink / raw)
  To: gdb-patches; +Cc: Pedro Alves, Simon Marchi, Andrew Burgess

It was pointed out during review of another patch that the function
displaced_step_dump_bytes really isn't specific to displaced stepping,
and should really get a more generic name and move into gdbsupport/.

This commit does just that.  The function is renamed to
bytes_to_string and is moved into gdbsupport/common-utils.{cc,h}.  The
function implementation doesn't really change. Much...

... I have updated the function to take an array view, which makes it
slightly easier to call in a couple of places where we already have a
gdb::bytes_vector.  I've then added an inline wrapper to convert a raw
pointer and length into an array view, which is used in places where
we don't easily have a gdb::bytes_vector (or similar).

Updated all users of displaced_step_dump_bytes.

There should be no user visible changes after this commit.
---
 gdb/amd64-tdep.c           |  2 +-
 gdb/displaced-stepping.c   |  3 +--
 gdb/i386-tdep.c            |  2 +-
 gdb/infrun.c               | 24 ++----------------------
 gdb/infrun.h               |  3 ---
 gdb/rs6000-tdep.c          |  2 +-
 gdb/s390-tdep.c            |  2 +-
 gdbsupport/array-view.h    |  1 +
 gdbsupport/common-utils.cc | 18 ++++++++++++++++++
 gdbsupport/common-utils.h  | 15 +++++++++++++++
 10 files changed, 41 insertions(+), 31 deletions(-)

diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index 81665e52d29..228b7518cb0 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -1526,7 +1526,7 @@ amd64_displaced_step_copy_insn (struct gdbarch *gdbarch,
 
   displaced_debug_printf ("copy %s->%s: %s",
 			  paddress (gdbarch, from), paddress (gdbarch, to),
-			  displaced_step_dump_bytes (buf, len).c_str ());
+			  bytes_to_string (buf, len).c_str ());
 
   /* This is a work around for a problem with g++ 4.8.  */
   return displaced_step_copy_insn_closure_up (dsc.release ());
diff --git a/gdb/displaced-stepping.c b/gdb/displaced-stepping.c
index 9f98ea8c35b..534e031a88e 100644
--- a/gdb/displaced-stepping.c
+++ b/gdb/displaced-stepping.c
@@ -122,8 +122,7 @@ displaced_step_buffers::prepare (thread_info *thread, CORE_ADDR &displaced_pc)
 
   displaced_debug_printf ("saved %s: %s",
 			  paddress (arch, buffer->addr),
-			  displaced_step_dump_bytes
-			  (buffer->saved_copy.data (), len).c_str ());
+			  bytes_to_string (buffer->saved_copy).c_str ());
 
   /* Save this in a local variable first, so it's released if code below
      throws.  */
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 96c04c1a3d6..e93479c35a3 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -830,7 +830,7 @@ i386_displaced_step_copy_insn (struct gdbarch *gdbarch,
 
   displaced_debug_printf ("%s->%s: %s",
 			  paddress (gdbarch, from), paddress (gdbarch, to),
-			  displaced_step_dump_bytes (buf, len).c_str ());
+			  bytes_to_string (buf, len).c_str ());
 
   /* This is a work around for a problem with g++ 4.8.  */
   return displaced_step_copy_insn_closure_up (closure.release ());
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 8c56a9a4dfb..b021f40ae6e 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -1725,24 +1725,6 @@ displaced_step_reset (displaced_step_thread_state *displaced)
 
 using displaced_step_reset_cleanup = FORWARD_SCOPE_EXIT (displaced_step_reset);
 
-/* See infrun.h.  */
-
-std::string
-displaced_step_dump_bytes (const gdb_byte *buf, size_t len)
-{
-  std::string ret;
-
-  for (size_t i = 0; i < len; i++)
-    {
-      if (i == 0)
-	ret += string_printf ("%02x", buf[i]);
-      else
-	ret += string_printf (" %02x", buf[i]);
-    }
-
-  return ret;
-}
-
 /* Prepare to single-step, using displaced stepping.
 
    Note that we cannot use displaced stepping when we have a signal to
@@ -1820,8 +1802,7 @@ displaced_step_prepare_throw (thread_info *tp)
 	  gdb::byte_vector insn_buf (dislen);
 	  read_memory (original_pc, insn_buf.data (), insn_buf.size ());
 
-	  std::string insn_bytes
-	    = displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ());
+	  std::string insn_bytes = bytes_to_string (insn_buf);
 
 	  displaced_debug_printf ("original insn %s: %s \t %s",
 				  paddress (gdbarch, original_pc),
@@ -1902,8 +1883,7 @@ displaced_step_prepare_throw (thread_info *tp)
 	  gdb::byte_vector insn_buf (dislen);
 	  read_memory (addr, insn_buf.data (), insn_buf.size ());
 
-	  std::string insn_bytes
-	    = displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ());
+	  std::string insn_bytes = bytes_to_string (insn_buf);
 	  std::string insn_str = tmp_stream.release ();
 	  displaced_debug_printf ("replacement insn %s: %s \t %s",
 				  paddress (gdbarch, addr),
diff --git a/gdb/infrun.h b/gdb/infrun.h
index 5219063586d..9b3c8962939 100644
--- a/gdb/infrun.h
+++ b/gdb/infrun.h
@@ -270,9 +270,6 @@ extern void update_signals_program_target (void);
    $_exitsignal.  */
 extern void clear_exit_convenience_vars (void);
 
-/* Dump LEN bytes at BUF in hex to a string and return it.  */
-extern std::string displaced_step_dump_bytes (const gdb_byte *buf, size_t len);
-
 extern void update_observer_mode (void);
 
 extern void signal_catch_update (const unsigned int *);
diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index 52dcc89b2df..8e37c3d5183 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -940,7 +940,7 @@ ppc_displaced_step_copy_insn (struct gdbarch *gdbarch,
 
   displaced_debug_printf ("copy %s->%s: %s",
 			  paddress (gdbarch, from), paddress (gdbarch, to),
-			  displaced_step_dump_bytes (buf, len).c_str ());
+			  bytes_to_string (buf, len).c_str ());
 
   /* This is a work around for a problem with g++ 4.8.  */
   return displaced_step_copy_insn_closure_up (closure.release ());
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
index cab1757c5ab..081a8b68867 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -469,7 +469,7 @@ s390_displaced_step_copy_insn (struct gdbarch *gdbarch,
 
   displaced_debug_printf ("copy %s->%s: %s",
 			  paddress (gdbarch, from), paddress (gdbarch, to),
-			  displaced_step_dump_bytes (buf, len).c_str ());
+			  bytes_to_string (buf, len).c_str ());
 
   /* This is a work around for a problem with g++ 4.8.  */
   return displaced_step_copy_insn_closure_up (closure.release ());
diff --git a/gdbsupport/array-view.h b/gdbsupport/array-view.h
index 3d8248b08b7..d07c8bc53fc 100644
--- a/gdbsupport/array-view.h
+++ b/gdbsupport/array-view.h
@@ -21,6 +21,7 @@
 #include "traits.h"
 #include <algorithm>
 #include <type_traits>
+#include "gdbsupport/gdb_assert.h"
 
 /* An array_view is an abstraction that provides a non-owning view
    over a sequence of contiguous objects.
diff --git a/gdbsupport/common-utils.cc b/gdbsupport/common-utils.cc
index e382fb28d8f..8e13d2f218b 100644
--- a/gdbsupport/common-utils.cc
+++ b/gdbsupport/common-utils.cc
@@ -445,3 +445,21 @@ hex2bin (const char *hex)
 
   return bin;
 }
+
+/* See gdbsupport/common-utils.h.  */
+
+std::string
+bytes_to_string (gdb::array_view<gdb_byte> bytes)
+{
+  std::string ret;
+
+  for (size_t i = 0; i < bytes.size (); i++)
+    {
+      if (i == 0)
+	ret += string_printf ("%02x", bytes[i]);
+      else
+	ret += string_printf (" %02x", bytes[i]);
+    }
+
+  return ret;
+}
diff --git a/gdbsupport/common-utils.h b/gdbsupport/common-utils.h
index 97dcb9fa8ce..8ee4549a43d 100644
--- a/gdbsupport/common-utils.h
+++ b/gdbsupport/common-utils.h
@@ -24,6 +24,7 @@
 #include <vector>
 #include "gdbsupport/byte-vector.h"
 #include "gdbsupport/gdb_unique_ptr.h"
+#include "gdbsupport/array-view.h"
 #include "poison.h"
 #include "gdb_string_view.h"
 
@@ -194,6 +195,20 @@ extern int hex2bin (const char *hex, gdb_byte *bin, int count);
 /* Like the above, but return a gdb::byte_vector.  */
 gdb::byte_vector hex2bin (const char *hex);
 
+/* Build a string containing the contents of BYTES.  Each byte is
+   represented as a 2 character hex string, with spaces separating each
+   individual byte.  */
+
+extern std::string bytes_to_string (gdb::array_view<gdb_byte> bytes);
+
+/* See bytes_to_string above.  This takes a BUFFER pointer and LENGTH
+   rather than an array view.  */
+
+static inline std::string bytes_to_string (gdb_byte *buffer, size_t length)
+{
+  return bytes_to_string ({buffer, length});
+}
+
 /* A fast hashing function.  This can be used to hash data in a fast way
    when the length is known.  If no fast hashing library is available, falls
    back to iterative_hash from libiberty.  START_VALUE can be set to
-- 
2.25.4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCHv3 3/3] gdb: fix reg corruption from displaced stepping on amd64
  2023-03-27 12:32   ` [PATCHv3 0/3] AMD64 Displaced Stepping Fix Andrew Burgess
  2023-03-27 12:32     ` [PATCHv3 1/3] gdb: more debug output for displaced stepping Andrew Burgess
  2023-03-27 12:32     ` [PATCHv3 2/3] gdb: move displaced_step_dump_bytes into gdbsupport (and rename) Andrew Burgess
@ 2023-03-27 12:32     ` Andrew Burgess
  2023-03-29  9:43       ` Pedro Alves
  2023-03-28 12:33     ` [PATCHv3 0/3] AMD64 Displaced Stepping Fix Simon Marchi
  2023-03-29 13:46     ` [PATCHv4] gdb: fix reg corruption from displaced stepping on amd64 Andrew Burgess
  4 siblings, 1 reply; 36+ messages in thread
From: Andrew Burgess @ 2023-03-27 12:32 UTC (permalink / raw)
  To: gdb-patches; +Cc: Pedro Alves, Simon Marchi, Andrew Burgess

This commit aims to address a problem that exists with the current
approach to displaced stepping, and was identified in PR gdb/22921.

Displaced stepping is currently supported on AArch64, ARM, amd64,
i386, rs6000 (ppc), and s390.  Of these, I believe there is a problem
with the current approach which will impact amd64 and ARM, and can
lead to random register corruption when the inferior makes use of
asynchronous signals and GDB is using displaced stepping.

The problem can be found in displaced_step_buffers::finish in
displaced-stepping.c, and is this; after GDB tries to perform a
displaced step, and the inferior stops, GDB classifies the stop into
one of two states, either the displaced step succeeded, or the
displaced step failed.

If the displaced step succeeded then gdbarch_displaced_step_fixup is
called, which has the job of fixing up the state of the current
inferior as if the step had not been performed in a displaced manner.
This all seems just fine.

However, if the displaced step is considered to have not completed
then GDB doesn't call gdbarch_displaced_step_fixup, instead GDB
remains in displaced_step_buffers::finish and just performs a minimal
fixup which involves adjusting the program counter back to its
original value.

The problem here is that for amd64 and ARM setting up for a displaced
step can involve changing the values in some temporary registers.  If
the displaced step succeeds then this is fine; after the step the
temporary registers are restored to their original values in the
architecture specific code.

But if the displaced step does not succeed then the temporary
registers are never restored, and they retain their modified values.

In this context a temporary register is simply any register that is
not otherwise used by the instruction being stepped that the
architecture specific code considers safe to borrow for the lifetime
of the instruction being stepped.

In the bug PR gdb/22921, the amd64 instruction being stepped is
an rip-relative instruction like this:

  jmp    *0x2fe2(%rip)

When we displaced step this instruction we borrow a register, and
modify the instruction to something like:

  jmp    *0x2fe2(%rcx)

with %rcx having its value adjusted to contain the original %rip
value.

Now if the displaced step does not succeed, then %rcx will be left
with a corrupted value.  Obviously corrupting any register is bad; in
the bug report this problem was spotted because %rcx is used as a
function argument register.

And finally, why might a displaced step not succeed?  Asynchronous
signals provides one reason.  GDB sets up for the displaced step and,
at that precise moment, the OS delivers a signal (SIGALRM in the bug
report), the signal stops the inferior at the address of the displaced
instruction.  GDB cancels the displaced instruction, handles the
signal, and then tries again with the displaced step.  But it is that
first cancellation of the displaced step that causes the problem; in
that case GDB (correctly) sees the displaced step as having not
completed, and so does not perform the architecture specific fixup,
leaving the register corrupted.

The reason why I think AArch64, rs600, i386, and s390 are not effected
by this problem is that I don't believe these architectures make use
of any temporary registers, so when a displaced step is not completed
successfully, the minimal fix up is sufficient.

On amd64 we use at most one temporary register.

On ARM, looking at arm_displaced_step_copy_insn_closure, we could
modify up to 16 temporary registers, and the instruction being
displaced stepped could be expanded to multiple replacement
instructions, which increases the chances of this bug triggering.

This commit only aims to address the issue on amd64 for now, though I
believe that the approach I'm proposing here might be applicable for
ARM too.

What I propose is that we always call gdbarch_displaced_step_fixup.

We will now pass an extra argument to gdbarch_displaced_step_fixup,
this a boolean that indicates whether GDB thinks the displaced step
completed successfully or not.

When this flag is false this indicates that the displaced step halted
for some "other" reason.  On ARM GDB can potentially read the
inferior's program counter in order figure out how far through the
sequence of replacement instructions we got, and from that GDB can
figure out what fixup needs to be performed.

On targets like amd64 the problem is slightly easier as displaced
stepping only uses a single replacement instruction.  If the displaced
step didn't complete the GDB knows that the single instruction didn't
execute.

The point is that by always calling gdbarch_displaced_step_fixup, each
architecture can now ensure that the inferior state is fixed up
correctly in all cases, not just the success case.

On amd64 this ensures that we always restore the temporary register
value, and so bug PR gdb/22921 is resolved.

In order to move all architectures to this new API, I have moved the
minimal roll-back version of the code inside the architecture specific
fixup functions for AArch64, rs600, s390, and ARM.  For all of these
except ARM I think this is good enough, as no temporaries are used all
that's needed is the program counter restore anyway.

For ARM the minimal code is no worse than what we had before, though I
do consider this architecture's displaced-stepping broken.

I've updated the gdb.arch/amd64-disp-step.exp test to cover the
'jmpq*' instruction that was causing problems in the original bug, and
also added support for testing the displaced step in the presence of
asynchronous signal delivery.

I've also added two new tests (for amd64 and i386) that check that GDB
can correctly handle displaced stepping over a single instruction that
branches to itself.  I added these tests after a first version of this
patch relied too much on checking the program-counter value in order
to see if the displaced instruction had executed.  This works fine in
almost all cases, but when an instruction branches to itself a pure
program counter check is not sufficient.  The new tests expose this
problem.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=22921
---
 gdb/aarch64-tdep.c                            |  17 ++-
 gdb/aarch64-tdep.h                            |   2 +-
 gdb/amd64-tdep.c                              |  27 +++--
 gdb/amd64-tdep.h                              |   2 +-
 gdb/arm-tdep.c                                |  26 ++++-
 gdb/arm-tdep.h                                |   3 +-
 gdb/displaced-stepping.c                      |  23 ++--
 gdb/gdbarch-gen.h                             |  25 +++--
 gdb/gdbarch.c                                 |   4 +-
 gdb/gdbarch_components.py                     |  22 +++-
 gdb/i386-tdep.c                               |  24 ++--
 gdb/i386-tdep.h                               |   2 +-
 gdb/rs6000-tdep.c                             |  12 +-
 gdb/s390-tdep.c                               |  14 ++-
 .../amd64-disp-step-self-call-alarm.c         |  24 ++++
 .../gdb.arch/amd64-disp-step-self-call.S      |  50 +++++++++
 .../gdb.arch/amd64-disp-step-self-call.exp    |  81 +++++++++++++
 .../gdb.arch/amd64-disp-step-signal.c         |  30 +++++
 gdb/testsuite/gdb.arch/amd64-disp-step.S      |  15 +++
 gdb/testsuite/gdb.arch/amd64-disp-step.exp    | 106 +++++++++++++++---
 .../gdb.arch/i386-disp-step-self-call-alarm.c |  24 ++++
 .../gdb.arch/i386-disp-step-self-call.S       |  50 +++++++++
 .../gdb.arch/i386-disp-step-self-call.exp     |  81 +++++++++++++
 23 files changed, 574 insertions(+), 90 deletions(-)
 create mode 100644 gdb/testsuite/gdb.arch/amd64-disp-step-self-call-alarm.c
 create mode 100644 gdb/testsuite/gdb.arch/amd64-disp-step-self-call.S
 create mode 100644 gdb/testsuite/gdb.arch/amd64-disp-step-self-call.exp
 create mode 100644 gdb/testsuite/gdb.arch/amd64-disp-step-signal.c
 create mode 100644 gdb/testsuite/gdb.arch/i386-disp-step-self-call-alarm.c
 create mode 100644 gdb/testsuite/gdb.arch/i386-disp-step-self-call.S
 create mode 100644 gdb/testsuite/gdb.arch/i386-disp-step-self-call.exp

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"
-- 
2.25.4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCHv2 0/2] displaced stepping debug improvements
  2023-03-22 21:17         ` [PATCHv2 0/2] displaced stepping debug improvements Andrew Burgess
  2023-03-22 21:17           ` [PATCHv2 1/2] gdb: more debug output for displaced stepping Andrew Burgess
  2023-03-22 21:17           ` [PATCHv2 2/2] gdb: move displaced_step_dump_bytes into gdbsupport (and rename) Andrew Burgess
@ 2023-03-27 12:35           ` Andrew Burgess
  2 siblings, 0 replies; 36+ messages in thread
From: Andrew Burgess @ 2023-03-27 12:35 UTC (permalink / raw)
  To: gdb-patches; +Cc: Pedro Alves, Simon Marchi

Andrew Burgess <aburgess@redhat.com> writes:

> This mini series is just an expansion of the first patch from the
> parent series: improvements to the displaced stepping debug output.

Don't review this.  Look for the V3 series instead!

  https://inbox.sourceware.org/gdb-patches/cover.1679919937.git.aburgess@redhat.com/T/#u

Thanks,
Andrew



>
> In V2:
>
>   - As Simon suggested, the debug has now moved to
>     displaced_step_prepare_throw, so the (not yet upstreamed)
>     displaced stepping for the AMD GPU target should still see the
>     debug output,
>
>   - The debug code now handles a failure to disassemble better:
>     there's nothing worse than enabling debug to try and solve a
>     problem, and having GDB crash in a different way.  If an
>     instruction fails to disassemble GDB will now print a basic debug
>     message and skip the rest of the debug output,
>
>   - As suggested, I've moved the displaced_step_dump_bytes helper
>     function into gdbsupport/ and given it a better name,
>
>   - I have NOT tried to implement the improvement Simon suggested
>     where the architecture backend tells GDB core how many bytes the
>     replacement instruction(s) occupied.  This still means that in
>     some cases we will disassemble the entire displaced step buffer
>     unnecessarily, but I don't see that as a huge problem.  Fixing
>     this just to reduce some debug output a little seems excessive.
>     Let me know if you feel this is a blocker for this work being
>     merged and I can take another look at it.
>
> Thanks,
> Andrew
>
> ---
>
> Andrew Burgess (2):
>   gdb: more debug output for displaced stepping
>   gdb: move displaced_step_dump_bytes into gdbsupport (and rename)
>
>  gdb/amd64-tdep.c           |   2 +-
>  gdb/displaced-stepping.c   |   3 +-
>  gdb/i386-tdep.c            |   2 +-
>  gdb/infrun.c               | 101 ++++++++++++++++++++++++-------------
>  gdb/infrun.h               |   3 --
>  gdb/rs6000-tdep.c          |   2 +-
>  gdb/s390-tdep.c            |   2 +-
>  gdbsupport/array-view.h    |   1 +
>  gdbsupport/common-utils.cc |  18 +++++++
>  gdbsupport/common-utils.h  |  15 ++++++
>  10 files changed, 105 insertions(+), 44 deletions(-)
>
>
> base-commit: bf3f6c02d73f9823b8cb4f59524f29fbbfb6126d
> -- 
> 2.25.4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCHv3 0/3] AMD64 Displaced Stepping Fix
  2023-03-27 12:32   ` [PATCHv3 0/3] AMD64 Displaced Stepping Fix Andrew Burgess
                       ` (2 preceding siblings ...)
  2023-03-27 12:32     ` [PATCHv3 3/3] gdb: fix reg corruption from displaced stepping on amd64 Andrew Burgess
@ 2023-03-28 12:33     ` Simon Marchi
  2023-03-28 15:29       ` Andrew Burgess
  2023-03-29 13:46     ` [PATCHv4] gdb: fix reg corruption from displaced stepping on amd64 Andrew Burgess
  4 siblings, 1 reply; 36+ messages in thread
From: Simon Marchi @ 2023-03-28 12:33 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Pedro Alves

On 3/27/23 08:32, Andrew Burgess via Gdb-patches wrote:
> In V3:
> 
>   - Change of direction since v2.  As Pedro pointed out, using the $pc
>     value to decide if the displaced step succeeded is not good
>     enough.  This new version switches back to using the
>     target_waitstatus value.
> 
>   - The target_waitstatus is examined, and the result (a bool) is
>     passed to each architecture's fixup routine.
> 
>   - Just like in v2, each fixup routine is updated, with amd64 being
>     "fixed", while aarch64, ppc, and s390 (which aren't broken) just
>     have an early return case added to handle the unsuccessful
>     displaced step case.
> 
>   - As with v2, ARM is left broken, though this is no more broken than
>     it was before this patch series.
> 
>   - As Simon suggested, the debug has now moved to
>     displaced_step_prepare_throw, so the (not yet upstreamed)
>     displaced stepping for the AMD GPU target should still see the
>     debug output,
> 
>   - The debug code now handles a failure to disassemble better:
>     there's nothing worse than enabling debug to try and solve a
>     problem, and having GDB crash in a different way.  If an
>     instruction fails to disassemble GDB will now print a basic debug
>     message and skip the rest of the debug output,
> 
>   - As suggested, I've moved the displaced_step_dump_bytes helper
>     function into gdbsupport/ and given it a better name,
> 
>   - I have NOT tried to implement the improvement Simon suggested
>     where the architecture backend tells GDB core how many bytes the
>     replacement instruction(s) occupied.  This still means that in
>     some cases we will disassemble the entire displaced step buffer
>     unnecessarily, but I don't see that as a huge problem.  Fixing
>     this just to reduce some debug output a little seems excessive.
>     Let me know if you feel this is a blocker for this work being
>     merged and I can take another look at it.
> 
> Thanks,
> Andrew
> 
> ---
> 
> Andrew Burgess (3):
>   gdb: more debug output for displaced stepping
>   gdb: move displaced_step_dump_bytes into gdbsupport (and rename)
>   gdb: fix reg corruption from displaced stepping on amd64

When applying the last one:


Applying: gdb: fix reg corruption from displaced stepping on amd64
.git/rebase-apply/patch:591: new blank line at EOF.
+
.git/rebase-apply/patch:995: new blank line at EOF.
+

Simon

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCHv3 1/3] gdb: more debug output for displaced stepping
  2023-03-27 12:32     ` [PATCHv3 1/3] gdb: more debug output for displaced stepping Andrew Burgess
@ 2023-03-28 13:05       ` Simon Marchi
  2023-03-28 15:08         ` Andrew Burgess
  0 siblings, 1 reply; 36+ messages in thread
From: Simon Marchi @ 2023-03-28 13:05 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Pedro Alves

On 3/27/23 08:32, Andrew Burgess wrote:
> While investigating a displaced stepping issue I wanted an easy way to
> see what GDB thought the original instruction was, and what
> instruction GDB replaced that with when performing the displaced step.
> 
> We do print out the address that is being stepped, so I can track down
> the original instruction, I just need to go find the information
> myself.
> 
> And we do print out the bytes of the new instruction, so I can figure
> out what the replacement instruction was, but it's not really easy.
> 
> Also, the code that prints the bytes of the replacement instruction
> only prints 4 bytes, which clearly isn't always going to be correct.
> 
> In this commit I remove the existing code that prints the bytes of the
> replacement instruction, and add two new blocks of code to
> displaced_step_prepare_throw.  This new code prints the original
> instruction, and the replacement instruction.  In each case we print
> both the bytes that make up the instruction and the completely
> disassembled instruction.
> 
> Here's an example of what the output looks like on x86-64 (this is
> with 'set debug displaced on').  The two interesting lines contain the
> strings 'original insn' and 'replacement insn':
> 
>   (gdb) step
>   [displaced] displaced_step_prepare_throw: displaced-stepping 2892655.2892655.0 now
>   [displaced] displaced_step_prepare_throw: original insn 0x401030: ff 25 e2 2f 00 00	jmp    *0x2fe2(%rip)        # 0x404018 <puts@got.plt>
>   [displaced] prepare: selected buffer at 0x401052
>   [displaced] prepare: saved 0x401052: 1e fa 31 ed 49 89 d1 5e 48 89 e2 48 83 e4 f0 50
>   [displaced] fixup_riprel: %rip-relative addressing used.
>   [displaced] fixup_riprel: using temp reg 2, old value 0x7ffff7f8a578, new value 0x401036
>   [displaced] amd64_displaced_step_copy_insn: copy 0x401030->0x401052: ff a1 e2 2f 00 00 68 00 00 00 00 e9 e0 ff ff ff
>   [displaced] displaced_step_prepare_throw: prepared successfully thread=2892655.2892655.0, original_pc=0x401030, displaced_pc=0x401052
>   [displaced] displaced_step_prepare_throw: replacement insn 0x401052: ff a1 e2 2f 00 00	jmp    *0x2fe2(%rcx)
>   [displaced] finish: restored 2892655.2892655.0 0x401052
>   [displaced] amd64_displaced_step_fixup: fixup (0x401030, 0x401052), insn = 0xff 0xa1 ...
>   [displaced] amd64_displaced_step_fixup: restoring reg 2 to 0x7ffff7f8a578
>   0x00007ffff7e402c0 in puts () from /lib64/libc.so.6
>   (gdb)
> 
> One final note.  For many targets that support displaced stepping (in
> fact all targets except ARM) the replacement instruction is always a
> single instruction.  But on ARM the replacement could actually be a
> series of instructions.
> 
> The debug code tries to handle this by disassembling the entire
> displaced stepping buffer.  Obviously this might actually print more
> than is necessary, but there's (currently) no easy way to know how
> many instructions to disassemble; that knowledge is all locked in the
> architecture specific code.  Still I don't think it really hurts, if
> someone is looking at this debug then hopefully they known what to
> expect.
> 
> Obviously we can imagine schemes where the architecture specific
> displaced stepping code could communicate back how many bytes its
> replacement sequence was, and then our debug print code could use this
> to limit the disassembly.  But this seems like a lot of effort just to
> save printing a few additional instructions in some debug output.
> 
> I'm not proposing to do anything about this issue for now.
> ---
>  gdb/infrun.c | 85 +++++++++++++++++++++++++++++++++++++++++-----------
>  1 file changed, 68 insertions(+), 17 deletions(-)
> 
> diff --git a/gdb/infrun.c b/gdb/infrun.c
> index 5c9babb9104..8c56a9a4dfb 100644
> --- a/gdb/infrun.c
> +++ b/gdb/infrun.c
> @@ -74,6 +74,7 @@
>  #include "gdbsupport/common-debug.h"
>  #include "gdbsupport/buildargv.h"
>  #include "extension.h"
> +#include "disasm.h"
>  
>  /* Prototypes for local functions */
>  
> @@ -1807,6 +1808,31 @@ displaced_step_prepare_throw (thread_info *tp)
>    CORE_ADDR original_pc = regcache_read_pc (regcache);
>    CORE_ADDR displaced_pc;
>  
> +  /* Display the instruction we are going to displaced step.  */
> +  if (debug_displaced)
> +    {
> +      string_file tmp_stream;
> +      int dislen = gdb_print_insn (gdbarch, original_pc, &tmp_stream,
> +				   nullptr);
> +
> +      if (dislen > 0)
> +	{
> +	  gdb::byte_vector insn_buf (dislen);
> +	  read_memory (original_pc, insn_buf.data (), insn_buf.size ());
> +
> +	  std::string insn_bytes
> +	    = displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ());
> +
> +	  displaced_debug_printf ("original insn %s: %s \t %s",
> +				  paddress (gdbarch, original_pc),
> +				  insn_bytes.c_str (),
> +				  tmp_stream.string ().c_str ());

If the bytes disassemble to more than one instruction, does tmp_stream
contain new lines characters?  Just wondering what the output would look
like (not a big deal in any case).

Approved-By: Simon Marchi <simon.marchi@efficios.com>

Simon

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCHv3 2/3] gdb: move displaced_step_dump_bytes into gdbsupport (and rename)
  2023-03-27 12:32     ` [PATCHv3 2/3] gdb: move displaced_step_dump_bytes into gdbsupport (and rename) Andrew Burgess
@ 2023-03-28 13:10       ` Simon Marchi
  2023-03-29  9:43         ` Andrew Burgess
  0 siblings, 1 reply; 36+ messages in thread
From: Simon Marchi @ 2023-03-28 13:10 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Pedro Alves

> diff --git a/gdbsupport/array-view.h b/gdbsupport/array-view.h
> index 3d8248b08b7..d07c8bc53fc 100644
> --- a/gdbsupport/array-view.h
> +++ b/gdbsupport/array-view.h
> @@ -21,6 +21,7 @@
>  #include "traits.h"
>  #include <algorithm>
>  #include <type_traits>
> +#include "gdbsupport/gdb_assert.h"
I suppose this is needed because array-view.h uses gdb_assert, and you
noticed it because some files didn't compile, because they didn't
happen to include gdb_assert.h before array-view.h.

> @@ -194,6 +195,20 @@ extern int hex2bin (const char *hex, gdb_byte *bin, int count);
>  /* Like the above, but return a gdb::byte_vector.  */
>  gdb::byte_vector hex2bin (const char *hex);
>  
> +/* Build a string containing the contents of BYTES.  Each byte is
> +   represented as a 2 character hex string, with spaces separating each
> +   individual byte.  */
> +
> +extern std::string bytes_to_string (gdb::array_view<gdb_byte> bytes);

I think it could be const:

gdb::array_view<const gdb_byte>

And the other overload of bytes_to_string too.

Otherwise:

Approved-By: Simon Marchi <simon.marchi@efficios.com>

Simon


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCHv3 1/3] gdb: more debug output for displaced stepping
  2023-03-28 13:05       ` Simon Marchi
@ 2023-03-28 15:08         ` Andrew Burgess
  2023-03-28 15:11           ` Simon Marchi
  0 siblings, 1 reply; 36+ messages in thread
From: Andrew Burgess @ 2023-03-28 15:08 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches; +Cc: Pedro Alves

Simon Marchi <simark@simark.ca> writes:

> On 3/27/23 08:32, Andrew Burgess wrote:
>> While investigating a displaced stepping issue I wanted an easy way to
>> see what GDB thought the original instruction was, and what
>> instruction GDB replaced that with when performing the displaced step.
>> 
>> We do print out the address that is being stepped, so I can track down
>> the original instruction, I just need to go find the information
>> myself.
>> 
>> And we do print out the bytes of the new instruction, so I can figure
>> out what the replacement instruction was, but it's not really easy.
>> 
>> Also, the code that prints the bytes of the replacement instruction
>> only prints 4 bytes, which clearly isn't always going to be correct.
>> 
>> In this commit I remove the existing code that prints the bytes of the
>> replacement instruction, and add two new blocks of code to
>> displaced_step_prepare_throw.  This new code prints the original
>> instruction, and the replacement instruction.  In each case we print
>> both the bytes that make up the instruction and the completely
>> disassembled instruction.
>> 
>> Here's an example of what the output looks like on x86-64 (this is
>> with 'set debug displaced on').  The two interesting lines contain the
>> strings 'original insn' and 'replacement insn':
>> 
>>   (gdb) step
>>   [displaced] displaced_step_prepare_throw: displaced-stepping 2892655.2892655.0 now
>>   [displaced] displaced_step_prepare_throw: original insn 0x401030: ff 25 e2 2f 00 00	jmp    *0x2fe2(%rip)        # 0x404018 <puts@got.plt>
>>   [displaced] prepare: selected buffer at 0x401052
>>   [displaced] prepare: saved 0x401052: 1e fa 31 ed 49 89 d1 5e 48 89 e2 48 83 e4 f0 50
>>   [displaced] fixup_riprel: %rip-relative addressing used.
>>   [displaced] fixup_riprel: using temp reg 2, old value 0x7ffff7f8a578, new value 0x401036
>>   [displaced] amd64_displaced_step_copy_insn: copy 0x401030->0x401052: ff a1 e2 2f 00 00 68 00 00 00 00 e9 e0 ff ff ff
>>   [displaced] displaced_step_prepare_throw: prepared successfully thread=2892655.2892655.0, original_pc=0x401030, displaced_pc=0x401052
>>   [displaced] displaced_step_prepare_throw: replacement insn 0x401052: ff a1 e2 2f 00 00	jmp    *0x2fe2(%rcx)
>>   [displaced] finish: restored 2892655.2892655.0 0x401052
>>   [displaced] amd64_displaced_step_fixup: fixup (0x401030, 0x401052), insn = 0xff 0xa1 ...
>>   [displaced] amd64_displaced_step_fixup: restoring reg 2 to 0x7ffff7f8a578
>>   0x00007ffff7e402c0 in puts () from /lib64/libc.so.6
>>   (gdb)
>> 
>> One final note.  For many targets that support displaced stepping (in
>> fact all targets except ARM) the replacement instruction is always a
>> single instruction.  But on ARM the replacement could actually be a
>> series of instructions.
>> 
>> The debug code tries to handle this by disassembling the entire
>> displaced stepping buffer.  Obviously this might actually print more
>> than is necessary, but there's (currently) no easy way to know how
>> many instructions to disassemble; that knowledge is all locked in the
>> architecture specific code.  Still I don't think it really hurts, if
>> someone is looking at this debug then hopefully they known what to
>> expect.
>> 
>> Obviously we can imagine schemes where the architecture specific
>> displaced stepping code could communicate back how many bytes its
>> replacement sequence was, and then our debug print code could use this
>> to limit the disassembly.  But this seems like a lot of effort just to
>> save printing a few additional instructions in some debug output.
>> 
>> I'm not proposing to do anything about this issue for now.
>> ---
>>  gdb/infrun.c | 85 +++++++++++++++++++++++++++++++++++++++++-----------
>>  1 file changed, 68 insertions(+), 17 deletions(-)
>> 
>> diff --git a/gdb/infrun.c b/gdb/infrun.c
>> index 5c9babb9104..8c56a9a4dfb 100644
>> --- a/gdb/infrun.c
>> +++ b/gdb/infrun.c
>> @@ -74,6 +74,7 @@
>>  #include "gdbsupport/common-debug.h"
>>  #include "gdbsupport/buildargv.h"
>>  #include "extension.h"
>> +#include "disasm.h"
>>  
>>  /* Prototypes for local functions */
>>  
>> @@ -1807,6 +1808,31 @@ displaced_step_prepare_throw (thread_info *tp)
>>    CORE_ADDR original_pc = regcache_read_pc (regcache);
>>    CORE_ADDR displaced_pc;
>>  
>> +  /* Display the instruction we are going to displaced step.  */
>> +  if (debug_displaced)
>> +    {
>> +      string_file tmp_stream;
>> +      int dislen = gdb_print_insn (gdbarch, original_pc, &tmp_stream,
>> +				   nullptr);
>> +
>> +      if (dislen > 0)
>> +	{
>> +	  gdb::byte_vector insn_buf (dislen);
>> +	  read_memory (original_pc, insn_buf.data (), insn_buf.size ());
>> +
>> +	  std::string insn_bytes
>> +	    = displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ());
>> +
>> +	  displaced_debug_printf ("original insn %s: %s \t %s",
>> +				  paddress (gdbarch, original_pc),
>> +				  insn_bytes.c_str (),
>> +				  tmp_stream.string ().c_str ());
>
> If the bytes disassemble to more than one instruction, does tmp_stream
> contain new lines characters?  Just wondering what the output would look
> like (not a big deal in any case).

No.  gdb_print_insn will only disassemble a single instruction and
return its length.  In this bit of debug, we assume the original
instruction is always a single instruction.  If that's not true then
I've seriously not understood how displaced stepping works.

For the replacement instructions the call to gdb_print_insn is placed
inside a loop which calls gdb_print_insn multiple times, so you'll see
multiple lines like:

  [displaced] displaced_step_prepare_throw: replacement insn <ADDRESS>: <BYTES> <DISASSEMBLY>

I use this trick:

  CORE_ADDR end
    = addr + (gdbarch_displaced_step_hw_singlestep (gdbarch)
              ? 1 : gdbarch_displaced_step_buffer_length (gdbarch));

Which means for targets that do a 1:1 replacement we only disassemble a
single instruction.  But for everyone else we'll always disassemble the
entire displaced step buffer.  Currently this is just ARM.

>
> Approved-By: Simon Marchi <simon.marchi@efficios.com>

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCHv3 1/3] gdb: more debug output for displaced stepping
  2023-03-28 15:08         ` Andrew Burgess
@ 2023-03-28 15:11           ` Simon Marchi
  2023-03-29  9:42             ` Andrew Burgess
  0 siblings, 1 reply; 36+ messages in thread
From: Simon Marchi @ 2023-03-28 15:11 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Pedro Alves

On 3/28/23 11:08, Andrew Burgess wrote:
> Simon Marchi <simark@simark.ca> writes:
> 
>> On 3/27/23 08:32, Andrew Burgess wrote:
>>> While investigating a displaced stepping issue I wanted an easy way to
>>> see what GDB thought the original instruction was, and what
>>> instruction GDB replaced that with when performing the displaced step.
>>>
>>> We do print out the address that is being stepped, so I can track down
>>> the original instruction, I just need to go find the information
>>> myself.
>>>
>>> And we do print out the bytes of the new instruction, so I can figure
>>> out what the replacement instruction was, but it's not really easy.
>>>
>>> Also, the code that prints the bytes of the replacement instruction
>>> only prints 4 bytes, which clearly isn't always going to be correct.
>>>
>>> In this commit I remove the existing code that prints the bytes of the
>>> replacement instruction, and add two new blocks of code to
>>> displaced_step_prepare_throw.  This new code prints the original
>>> instruction, and the replacement instruction.  In each case we print
>>> both the bytes that make up the instruction and the completely
>>> disassembled instruction.
>>>
>>> Here's an example of what the output looks like on x86-64 (this is
>>> with 'set debug displaced on').  The two interesting lines contain the
>>> strings 'original insn' and 'replacement insn':
>>>
>>>   (gdb) step
>>>   [displaced] displaced_step_prepare_throw: displaced-stepping 2892655.2892655.0 now
>>>   [displaced] displaced_step_prepare_throw: original insn 0x401030: ff 25 e2 2f 00 00	jmp    *0x2fe2(%rip)        # 0x404018 <puts@got.plt>
>>>   [displaced] prepare: selected buffer at 0x401052
>>>   [displaced] prepare: saved 0x401052: 1e fa 31 ed 49 89 d1 5e 48 89 e2 48 83 e4 f0 50
>>>   [displaced] fixup_riprel: %rip-relative addressing used.
>>>   [displaced] fixup_riprel: using temp reg 2, old value 0x7ffff7f8a578, new value 0x401036
>>>   [displaced] amd64_displaced_step_copy_insn: copy 0x401030->0x401052: ff a1 e2 2f 00 00 68 00 00 00 00 e9 e0 ff ff ff
>>>   [displaced] displaced_step_prepare_throw: prepared successfully thread=2892655.2892655.0, original_pc=0x401030, displaced_pc=0x401052
>>>   [displaced] displaced_step_prepare_throw: replacement insn 0x401052: ff a1 e2 2f 00 00	jmp    *0x2fe2(%rcx)
>>>   [displaced] finish: restored 2892655.2892655.0 0x401052
>>>   [displaced] amd64_displaced_step_fixup: fixup (0x401030, 0x401052), insn = 0xff 0xa1 ...
>>>   [displaced] amd64_displaced_step_fixup: restoring reg 2 to 0x7ffff7f8a578
>>>   0x00007ffff7e402c0 in puts () from /lib64/libc.so.6
>>>   (gdb)
>>>
>>> One final note.  For many targets that support displaced stepping (in
>>> fact all targets except ARM) the replacement instruction is always a
>>> single instruction.  But on ARM the replacement could actually be a
>>> series of instructions.
>>>
>>> The debug code tries to handle this by disassembling the entire
>>> displaced stepping buffer.  Obviously this might actually print more
>>> than is necessary, but there's (currently) no easy way to know how
>>> many instructions to disassemble; that knowledge is all locked in the
>>> architecture specific code.  Still I don't think it really hurts, if
>>> someone is looking at this debug then hopefully they known what to
>>> expect.
>>>
>>> Obviously we can imagine schemes where the architecture specific
>>> displaced stepping code could communicate back how many bytes its
>>> replacement sequence was, and then our debug print code could use this
>>> to limit the disassembly.  But this seems like a lot of effort just to
>>> save printing a few additional instructions in some debug output.
>>>
>>> I'm not proposing to do anything about this issue for now.
>>> ---
>>>  gdb/infrun.c | 85 +++++++++++++++++++++++++++++++++++++++++-----------
>>>  1 file changed, 68 insertions(+), 17 deletions(-)
>>>
>>> diff --git a/gdb/infrun.c b/gdb/infrun.c
>>> index 5c9babb9104..8c56a9a4dfb 100644
>>> --- a/gdb/infrun.c
>>> +++ b/gdb/infrun.c
>>> @@ -74,6 +74,7 @@
>>>  #include "gdbsupport/common-debug.h"
>>>  #include "gdbsupport/buildargv.h"
>>>  #include "extension.h"
>>> +#include "disasm.h"
>>>  
>>>  /* Prototypes for local functions */
>>>  
>>> @@ -1807,6 +1808,31 @@ displaced_step_prepare_throw (thread_info *tp)
>>>    CORE_ADDR original_pc = regcache_read_pc (regcache);
>>>    CORE_ADDR displaced_pc;
>>>  
>>> +  /* Display the instruction we are going to displaced step.  */
>>> +  if (debug_displaced)
>>> +    {
>>> +      string_file tmp_stream;
>>> +      int dislen = gdb_print_insn (gdbarch, original_pc, &tmp_stream,
>>> +				   nullptr);
>>> +
>>> +      if (dislen > 0)
>>> +	{
>>> +	  gdb::byte_vector insn_buf (dislen);
>>> +	  read_memory (original_pc, insn_buf.data (), insn_buf.size ());
>>> +
>>> +	  std::string insn_bytes
>>> +	    = displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ());
>>> +
>>> +	  displaced_debug_printf ("original insn %s: %s \t %s",
>>> +				  paddress (gdbarch, original_pc),
>>> +				  insn_bytes.c_str (),
>>> +				  tmp_stream.string ().c_str ());
>>
>> If the bytes disassemble to more than one instruction, does tmp_stream
>> contain new lines characters?  Just wondering what the output would look
>> like (not a big deal in any case).
> 
> No.  gdb_print_insn will only disassemble a single instruction and
> return its length.  In this bit of debug, we assume the original
> instruction is always a single instruction.  If that's not true then
> I've seriously not understood how displaced stepping works.

Eh yeah, that's the input instruction, you're right.

> 
> For the replacement instructions the call to gdb_print_insn is placed
> inside a loop which calls gdb_print_insn multiple times, so you'll see
> multiple lines like:
> 
>   [displaced] displaced_step_prepare_throw: replacement insn <ADDRESS>: <BYTES> <DISASSEMBLY>
> 
> I use this trick:
> 
>   CORE_ADDR end
>     = addr + (gdbarch_displaced_step_hw_singlestep (gdbarch)
>               ? 1 : gdbarch_displaced_step_buffer_length (gdbarch));
> 
> Which means for targets that do a 1:1 replacement we only disassemble a
> single instruction.  But for everyone else we'll always disassemble the
> entire displaced step buffer.  Currently this is just ARM.

Ok I missed that, that makes sense.

Simon

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCHv3 0/3] AMD64 Displaced Stepping Fix
  2023-03-28 12:33     ` [PATCHv3 0/3] AMD64 Displaced Stepping Fix Simon Marchi
@ 2023-03-28 15:29       ` Andrew Burgess
  0 siblings, 0 replies; 36+ messages in thread
From: Andrew Burgess @ 2023-03-28 15:29 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches; +Cc: Pedro Alves

Simon Marchi <simark@simark.ca> writes:

> On 3/27/23 08:32, Andrew Burgess via Gdb-patches wrote:
>> In V3:
>> 
>>   - Change of direction since v2.  As Pedro pointed out, using the $pc
>>     value to decide if the displaced step succeeded is not good
>>     enough.  This new version switches back to using the
>>     target_waitstatus value.
>> 
>>   - The target_waitstatus is examined, and the result (a bool) is
>>     passed to each architecture's fixup routine.
>> 
>>   - Just like in v2, each fixup routine is updated, with amd64 being
>>     "fixed", while aarch64, ppc, and s390 (which aren't broken) just
>>     have an early return case added to handle the unsuccessful
>>     displaced step case.
>> 
>>   - As with v2, ARM is left broken, though this is no more broken than
>>     it was before this patch series.
>> 
>>   - As Simon suggested, the debug has now moved to
>>     displaced_step_prepare_throw, so the (not yet upstreamed)
>>     displaced stepping for the AMD GPU target should still see the
>>     debug output,
>> 
>>   - The debug code now handles a failure to disassemble better:
>>     there's nothing worse than enabling debug to try and solve a
>>     problem, and having GDB crash in a different way.  If an
>>     instruction fails to disassemble GDB will now print a basic debug
>>     message and skip the rest of the debug output,
>> 
>>   - As suggested, I've moved the displaced_step_dump_bytes helper
>>     function into gdbsupport/ and given it a better name,
>> 
>>   - I have NOT tried to implement the improvement Simon suggested
>>     where the architecture backend tells GDB core how many bytes the
>>     replacement instruction(s) occupied.  This still means that in
>>     some cases we will disassemble the entire displaced step buffer
>>     unnecessarily, but I don't see that as a huge problem.  Fixing
>>     this just to reduce some debug output a little seems excessive.
>>     Let me know if you feel this is a blocker for this work being
>>     merged and I can take another look at it.
>> 
>> Thanks,
>> Andrew
>> 
>> ---
>> 
>> Andrew Burgess (3):
>>   gdb: more debug output for displaced stepping
>>   gdb: move displaced_step_dump_bytes into gdbsupport (and rename)
>>   gdb: fix reg corruption from displaced stepping on amd64
>
> When applying the last one:
>
>
> Applying: gdb: fix reg corruption from displaced stepping on amd64
> .git/rebase-apply/patch:591: new blank line at EOF.
> +
> .git/rebase-apply/patch:995: new blank line at EOF.
> +

Thanks, I fixed these locally.

Andrew


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCHv3 1/3] gdb: more debug output for displaced stepping
  2023-03-28 15:11           ` Simon Marchi
@ 2023-03-29  9:42             ` Andrew Burgess
  0 siblings, 0 replies; 36+ messages in thread
From: Andrew Burgess @ 2023-03-29  9:42 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches; +Cc: Pedro Alves

Simon Marchi <simark@simark.ca> writes:

> On 3/28/23 11:08, Andrew Burgess wrote:
>> Simon Marchi <simark@simark.ca> writes:
>> 
>>> On 3/27/23 08:32, Andrew Burgess wrote:
>>>> While investigating a displaced stepping issue I wanted an easy way to
>>>> see what GDB thought the original instruction was, and what
>>>> instruction GDB replaced that with when performing the displaced step.
>>>>
>>>> We do print out the address that is being stepped, so I can track down
>>>> the original instruction, I just need to go find the information
>>>> myself.
>>>>
>>>> And we do print out the bytes of the new instruction, so I can figure
>>>> out what the replacement instruction was, but it's not really easy.
>>>>
>>>> Also, the code that prints the bytes of the replacement instruction
>>>> only prints 4 bytes, which clearly isn't always going to be correct.
>>>>
>>>> In this commit I remove the existing code that prints the bytes of the
>>>> replacement instruction, and add two new blocks of code to
>>>> displaced_step_prepare_throw.  This new code prints the original
>>>> instruction, and the replacement instruction.  In each case we print
>>>> both the bytes that make up the instruction and the completely
>>>> disassembled instruction.
>>>>
>>>> Here's an example of what the output looks like on x86-64 (this is
>>>> with 'set debug displaced on').  The two interesting lines contain the
>>>> strings 'original insn' and 'replacement insn':
>>>>
>>>>   (gdb) step
>>>>   [displaced] displaced_step_prepare_throw: displaced-stepping 2892655.2892655.0 now
>>>>   [displaced] displaced_step_prepare_throw: original insn 0x401030: ff 25 e2 2f 00 00	jmp    *0x2fe2(%rip)        # 0x404018 <puts@got.plt>
>>>>   [displaced] prepare: selected buffer at 0x401052
>>>>   [displaced] prepare: saved 0x401052: 1e fa 31 ed 49 89 d1 5e 48 89 e2 48 83 e4 f0 50
>>>>   [displaced] fixup_riprel: %rip-relative addressing used.
>>>>   [displaced] fixup_riprel: using temp reg 2, old value 0x7ffff7f8a578, new value 0x401036
>>>>   [displaced] amd64_displaced_step_copy_insn: copy 0x401030->0x401052: ff a1 e2 2f 00 00 68 00 00 00 00 e9 e0 ff ff ff
>>>>   [displaced] displaced_step_prepare_throw: prepared successfully thread=2892655.2892655.0, original_pc=0x401030, displaced_pc=0x401052
>>>>   [displaced] displaced_step_prepare_throw: replacement insn 0x401052: ff a1 e2 2f 00 00	jmp    *0x2fe2(%rcx)
>>>>   [displaced] finish: restored 2892655.2892655.0 0x401052
>>>>   [displaced] amd64_displaced_step_fixup: fixup (0x401030, 0x401052), insn = 0xff 0xa1 ...
>>>>   [displaced] amd64_displaced_step_fixup: restoring reg 2 to 0x7ffff7f8a578
>>>>   0x00007ffff7e402c0 in puts () from /lib64/libc.so.6
>>>>   (gdb)
>>>>
>>>> One final note.  For many targets that support displaced stepping (in
>>>> fact all targets except ARM) the replacement instruction is always a
>>>> single instruction.  But on ARM the replacement could actually be a
>>>> series of instructions.
>>>>
>>>> The debug code tries to handle this by disassembling the entire
>>>> displaced stepping buffer.  Obviously this might actually print more
>>>> than is necessary, but there's (currently) no easy way to know how
>>>> many instructions to disassemble; that knowledge is all locked in the
>>>> architecture specific code.  Still I don't think it really hurts, if
>>>> someone is looking at this debug then hopefully they known what to
>>>> expect.
>>>>
>>>> Obviously we can imagine schemes where the architecture specific
>>>> displaced stepping code could communicate back how many bytes its
>>>> replacement sequence was, and then our debug print code could use this
>>>> to limit the disassembly.  But this seems like a lot of effort just to
>>>> save printing a few additional instructions in some debug output.
>>>>
>>>> I'm not proposing to do anything about this issue for now.
>>>> ---
>>>>  gdb/infrun.c | 85 +++++++++++++++++++++++++++++++++++++++++-----------
>>>>  1 file changed, 68 insertions(+), 17 deletions(-)
>>>>
>>>> diff --git a/gdb/infrun.c b/gdb/infrun.c
>>>> index 5c9babb9104..8c56a9a4dfb 100644
>>>> --- a/gdb/infrun.c
>>>> +++ b/gdb/infrun.c
>>>> @@ -74,6 +74,7 @@
>>>>  #include "gdbsupport/common-debug.h"
>>>>  #include "gdbsupport/buildargv.h"
>>>>  #include "extension.h"
>>>> +#include "disasm.h"
>>>>  
>>>>  /* Prototypes for local functions */
>>>>  
>>>> @@ -1807,6 +1808,31 @@ displaced_step_prepare_throw (thread_info *tp)
>>>>    CORE_ADDR original_pc = regcache_read_pc (regcache);
>>>>    CORE_ADDR displaced_pc;
>>>>  
>>>> +  /* Display the instruction we are going to displaced step.  */
>>>> +  if (debug_displaced)
>>>> +    {
>>>> +      string_file tmp_stream;
>>>> +      int dislen = gdb_print_insn (gdbarch, original_pc, &tmp_stream,
>>>> +				   nullptr);
>>>> +
>>>> +      if (dislen > 0)
>>>> +	{
>>>> +	  gdb::byte_vector insn_buf (dislen);
>>>> +	  read_memory (original_pc, insn_buf.data (), insn_buf.size ());
>>>> +
>>>> +	  std::string insn_bytes
>>>> +	    = displaced_step_dump_bytes (insn_buf.data (), insn_buf.size ());
>>>> +
>>>> +	  displaced_debug_printf ("original insn %s: %s \t %s",
>>>> +				  paddress (gdbarch, original_pc),
>>>> +				  insn_bytes.c_str (),
>>>> +				  tmp_stream.string ().c_str ());
>>>
>>> If the bytes disassemble to more than one instruction, does tmp_stream
>>> contain new lines characters?  Just wondering what the output would look
>>> like (not a big deal in any case).
>> 
>> No.  gdb_print_insn will only disassemble a single instruction and
>> return its length.  In this bit of debug, we assume the original
>> instruction is always a single instruction.  If that's not true then
>> I've seriously not understood how displaced stepping works.
>
> Eh yeah, that's the input instruction, you're right.
>
>> 
>> For the replacement instructions the call to gdb_print_insn is placed
>> inside a loop which calls gdb_print_insn multiple times, so you'll see
>> multiple lines like:
>> 
>>   [displaced] displaced_step_prepare_throw: replacement insn <ADDRESS>: <BYTES> <DISASSEMBLY>
>> 
>> I use this trick:
>> 
>>   CORE_ADDR end
>>     = addr + (gdbarch_displaced_step_hw_singlestep (gdbarch)
>>               ? 1 : gdbarch_displaced_step_buffer_length (gdbarch));
>> 
>> Which means for targets that do a 1:1 replacement we only disassemble a
>> single instruction.  But for everyone else we'll always disassemble the
>> entire displaced step buffer.  Currently this is just ARM.
>
> Ok I missed that, that makes sense.

I pushed this patch.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCHv3 3/3] gdb: fix reg corruption from displaced stepping on amd64
  2023-03-27 12:32     ` [PATCHv3 3/3] gdb: fix reg corruption from displaced stepping on amd64 Andrew Burgess
@ 2023-03-29  9:43       ` Pedro Alves
  0 siblings, 0 replies; 36+ messages in thread
From: Pedro Alves @ 2023-03-29  9:43 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Simon Marchi

Hi!

On 2023-03-27 1:32 p.m., Andrew Burgess wrote:

> 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. */

This comment seems stale, as we can't use PC/TO to determine whether stepped
or not.  I can't find the comment in the .py file, so, just need to regen?

> --- 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.  */

Comment slightly stale now.  No longer getting current PC here.

> -  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

Missing period.

> +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);

Space before parens.

The downside of using SIGALRM is that the gdb.arch/amd64-disp-step*.exp testcases won't be usable
with targets that have no such concept like Windows or embedded systems or GPUs anymore.  I think
just wrapping the two functions in #ifdef SIGLRM, and then skipping the signals-related parts
of the testcases with [target_info exists gdb,nosignals] || [istarget "*-*-mingw*"]
would be sufficient.

> +}
> 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

destiation -> 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 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.

non -> none

[remote_exec target "kill ...] instead of Python would work with
remote though.  Like:

  remote_exec target "kill -ALRM $inferior_pid"

Several other tests do that already to send other signals to the
inferior.  Any reason not to here?

> +    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

Double "The the".

> +# 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

Missing period.

> +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"
> 


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCHv3 2/3] gdb: move displaced_step_dump_bytes into gdbsupport (and rename)
  2023-03-28 13:10       ` Simon Marchi
@ 2023-03-29  9:43         ` Andrew Burgess
  0 siblings, 0 replies; 36+ messages in thread
From: Andrew Burgess @ 2023-03-29  9:43 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches; +Cc: Pedro Alves

Simon Marchi <simark@simark.ca> writes:

>> diff --git a/gdbsupport/array-view.h b/gdbsupport/array-view.h
>> index 3d8248b08b7..d07c8bc53fc 100644
>> --- a/gdbsupport/array-view.h
>> +++ b/gdbsupport/array-view.h
>> @@ -21,6 +21,7 @@
>>  #include "traits.h"
>>  #include <algorithm>
>>  #include <type_traits>
>> +#include "gdbsupport/gdb_assert.h"
> I suppose this is needed because array-view.h uses gdb_assert, and you
> noticed it because some files didn't compile, because they didn't
> happen to include gdb_assert.h before array-view.h.
>
>> @@ -194,6 +195,20 @@ extern int hex2bin (const char *hex, gdb_byte *bin, int count);
>>  /* Like the above, but return a gdb::byte_vector.  */
>>  gdb::byte_vector hex2bin (const char *hex);
>>  
>> +/* Build a string containing the contents of BYTES.  Each byte is
>> +   represented as a 2 character hex string, with spaces separating each
>> +   individual byte.  */
>> +
>> +extern std::string bytes_to_string (gdb::array_view<gdb_byte> bytes);
>
> I think it could be const:
>
> gdb::array_view<const gdb_byte>
>
> And the other overload of bytes_to_string too.
>
> Otherwise:
>
> Approved-By: Simon Marchi <simon.marchi@efficios.com>

I fixed the 'const' issue and pushed this patch.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCHv4] gdb: fix reg corruption from displaced stepping on amd64
  2023-03-27 12:32   ` [PATCHv3 0/3] AMD64 Displaced Stepping Fix Andrew Burgess
                       ` (3 preceding siblings ...)
  2023-03-28 12:33     ` [PATCHv3 0/3] AMD64 Displaced Stepping Fix Simon Marchi
@ 2023-03-29 13:46     ` Andrew Burgess
  2023-04-04 13:03       ` Pedro Alves
  4 siblings, 1 reply; 36+ messages in thread
From: Andrew Burgess @ 2023-03-29 13:46 UTC (permalink / raw)
  To: gdb-patches; +Cc: Pedro Alves, Simon Marchi, Andrew Burgess

In v4:

  - The first two patches, which related to displaced step debugging,
    have now been merged,

  - The test now uses 'remote_exec target "kill ...' as suggested by
    Pedro, I've confirmed that the test still exposes the issue when
    the GDB fix is not applied,

  - Have regenerated the gdbarch related files.  The only change to
    generated code is within a comment -- so no functional change,

  - All the typos and gramatical errors Pedro pointed out have been
    fixed,

  - The trailing blank lines that Simon pointed out have been removed.

Thanks,
Andrew

---

This commit aims to address a problem that exists with the current
approach to displaced stepping, and was identified in PR gdb/22921.

Displaced stepping is currently supported on AArch64, ARM, amd64,
i386, rs6000 (ppc), and s390.  Of these, I believe there is a problem
with the current approach which will impact amd64 and ARM, and can
lead to random register corruption when the inferior makes use of
asynchronous signals and GDB is using displaced stepping.

The problem can be found in displaced_step_buffers::finish in
displaced-stepping.c, and is this; after GDB tries to perform a
displaced step, and the inferior stops, GDB classifies the stop into
one of two states, either the displaced step succeeded, or the
displaced step failed.

If the displaced step succeeded then gdbarch_displaced_step_fixup is
called, which has the job of fixing up the state of the current
inferior as if the step had not been performed in a displaced manner.
This all seems just fine.

However, if the displaced step is considered to have not completed
then GDB doesn't call gdbarch_displaced_step_fixup, instead GDB
remains in displaced_step_buffers::finish and just performs a minimal
fixup which involves adjusting the program counter back to its
original value.

The problem here is that for amd64 and ARM setting up for a displaced
step can involve changing the values in some temporary registers.  If
the displaced step succeeds then this is fine; after the step the
temporary registers are restored to their original values in the
architecture specific code.

But if the displaced step does not succeed then the temporary
registers are never restored, and they retain their modified values.

In this context a temporary register is simply any register that is
not otherwise used by the instruction being stepped that the
architecture specific code considers safe to borrow for the lifetime
of the instruction being stepped.

In the bug PR gdb/22921, the amd64 instruction being stepped is
an rip-relative instruction like this:

  jmp    *0x2fe2(%rip)

When we displaced step this instruction we borrow a register, and
modify the instruction to something like:

  jmp    *0x2fe2(%rcx)

with %rcx having its value adjusted to contain the original %rip
value.

Now if the displaced step does not succeed, then %rcx will be left
with a corrupted value.  Obviously corrupting any register is bad; in
the bug report this problem was spotted because %rcx is used as a
function argument register.

And finally, why might a displaced step not succeed?  Asynchronous
signals provides one reason.  GDB sets up for the displaced step and,
at that precise moment, the OS delivers a signal (SIGALRM in the bug
report), the signal stops the inferior at the address of the displaced
instruction.  GDB cancels the displaced instruction, handles the
signal, and then tries again with the displaced step.  But it is that
first cancellation of the displaced step that causes the problem; in
that case GDB (correctly) sees the displaced step as having not
completed, and so does not perform the architecture specific fixup,
leaving the register corrupted.

The reason why I think AArch64, rs600, i386, and s390 are not effected
by this problem is that I don't believe these architectures make use
of any temporary registers, so when a displaced step is not completed
successfully, the minimal fix up is sufficient.

On amd64 we use at most one temporary register.

On ARM, looking at arm_displaced_step_copy_insn_closure, we could
modify up to 16 temporary registers, and the instruction being
displaced stepped could be expanded to multiple replacement
instructions, which increases the chances of this bug triggering.

This commit only aims to address the issue on amd64 for now, though I
believe that the approach I'm proposing here might be applicable for
ARM too.

What I propose is that we always call gdbarch_displaced_step_fixup.

We will now pass an extra argument to gdbarch_displaced_step_fixup,
this a boolean that indicates whether GDB thinks the displaced step
completed successfully or not.

When this flag is false this indicates that the displaced step halted
for some "other" reason.  On ARM GDB can potentially read the
inferior's program counter in order figure out how far through the
sequence of replacement instructions we got, and from that GDB can
figure out what fixup needs to be performed.

On targets like amd64 the problem is slightly easier as displaced
stepping only uses a single replacement instruction.  If the displaced
step didn't complete the GDB knows that the single instruction didn't
execute.

The point is that by always calling gdbarch_displaced_step_fixup, each
architecture can now ensure that the inferior state is fixed up
correctly in all cases, not just the success case.

On amd64 this ensures that we always restore the temporary register
value, and so bug PR gdb/22921 is resolved.

In order to move all architectures to this new API, I have moved the
minimal roll-back version of the code inside the architecture specific
fixup functions for AArch64, rs600, s390, and ARM.  For all of these
except ARM I think this is good enough, as no temporaries are used all
that's needed is the program counter restore anyway.

For ARM the minimal code is no worse than what we had before, though I
do consider this architecture's displaced-stepping broken.

I've updated the gdb.arch/amd64-disp-step.exp test to cover the
'jmpq*' instruction that was causing problems in the original bug, and
also added support for testing the displaced step in the presence of
asynchronous signal delivery.

I've also added two new tests (for amd64 and i386) that check that GDB
can correctly handle displaced stepping over a single instruction that
branches to itself.  I added these tests after a first version of this
patch relied too much on checking the program-counter value in order
to see if the displaced instruction had executed.  This works fine in
almost all cases, but when an instruction branches to itself a pure
program counter check is not sufficient.  The new tests expose this
problem.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=22921
---
 gdb/aarch64-tdep.c                            | 17 +++-
 gdb/aarch64-tdep.h                            |  2 +-
 gdb/amd64-tdep.c                              | 27 +++---
 gdb/amd64-tdep.h                              |  2 +-
 gdb/arm-tdep.c                                | 26 ++++-
 gdb/arm-tdep.h                                |  3 +-
 gdb/displaced-stepping.c                      | 23 ++---
 gdb/gdbarch-gen.h                             | 25 +++--
 gdb/gdbarch.c                                 |  4 +-
 gdb/gdbarch_components.py                     | 22 +++--
 gdb/i386-tdep.c                               | 24 ++---
 gdb/i386-tdep.h                               |  2 +-
 gdb/rs6000-tdep.c                             | 12 ++-
 gdb/s390-tdep.c                               | 17 +++-
 .../amd64-disp-step-self-call-alarm.c         | 24 +++++
 .../gdb.arch/amd64-disp-step-self-call.S      | 49 ++++++++++
 .../gdb.arch/amd64-disp-step-self-call.exp    | 81 ++++++++++++++++
 .../gdb.arch/amd64-disp-step-signal.c         | 36 +++++++
 gdb/testsuite/gdb.arch/amd64-disp-step.S      | 15 +++
 gdb/testsuite/gdb.arch/amd64-disp-step.exp    | 94 +++++++++++++++----
 .../gdb.arch/i386-disp-step-self-call-alarm.c | 24 +++++
 .../gdb.arch/i386-disp-step-self-call.S       | 49 ++++++++++
 .../gdb.arch/i386-disp-step-self-call.exp     | 81 ++++++++++++++++
 23 files changed, 567 insertions(+), 92 deletions(-)
 create mode 100644 gdb/testsuite/gdb.arch/amd64-disp-step-self-call-alarm.c
 create mode 100644 gdb/testsuite/gdb.arch/amd64-disp-step-self-call.S
 create mode 100644 gdb/testsuite/gdb.arch/amd64-disp-step-self-call.exp
 create mode 100644 gdb/testsuite/gdb.arch/amd64-disp-step-signal.c
 create mode 100644 gdb/testsuite/gdb.arch/i386-disp-step-self-call-alarm.c
 create mode 100644 gdb/testsuite/gdb.arch/i386-disp-step-self-call.S
 create mode 100644 gdb/testsuite/gdb.arch/i386-disp-step-self-call.exp

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 c26888404b3..bc59ef01478 100644
--- a/gdb/displaced-stepping.c
+++ b/gdb/displaced-stepping.c
@@ -263,22 +263,13 @@ displaced_step_buffers::finish (gdbarch *arch, thread_info *thread,
   bool instruction_executed_successfully
     = displaced_step_instruction_executed_successfully (arch, status);
 
-  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 bbf2376fc4b..a2bd08b8315 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,24 @@ 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.
+
+   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. */
 
-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 84beb087336..995f49e525f 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 52beaeaa245..d30c537ee1c 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 b071f38c960..afeea02a84d 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..047ee088aed 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_;
@@ -495,10 +506,8 @@ s390_displaced_step_fixup (struct gdbarch *gdbarch,
   unsigned int b2, r1, r2, x2, r3;
   int i2, d2;
 
-  /* Get current PC and addressing mode bit.  */
-  CORE_ADDR pc = regcache_read_pc (regs);
+  /* Get addressing mode bit.  */
   ULONGEST amode = 0;
-
   if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
     {
       regcache_cooked_read_unsigned (regs, S390_PSWA_REGNUM, &amode);
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..7372dc10132
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-disp-step-self-call.S
@@ -0,0 +1,49 @@
+/* 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..db44a319a47
--- /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..311e11e7afd
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-disp-step-signal.c
@@ -0,0 +1,36 @@
+/* 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>
+
+#ifdef SIGALRM
+
+static void
+sigalrm_handler (int sig)
+{
+}
+
+#endif
+
+void
+setup_signal_handler (void)
+{
+#ifdef SIGALRM
+  signal (SIGALRM, sigalrm_handler);
+#endif
+}
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..0919e71d715 100644
--- a/gdb/testsuite/gdb.arch/amd64-disp-step.exp
+++ b/gdb/testsuite/gdb.arch/amd64-disp-step.exp
@@ -17,15 +17,14 @@
 
 # Test amd64 displaced stepping.
 
-
 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 +153,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 +168,89 @@ 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 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
 
-    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
+
+	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]
+	    remote_exec target "kill -ALRM $inferior_pid"
+	}
 
-    set_regs ${rip_regs} 0
+	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 {![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} {
-    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..30553d508ba
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/i386-disp-step-self-call.S
@@ -0,0 +1,49 @@
+/* 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..7ea036fe3e6
--- /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"

base-commit: f8c88b623130037546842a51beb78c66c644e05d
-- 
2.25.4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCHv4] gdb: fix reg corruption from displaced stepping on amd64
  2023-03-29 13:46     ` [PATCHv4] gdb: fix reg corruption from displaced stepping on amd64 Andrew Burgess
@ 2023-04-04 13:03       ` Pedro Alves
  2023-04-06 13:29         ` Andrew Burgess
  0 siblings, 1 reply; 36+ messages in thread
From: Pedro Alves @ 2023-04-04 13:03 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Simon Marchi

On 2023-03-29 2:46 p.m., Andrew Burgess wrote:
> In v4:
> 
>   - The first two patches, which related to displaced step debugging,
>     have now been merged,
> 
>   - The test now uses 'remote_exec target "kill ...' as suggested by
>     Pedro, I've confirmed that the test still exposes the issue when
>     the GDB fix is not applied,
> 
>   - Have regenerated the gdbarch related files.  The only change to
>     generated code is within a comment -- so no functional change,
> 
>   - All the typos and gramatical errors Pedro pointed out have been
>     fixed,
> 
>   - The trailing blank lines that Simon pointed out have been removed.
> 

Thank you.

Approved-By: Pedro Alves <pedro@palves.net>

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCHv4] gdb: fix reg corruption from displaced stepping on amd64
  2023-04-04 13:03       ` Pedro Alves
@ 2023-04-06 13:29         ` Andrew Burgess
  2023-04-06 15:38           ` Andrew Burgess
  0 siblings, 1 reply; 36+ messages in thread
From: Andrew Burgess @ 2023-04-06 13:29 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches; +Cc: Simon Marchi

Pedro Alves <pedro@palves.net> writes:

> On 2023-03-29 2:46 p.m., Andrew Burgess wrote:
>> In v4:
>> 
>>   - The first two patches, which related to displaced step debugging,
>>     have now been merged,
>> 
>>   - The test now uses 'remote_exec target "kill ...' as suggested by
>>     Pedro, I've confirmed that the test still exposes the issue when
>>     the GDB fix is not applied,
>> 
>>   - Have regenerated the gdbarch related files.  The only change to
>>     generated code is within a comment -- so no functional change,
>> 
>>   - All the typos and gramatical errors Pedro pointed out have been
>>     fixed,
>> 
>>   - The trailing blank lines that Simon pointed out have been removed.
>> 
>
> Thank you.
>
> Approved-By: Pedro Alves <pedro@palves.net>

Pushed.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCHv4] gdb: fix reg corruption from displaced stepping on amd64
  2023-04-06 13:29         ` Andrew Burgess
@ 2023-04-06 15:38           ` Andrew Burgess
  0 siblings, 0 replies; 36+ messages in thread
From: Andrew Burgess @ 2023-04-06 15:38 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches; +Cc: Simon Marchi

Andrew Burgess <aburgess@redhat.com> writes:

> Pedro Alves <pedro@palves.net> writes:
>
>> On 2023-03-29 2:46 p.m., Andrew Burgess wrote:
>>> In v4:
>>> 
>>>   - The first two patches, which related to displaced step debugging,
>>>     have now been merged,
>>> 
>>>   - The test now uses 'remote_exec target "kill ...' as suggested by
>>>     Pedro, I've confirmed that the test still exposes the issue when
>>>     the GDB fix is not applied,
>>> 
>>>   - Have regenerated the gdbarch related files.  The only change to
>>>     generated code is within a comment -- so no functional change,
>>> 
>>>   - All the typos and gramatical errors Pedro pointed out have been
>>>     fixed,
>>> 
>>>   - The trailing blank lines that Simon pointed out have been removed.
>>> 
>>
>> Thank you.
>>
>> Approved-By: Pedro Alves <pedro@palves.net>
>
> Pushed.

And buildbot pointed out that I failed to run black on
gdbarch_components.py.

I pushed the following patch to fix the formatting.

Thanks,
Andrew

---

commit a52aeef9237096bbde1c3092a4860d12c3778ffb
Author: Andrew Burgess <aburgess@redhat.com>
Date:   Thu Apr 6 16:34:17 2023 +0100

    gdb: run black code formatter on gdbarch_components.py
    
    The following commit changed gdbarch_components.py but failed to
    format it with black:
    
      commit cf141dd8ccd36efe833aae3ccdb060b517cc1112
      Date:   Wed Feb 22 12:15:34 2023 +0000
    
          gdb: fix reg corruption from displaced stepping on amd64
    
    This commit just runs black on the file and commits the result.
    
    The change is just the addition of an extra "," -- there will be no
    change to the generated source files after this commit.
    
    There will be no user visible changes after this commit.

diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
index d30c537ee1c..9f0430c7770 100644
--- a/gdb/gdbarch_components.py
+++ b/gdb/gdbarch_components.py
@@ -1804,7 +1804,7 @@ see the comments in infrun.c.
         ("CORE_ADDR", "from"),
         ("CORE_ADDR", "to"),
         ("struct regcache *", "regs"),
-        ("bool", "completed_p")
+        ("bool", "completed_p"),
     ],
     predicate=False,
     predefault="NULL",


^ permalink raw reply	[flat|nested] 36+ messages in thread

end of thread, other threads:[~2023-04-06 15:38 UTC | newest]

Thread overview: 36+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-23 15:46 [PATCH 0/3] AMD64 Displaced Stepping Fix Andrew Burgess
2023-02-23 15:46 ` [PATCH 1/3] gdb: more debug output for displaced stepping Andrew Burgess
2023-02-23 15:46 ` [PATCH 2/3] gdb: remove gdbarch_displaced_step_fixup_p Andrew Burgess
2023-02-23 15:46 ` [PATCH 3/3] gdb: fix reg corruption from displaced stepping on amd64 Andrew Burgess
2023-03-16 16:39 ` [PATCHv2 0/4] AMD64 Displaced Stepping Fix Andrew Burgess
2023-03-16 16:39   ` [PATCHv2 1/4] gdb: more debug output for displaced stepping Andrew Burgess
2023-03-21 12:29     ` Pedro Alves
2023-03-21 14:41       ` Simon Marchi
2023-03-22 21:17         ` [PATCHv2 0/2] displaced stepping debug improvements Andrew Burgess
2023-03-22 21:17           ` [PATCHv2 1/2] gdb: more debug output for displaced stepping Andrew Burgess
2023-03-22 21:17           ` [PATCHv2 2/2] gdb: move displaced_step_dump_bytes into gdbsupport (and rename) Andrew Burgess
2023-03-27 12:35           ` [PATCHv2 0/2] displaced stepping debug improvements Andrew Burgess
2023-03-21 14:45     ` [PATCHv2 1/4] gdb: more debug output for displaced stepping Simon Marchi
2023-03-16 16:39   ` [PATCHv2 2/4] gdb: remove gdbarch_displaced_step_fixup_p Andrew Burgess
2023-03-21 13:10     ` Pedro Alves
2023-03-22 21:22       ` Andrew Burgess
2023-03-16 16:39   ` [PATCHv2 3/4] gdb: fix reg corruption from displaced stepping on amd64 Andrew Burgess
2023-03-21 13:23     ` Pedro Alves
2023-03-16 16:39   ` [PATCHv2 4/4] gdb: remove redundant signal passing Andrew Burgess
2023-03-27 12:32   ` [PATCHv3 0/3] AMD64 Displaced Stepping Fix Andrew Burgess
2023-03-27 12:32     ` [PATCHv3 1/3] gdb: more debug output for displaced stepping Andrew Burgess
2023-03-28 13:05       ` Simon Marchi
2023-03-28 15:08         ` Andrew Burgess
2023-03-28 15:11           ` Simon Marchi
2023-03-29  9:42             ` Andrew Burgess
2023-03-27 12:32     ` [PATCHv3 2/3] gdb: move displaced_step_dump_bytes into gdbsupport (and rename) Andrew Burgess
2023-03-28 13:10       ` Simon Marchi
2023-03-29  9:43         ` Andrew Burgess
2023-03-27 12:32     ` [PATCHv3 3/3] gdb: fix reg corruption from displaced stepping on amd64 Andrew Burgess
2023-03-29  9:43       ` Pedro Alves
2023-03-28 12:33     ` [PATCHv3 0/3] AMD64 Displaced Stepping Fix Simon Marchi
2023-03-28 15:29       ` Andrew Burgess
2023-03-29 13:46     ` [PATCHv4] gdb: fix reg corruption from displaced stepping on amd64 Andrew Burgess
2023-04-04 13:03       ` Pedro Alves
2023-04-06 13:29         ` Andrew Burgess
2023-04-06 15:38           ` Andrew Burgess

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).