This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [try 2nd 4/8] Displaced stepping for Thumb 16-bit insn


Some refacotring code makes this patch obsolete.  Here is the new one.

-- 
Yao (éå)
2011-05-05  Yao Qi  <yao@codesourcery.com>

	Support displaced stepping for Thumb 16-bit insns.
	* arm-tdep.c (THUMB_NOP) Define.
	(thumb_copy_unmodified_16bit): New.
	(thumb_copy_b, thumb_copy_bx_blx_reg): New.
	(thumb_copy_alu_reg): New.
	(arm_copy_svc): Move some common code to ...
	(copy_svc): ... here.  New.
	(thumb_copy_svc): New.
	(install_pc_relative): New.
	(thumb_copy_pc_relative_16bit): New.
	(thumb_decode_pc_relative_16bit): New.
	(thumb_copy_16bit_ldr_literal): New.
	(thumb_copy_cbnz_cbz): New.
	(cleanup_pop_pc_16bit): New.
	(thumb_copy_pop_pc_16bit): New.
	(thumb_process_displaced_16bit_insn): New.
	(thumb_process_displaced_32bit_insn): New.
	(thumb_process_displaced_insn): process thumb instruction.

---
 gdb/arm-tdep.c |  475 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 463 insertions(+), 12 deletions(-)

diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 2dd8c9e..83ac297 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -5118,6 +5118,7 @@ arm_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr)
 
 /* NOP instruction (mov r0, r0).  */
 #define ARM_NOP				0xe1a00000
+#define THUMB_NOP 0x4600
 
 /* Helper for register reads for displaced stepping.  In particular, this
    returns the PC as it would be seen by the instruction at its original
@@ -5340,6 +5341,23 @@ arm_copy_unmodified (struct gdbarch *gdbarch, uint32_t insn,
   return 0;
 }
 
+/* Copy 16-bit Thumb(Thumb and 16-bit Thumb-2) instruction without any
+   modification.  */
+static int
+thumb_copy_unmodified_16bit (struct gdbarch *gdbarch, unsigned int insn,
+			     const char *iname,
+			     struct displaced_step_closure *dsc)
+{
+  if (debug_displaced)
+    fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.4x, "
+			"opcode/class '%s' unmodified\n", insn,
+			iname);
+
+  dsc->modinsn[0] = insn;
+
+  return 0;
+}
+
 /* Preload instructions with immediate offset.  */
 
 static void
@@ -5586,6 +5604,44 @@ arm_copy_b_bl_blx (struct gdbarch *gdbarch, uint32_t insn,
   return 0;
 }
 
+/* Copy B Thumb instructions.  */
+static int
+thumb_copy_b (struct gdbarch *gdbarch, unsigned short insn,
+	      struct displaced_step_closure *dsc)
+{
+  unsigned int cond = 0;
+  int offset = 0;
+  unsigned short bit_12_15 = bits (insn, 12, 15);
+  CORE_ADDR from = dsc->insn_addr;
+
+  if (bit_12_15 == 0xd)
+    {
+      offset = sbits (insn, 0, 7);
+      cond = bits (insn, 8, 11);
+    }
+  else if (bit_12_15 == 0xe)
+    {
+       offset = sbits (insn, 0, 10);
+       cond = INST_AL;
+    }
+
+  if (debug_displaced)
+    fprintf_unfiltered (gdb_stdlog,
+			"displaced: copying b immediate insn %.4x "
+			"with offset %d\n", insn, offset);
+
+  dsc->u.branch.cond = cond;
+  dsc->u.branch.link = 0;
+  dsc->u.branch.exchange = 0;
+  dsc->u.branch.dest = from + 4 + offset;
+
+  dsc->modinsn[0] = THUMB_NOP;
+
+  dsc->cleanup = &cleanup_branch;
+
+  return 0;
+}
+
 /* Copy BX/BLX with register-specified destinations.  */
 
 static void
@@ -5631,6 +5687,26 @@ arm_copy_bx_blx_reg (struct gdbarch *gdbarch, uint32_t insn,
   return 0;
 }
 
+static int
+thumb_copy_bx_blx_reg (struct gdbarch *gdbarch, uint16_t insn,
+		       struct regcache *regs,
+		       struct displaced_step_closure *dsc)
+{
+  int link = bit (insn, 7);
+  unsigned int rm = bits (insn, 3, 6);
+
+  if (debug_displaced)
+    fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.4x",
+			(unsigned short) insn);
+
+  dsc->modinsn[0] = THUMB_NOP;
+
+  install_bx_blx_reg (gdbarch, regs, dsc, link, INST_AL, rm);
+
+  return 0;
+}
+
+
 /* Copy/cleanup arithmetic/logic instruction with immediate RHS.  */
 
 static void
