[RFC,3/4,gdb/tdep] Enable prefer-software-single-stepping on amd64

Message ID 20231129203326.11952-3-tdevries@suse.de
State New
Headers
Series [RFC,1/4,gdb/tdep] Add dummy amd64_software_single_step |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm success Testing passed

Commit Message

Tom de Vries Nov. 29, 2023, 8:33 p.m. UTC
  Add amd64 support for prefer-software-single-stepping.

The support is not complete (only non control-flow insns are supported), but
it doesn't need to be since hardware single-stepping is used as fallback.

Tested on x86_64-linux.
---
 gdb/amd64-tdep.c | 68 ++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 63 insertions(+), 5 deletions(-)
  

Patch

diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index 85721bbadf4..841b072c714 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -1194,7 +1194,7 @@  static const unsigned char twobyte_has_modrm[256] = {
   /*	   0 1 2 3 4 5 6 7 8 9 a b c d e f	  */
 };
 
-static int amd64_syscall_p (const struct amd64_insn *insn, int *lengthp);
+static int amd64_syscall_with_length_p (const struct amd64_insn *insn, int *lengthp);
 
 static int
 rex_prefix_p (gdb_byte pfx)
@@ -1514,7 +1514,7 @@  amd64_displaced_step_copy_insn (struct gdbarch *gdbarch,
   {
     int syscall_length;
 
-    if (amd64_syscall_p (details, &syscall_length))
+    if (amd64_syscall_with_length_p (details, &syscall_length))
       buf[details->opcode_offset + syscall_length] = NOP_OPCODE;
   }
 
@@ -1623,11 +1623,28 @@  amd64_call_p (const struct amd64_insn *details)
   return 0;
 }
 
+static int
+amd64_cond_jmp_p (const struct amd64_insn *details)
+{
+  const gdb_byte *insn = &details->raw_insn[details->opcode_offset];
+
+  if (insn[0] >= 0x70 && insn[0] <= 0x7f)
+    return 1;
+
+  if (insn[0] == 0xe3)
+    return 1;
+
+  if (insn[0] == 0x0f && insn[1] >= 0x80 && insn[1] <= 0x8f)
+    return 1;
+
+  return 0;
+}
+
 /* Return non-zero if INSN is a system call, and set *LENGTHP to its
    length in bytes.  Otherwise, return zero.  */
 
 static int
-amd64_syscall_p (const struct amd64_insn *details, int *lengthp)
+amd64_syscall_with_length_p (const struct amd64_insn *details, int *lengthp)
 {
   const gdb_byte *insn = &details->raw_insn[details->opcode_offset];
 
@@ -1640,6 +1657,15 @@  amd64_syscall_p (const struct amd64_insn *details, int *lengthp)
   return 0;
 }
 
+/* As amd64_syscall_with_length_p, but without the lengthp argument.  */
+
+static int
+amd64_syscall_p (const struct amd64_insn *details)
+{
+  int dummy;
+  return amd64_syscall_with_length_p (details, &dummy);
+}
+
 /* Classify the instruction at ADDR using PRED.
    Throw an error if the memory can't be read.  */
 
@@ -1683,6 +1709,18 @@  amd64_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr)
   return amd64_classify_insn_at (gdbarch, addr, amd64_jmp_p);
 }
 
+static int
+amd64_insn_is_cond_jump (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  return amd64_classify_insn_at (gdbarch, addr, amd64_cond_jmp_p);
+}
+
+static int
+amd64_insn_is_syscall (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  return amd64_classify_insn_at (gdbarch, addr, amd64_syscall_p);
+}
+
 /* Fix up the state of registers and memory after having single-stepped
    a displaced instruction.  */
 
@@ -1748,7 +1786,7 @@  amd64_displaced_step_fixup (struct gdbarch *gdbarch,
 	 the instruction has put control where it belongs, and leave
 	 it unrelocated.  Goodness help us if there are PC-relative
 	 system calls.	*/
-      if (amd64_syscall_p (insn_details, &insn_len)
+      if (amd64_syscall_with_length_p (insn_details, &insn_len)
 	  /* GDB can get control back after the insn after the syscall.
 	     Presumably this is a kernel bug.  Fixup ensures it's a nop, we
 	     add one to the length for it.  */
@@ -3158,10 +3196,30 @@  amd64_in_indirect_branch_thunk (struct gdbarch *gdbarch, CORE_ADDR pc)
 				       AMD64_RIP_REGNUM);
 }
 
+extern bool prefer_software_single_stepping;
+
 static std::vector<CORE_ADDR>
 amd64_software_single_step (struct regcache *regcache)
 {
-  return {};
+  if (!prefer_software_single_stepping)
+    return {};
+
+  struct gdbarch *gdbarch = regcache->arch ();
+  CORE_ADDR pc = regcache_read_pc (regcache);
+
+  if (amd64_insn_is_call (gdbarch, pc))
+    return {};
+  else if (amd64_insn_is_ret (gdbarch, pc))
+    return {};
+  else if (amd64_insn_is_jump (gdbarch, pc))
+    return {};
+  else if (amd64_insn_is_cond_jump (gdbarch, pc))
+    return {};
+  else if (amd64_insn_is_syscall (gdbarch, pc))
+    return {};
+
+  size_t len = gdb_insn_length (gdbarch, pc);
+  return { pc + len };
 }
 
 static bool