@@ -5765,6 +5841,31 @@ arm_copy_alu_reg (struct gdbarch *gdbarch, uint32_t insn, struct regcache *regs,
   return 0;
 }
 
+static int
+thumb_copy_alu_reg (struct gdbarch *gdbarch, uint16_t insn,
+		    struct regcache *regs,
+		    struct displaced_step_closure *dsc)
+{
+  unsigned rn, rm, rd;
+
+  rd = bits (insn, 3, 6);
+  rn = (bit (insn, 7) << 3) | bits (insn, 0, 2);
+  rm = 2;
+
+  if (rd != ARM_PC_REGNUM && rn != ARM_PC_REGNUM)
+    return thumb_copy_unmodified_16bit(gdbarch, insn, "ALU reg", dsc);
+
+  if (debug_displaced)
+    fprintf_unfiltered (gdb_stdlog, "displaced: copying reg %s insn %.4x\n",
+			"ALU", (unsigned short) insn);
+
+  dsc->modinsn[0] = ((insn & 0xff00) | 0x08);
+
+  install_alu_reg (gdbarch, regs, dsc, rd, rn, rm);
+
+  return 0;
+}
+
 /* Cleanup/copy arithmetic/logic insns with shifted register RHS.  */
 
 static void
@@ -6439,21 +6540,16 @@ cleanup_svc (struct gdbarch *gdbarch, struct regcache *regs,
   displaced_write_reg (regs, dsc, ARM_PC_REGNUM, resume_addr, BRANCH_WRITE_PC);
 }
 
-static int
-
-arm_copy_svc (struct gdbarch *gdbarch, uint32_t insn,
-	      struct regcache *regs, struct displaced_step_closure *dsc)
-{
 
-  if (debug_displaced)
-    fprintf_unfiltered (gdb_stdlog, "displaced: copying svc insn %.8lx\n",
-			(unsigned long) insn);
+/* Common copy routine for svc instruciton.  */
 
+static int
+copy_svc (struct gdbarch *gdbarch, struct regcache *regs,
+	  struct displaced_step_closure *dsc)
+{
   /* Preparation: none.
      Insn: unmodified svc.
-     Cleanup: pc <- insn_addr + 4.  */
-
-  dsc->modinsn[0] = insn;
+     Cleanup: pc <- insn_addr + insn_size.  */
 
   /* Pretend we wrote to the PC, so cleanup doesn't set PC to the next
      instruction.  */
@@ -6467,7 +6563,34 @@ arm_copy_svc (struct gdbarch *gdbarch, uint32_t insn,
       dsc->cleanup = &cleanup_svc;
       return 0;
     }
+}
+
+static int
+arm_copy_svc (struct gdbarch *gdbarch, uint32_t insn,
+	      struct regcache *regs, struct displaced_step_closure *dsc)
+{
+
+  if (debug_displaced)
+    fprintf_unfiltered (gdb_stdlog, "displaced: copying svc insn %.8lx\n",
+			(unsigned long) insn);
+
+  dsc->modinsn[0] = insn;
+
+  return copy_svc (gdbarch, regs, dsc);
+}
+
+static int
+thumb_copy_svc (struct gdbarch *gdbarch, uint16_t insn,
+		struct regcache *regs, struct displaced_step_closure *dsc)
+{
+
+  if (debug_displaced)
+    fprintf_unfiltered (gdb_stdlog, "displaced: copying svc insn %.4x\n",
+			insn);
+
+  dsc->modinsn[0] = insn;
 
+  return copy_svc (gdbarch, regs, dsc);
 }
 
 /* Copy undefined instructions.  */
@@ -6929,11 +7052,339 @@ arm_decode_svc_copro (struct gdbarch *gdbarch, uint32_t insn, CORE_ADDR to,
 }
 
 static void
+install_pc_relative (struct gdbarch *gdbarch, struct regcache *regs,
+		     struct displaced_step_closure *dsc, int rd)
+{
+  /* ADR Rd, #imm
+
+     Rewrite as:
+
+     Preparation: Rd <- PC
+     Insn: ADD Rd, #imm
+     Cleanup: Null.
+  */
+
+  /* Rd <- PC */
+  int val = displaced_read_reg (regs, dsc, ARM_PC_REGNUM);
+  displaced_write_reg (regs, dsc, rd, val, CANNOT_WRITE_PC);
+}
+
+static int
+thumb_copy_pc_relative_16bit (struct gdbarch *gdbarch, struct regcache *regs,
+			      struct displaced_step_closure *dsc,
+			      int rd, unsigned int imm)
+{
+
+  /* Encoding T2: ADDS Rd, #imm */
+  dsc->modinsn[0] = (0x3000 | (rd << 8) | imm);
+
+  install_pc_relative (gdbarch, regs, dsc, rd);
+
+  return 0;
+}
+
+static int
+thumb_decode_pc_relative_16bit (struct gdbarch *gdbarch, uint16_t insn,
+				struct regcache *regs,
+				struct displaced_step_closure *dsc)
+{
+  unsigned int rd = bits (insn, 8, 10);
+  unsigned int imm8 = bits (insn, 0, 7);
+
+  if (debug_displaced)
+    fprintf_unfiltered (gdb_stdlog,
+			"displaced: copying thumb adr r%d, #%d insn %.4x\n",
+			rd, imm8, insn);
+
+  return thumb_copy_pc_relative_16bit (gdbarch, regs, dsc, rd, imm8);
+}
+
+static int
+thumb_copy_16bit_ldr_literal (struct gdbarch *gdbarch, unsigned short insn1,
+			      struct regcache *regs,
+			      struct displaced_step_closure *dsc)
+{
+  unsigned int rt = bits (insn1, 8, 7);
+  unsigned int pc;
+  int imm8 = sbits (insn1, 0, 7);
+  CORE_ADDR from = dsc->insn_addr;
+
+  /* LDR Rd, #imm8
+
+     Rwrite as:
+
+     Preparation: tmp2 <- R2, tmp3 <- R3, R2 <- PC, R3 <- #imm8;
+                  if (Rd is not R0) tmp0 <- R0;
+     Insn: LDR R0, [R2, R3];
+     Cleanup: R2 <- tmp2, R3 <- tmp3,
+              if (Rd is not R0) Rd <- R0, R0 <- tmp0 */
+
+  if (debug_displaced)
+    fprintf_unfiltered (gdb_stdlog, "displaced: copying thumb ldr literal "
+			"insn %.4x\n", insn1);
+
+  dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
+  dsc->tmp[2] = displaced_read_reg (regs, dsc, 2);
+  dsc->tmp[3] = displaced_read_reg (regs, dsc, 3);
+  pc = displaced_read_reg (regs, dsc, ARM_PC_REGNUM);
+
+  displaced_write_reg (regs, dsc, 2, pc, CANNOT_WRITE_PC);
+  displaced_write_reg (regs, dsc, 3, imm8, CANNOT_WRITE_PC);
+
+  dsc->rd = rt;
+  dsc->u.ldst.xfersize = 4;
+  dsc->u.ldst.rn = 0;
+  dsc->u.ldst.immed = 0;
+  dsc->u.ldst.writeback = 0;
+  dsc->u.ldst.restore_r4 = 0;
+
+  dsc->modinsn[0] = 0x58d0; /* ldr r0, [r2, r3]*/
+
+  dsc->cleanup = &cleanup_load;
+
+  return 0;
+}
+
+/* Copy Thumb cbnz/cbz insruction.  */
+
+static int
+thumb_copy_cbnz_cbz (struct gdbarch *gdbarch, uint16_t insn1,
+		     struct regcache *regs,
+		     struct displaced_step_closure *dsc)
+{
+  int non_zero = bit (insn1, 11);
+  unsigned int imm5 = (bit (insn1, 9) << 6) | (bits (insn1, 3, 7) << 1);
+  CORE_ADDR from = dsc->insn_addr;
+  int rn = bits (insn1, 0, 2);
+  int rn_val = displaced_read_reg (regs, dsc, rn);
+
+  dsc->u.branch.cond = (rn_val && non_zero) || (!rn_val && !non_zero);
+  /* CBNZ and CBZ do not affect the condition flags.  If condition is true,
+     set it INST_AL, so cleanup_branch will know branch is taken, otherwise,
+     condition is false, let it be, cleanup_branch will do nothing.  */
+  if (dsc->u.branch.cond)
+    dsc->u.branch.cond = INST_AL;
+
+  dsc->u.branch.link = 0;
+  dsc->u.branch.exchange = 0;
+
+  dsc->u.branch.dest = from + 2 + imm5;
+
+  if (debug_displaced)
+    fprintf_unfiltered (gdb_stdlog, "displaced: copying %s [r%d = 0x%x]"
+			" insn %.4x to %.8lx\n", non_zero ? "cbnz" : "cbz",
+			rn, rn_val, insn1, dsc->u.branch.dest);
+
+  dsc->modinsn[0] = THUMB_NOP;
+
+  dsc->cleanup = &cleanup_branch;
+  return 0;
+}
+
+static void
+cleanup_pop_pc_16bit(struct gdbarch *gdbarch, struct regcache *regs,
+		     struct displaced_step_closure *dsc)
+{
+  int rx = dsc->u.block.regmask ? 8 : 0;
+  int rx_val = displaced_read_reg (regs, dsc, rx);
+
+  displaced_write_reg (regs, dsc, ARM_PC_REGNUM, rx_val, BX_WRITE_PC);
+  displaced_write_reg (regs, dsc, rx, dsc->tmp[0], CANNOT_WRITE_PC);
+}
+
+static int
+thumb_copy_pop_pc_16bit (struct gdbarch *gdbarch, unsigned short insn1,
+			 struct regcache *regs,
+			 struct displaced_step_closure *dsc)
+{
+  dsc->u.block.regmask = insn1 & 0x00ff;
+
+  /* Rewrite instruction: POP {rX, rY, ...,rZ, PC}
+     to :
+
+     (1) register list is not empty,
+     Prepare: tmp[0] <- r8,
+
+     POP {rX};   PC is stored in rX
+     MOV r8, rX; finally, PC is stored in r8
+     POP {rX, rY, ...., rZ}
+
+     Cleanup: PC <-r8, r8 <- tmp[0]
+
+     (2) register list is empty,
+     Prepare: tmp[0] <- r0,
+
+     POP {r0}
+
+     Cleanup: PC <- r0, r0 <- tmp[0]
+  */
+  if (debug_displaced)
+    fprintf_unfiltered (gdb_stdlog,
+			"displaced: copying thumb pop {%.8x, pc} insn %.4x\n",
+			dsc->u.block.regmask, insn1);
+
+  if (dsc->u.block.regmask != 0)
+    {
+      int rx = 0;
+
+       dsc->tmp[0] = displaced_read_reg (regs, dsc, 8);
+
+      /* Look for the first register in register list.  */
+      for (rx = 0; rx < 8; rx++)
+	if (dsc->u.block.regmask & (1 << rx))
+	  break;
+
+      dsc->modinsn[0] = (0xbc00 | (1 << rx)); /* POP {rX} */
+      dsc->modinsn[1] = (0x4680 | (rx << 3)); /* MOV r8, rX */
+      dsc->modinsn[2] = (insn1 & 0xfeff);     /* POP {rX, rY, ..., rZ} */
+
+      dsc->numinsns = 3;
+    }
+  else
+    {
+      dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
+
+      dsc->modinsn[0] = 0xbc00; /* POP {r0} */
+      /* dsc->modinsn[ (1, 0x4683); */ /* MOV PC, r0 */
+
+      dsc->numinsns = 1;
+    }
+
+  dsc->cleanup = &cleanup_pop_pc_16bit;
+  return 0;
+}
+
+static void
+thumb_process_displaced_16bit_insn (struct gdbarch *gdbarch, uint16_t insn1,
+				    struct regcache *regs,
+				    struct displaced_step_closure *dsc)
+{
+  unsigned short op_bit_12_15 = bits (insn1, 12, 15);
+  unsigned short op_bit_10_11 = bits (insn1, 10, 11);
+  int err = 0;
+
+  /* 16-bit thumb instructions.  */
+  switch (op_bit_12_15)
+    {
+      /* Shift (imme), add, subtract, move and compare.  */
+    case 0: case 1: case 2: case 3:
+      err = thumb_copy_unmodified_16bit (gdbarch, insn1,"shift/add/sub/mov/cmp",
+					 dsc);
+      break;
+    case 4:
+      switch (op_bit_10_11)
+	{
+	case 0: /* Data-processing */
+	  err = thumb_copy_unmodified_16bit (gdbarch, insn1,"data-processing",
+					     dsc);
+	  break;
+	case 1: /* Special data instructions and branch and exchange.  */
+	  {
+	    unsigned short op = bits (insn1, 7, 9);
+	    if (op == 6 || op == 7) /* BX or BLX */
+	      err = thumb_copy_bx_blx_reg (gdbarch, insn1, regs, dsc);
+	    else if (bits (insn1, 6, 7) != 0) /* ADD/MOV/CMP high registers.  */
+	      err = thumb_copy_alu_reg (gdbarch, insn1, regs, dsc);
+	    else
+	      err = thumb_copy_unmodified_16bit (gdbarch, insn1, "special data",
+						 dsc);
+	  }
+	  break;
+	default: /* LDR (literal) */
+	  err = thumb_copy_16bit_ldr_literal (gdbarch, insn1, regs, dsc);
+	}
+      break;
+    case 5: case 6: case 7: case 8: case 9: /* Load/Store single data item */
+      err = thumb_copy_unmodified_16bit (gdbarch, insn1,"ldr/str", dsc);
+      break;
+    case 10:
+      if (op_bit_10_11 < 2) /* Generate PC-relative address */
+	err = thumb_decode_pc_relative_16bit (gdbarch, insn1, regs, dsc);
+      else /* Generate SP-relative address */
+	err = thumb_copy_unmodified_16bit (gdbarch, insn1,"sp-relative", dsc);
+      break;
+    case 11: /* Misc 16-bit instructions */
+      {
+	switch (bits (insn1, 8, 11))
+	  {
+	  case 1: case 3:  case 9: case 11: /* CBNZ, CBZ */
+	    err = thumb_copy_cbnz_cbz (gdbarch, insn1, regs, dsc);
+	    break;
+	  case 12: case 13: /* POP */
+	    if (bit (insn1, 8)) /* PC is in register list.  */
+	      err = thumb_copy_pop_pc_16bit (gdbarch, insn1, regs, dsc);
+	    else
+	      err = thumb_copy_unmodified_16bit (gdbarch, insn1,"pop", dsc);
+	    break;
+	  case 15: /* If-Then, and hints */
+	    if (bits (insn1, 0, 3))
+	      /* If-Then makes up to four following instructions conditional.
+		 IT instruction itself is not conditional, so handle it as a
+		 common unmodified instruction.  */
+	      err = thumb_copy_unmodified_16bit (gdbarch, insn1,"If-Then", dsc);
+	    else
+	      err = thumb_copy_unmodified_16bit (gdbarch, insn1,"hints", dsc);
+	    break;
+	  default:
+	    err = thumb_copy_unmodified_16bit (gdbarch, insn1,"misc", dsc);
+	  }
+      }
+      break;
+    case 12:
+      if (op_bit_10_11 < 2) /* Store multiple registers */
+	err = thumb_copy_unmodified_16bit (gdbarch, insn1,"stm", dsc);
+      else /* Load multiple registers */
+	err = thumb_copy_unmodified_16bit (gdbarch, insn1,"ldm", dsc);
+      break;
+    case 13: /* Conditional branch and supervisor call */
+      if (bits (insn1, 9, 11) != 7) /* conditional branch */
+	err = thumb_copy_b (gdbarch, insn1, dsc);
+      else
+	err = thumb_copy_svc (gdbarch, insn1, regs, dsc);
+      break;
+    case 14: /* Unconditional branch */
+      err = thumb_copy_b (gdbarch, insn1, dsc);
+      break;
+    default:
+      err = 1;
+    }
+
+  if (err)
+    internal_error (__FILE__, __LINE__,
+		    _("thumb_process_displaced_16bit_insn: Instruction decode error"));
+}
+
+static void
+thumb_process_displaced_32bit_insn (struct gdbarch *gdbarch, uint16_t insn1,
+				    uint16_t insn2, struct regcache *regs,
+				    struct displaced_step_closure *dsc)
+{
+  error (_("Displaced stepping is only supported in ARM mode and Thumb 16bit instructions"));
+}
+
+static void
 thumb_process_displaced_insn (struct gdbarch *gdbarch, CORE_ADDR from,
 			      CORE_ADDR to, struct regcache *regs,
 			      struct displaced_step_closure *dsc)
 {
-  error (_("Displaced stepping is only supported in ARM mode"));
+  enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
+  uint16_t insn1
+    = read_memory_unsigned_integer (from, 2, byte_order_for_code);
+
+  if (debug_displaced)
+    fprintf_unfiltered (gdb_stdlog, "displaced: process thumb insn %.4x "
+			"at %.8lx\n", insn1, (unsigned long) from);
+
+  dsc->is_thumb = 1;
+  dsc->insn_size = thumb_insn_size (insn1);
+  if (thumb_insn_size (insn1) == 4)
+    {
+      uint16_t insn2
+	= read_memory_unsigned_integer (from + 2, 2, byte_order_for_code);
+      thumb_process_displaced_32bit_insn(gdbarch, insn1, insn2, regs, dsc);
+    }
+  else
+    thumb_process_displaced_16bit_insn(gdbarch, insn1, regs, dsc);
 }
 
 void
-- 
1.7.0.4


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]