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: [patch 3/3] Displaced stepping for 16-bit Thumb instructions


Patch 3 is about supporting 16-bit Thumb displaced stepping.  In this
patch, we decode 16-bit instruction, and process them.  We also leave a
slot for 32-bit Thumb instructions, and put an error there.

Test cases are updated accordingly for some PC-related instructions.

-- 
Yao (éå)
gdb/
2010-12-25  Yao Qi  <yao@codesourcery.com>

	Displaced stepping support for 16-bit Thumb insns.

	* gdb/arm-tdep.c (THUMB_NOP): New macro.
	(displaced_read_reg): Support Thumb mode.
	(thumb_copy_unmodified_16bit): New.
	(cleanup_branch): Move some code to ...
	(cleanup_branch_1): ... here.  New.  Support Thumb mode.
	(cleanup_cbz_cbnz): New.
	(copy_b_bl_blx): Move some code to ...
	(arm_copy_b_bl_blx): ... here.  New.
	(thumb_copy_b): New.
	(copy_bx_blx_reg): Move some code to ...
	(arm_copy_bx_blx_reg): ... here.  New.
	(thumb_copy_bx_blx_reg): New.
	(decode_unconditional): Update caller.
	(decode_miscellaneous): Likewise.
	(decode_b_bl_ldmstm): Likewise.
	(copy_ldr_str_ldrb_strb): Replace magic number with macro.
	(thumb_decode_dp): New.
	(thumb_decode_pc_relative): New.
	(thumb_copy_16bit_ldr_literal): New.
	(thumb_copy_cbnz_cbz): New.
	(thumb_process_displaced_16bit_insn): New.
	(thumb_process_displaced_32bit_insn): New.

gdb/testsuite/
2010-12-25  Yao Qi  <yao@codesourcery.com>

	* gdb.arch/arm-disp-step.S: Test cbnz/cbz, adr and ldr.
	* gdb.arch/arm-disp-step.exp: Likewise.

diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 93c4e50..4d766c9 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -4326,6 +4326,9 @@ arm_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr)
 
 /* NOP instruction (mov r0, r0).  */
 #define ARM_NOP				0xe1a00000
+#define THUMB_NOP				0x4600
+
+static int displaced_in_arm_mode (struct regcache *regs);
 
 /* Helper for register reads for displaced stepping.  In particular, this
    returns the PC as it would be seen by the instruction at its original
@@ -4338,10 +4341,15 @@ displaced_read_reg (struct regcache *regs, CORE_ADDR from, int regno)
 
   if (regno == 15)
     {
+      if (displaced_in_arm_mode (regs))
+	from += 8;
+      else
+	from += 6;
+
       if (debug_displaced)
 	fprintf_unfiltered (gdb_stdlog, "displaced: read pc value %.8lx\n",
-			    (unsigned long) from + 8);
-      return (ULONGEST) from + 8;  /* Pipeline offset.  */
+			    (unsigned long) from);
+      return (ULONGEST) from;  /* Pipeline offset.  */
     }
   else
     {
@@ -4530,6 +4538,21 @@ copy_unmodified (struct gdbarch *gdbarch, uint32_t insn,
   return 0;
 }
 
+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);
+
+  RECORD_MOD_16BIT_INSN (0, insn);
+
+  return 0;
+}
+
 /* Preload instructions with immediate offset.  */
 
 static void
@@ -4668,16 +4691,11 @@ copy_copro_load_store (struct gdbarch *gdbarch, uint32_t insn,
   return 0;
 }
 
-/* Clean up branch instructions (actually perform the branch, by setting
-   PC).  */
-
 static void
-cleanup_branch (struct gdbarch *gdbarch, struct regcache *regs,
-		struct displaced_step_closure *dsc)
+cleanup_branch_1 (struct gdbarch *gdbarch, struct regcache *regs,
+		  struct displaced_step_closure *dsc, unsigned int branch_taken)
 {
   ULONGEST from = dsc->insn_addr;
-  uint32_t status = displaced_read_reg (regs, from, ARM_PS_REGNUM);
-  int branch_taken = condition_true (dsc->u.branch.cond, status);
   enum pc_write_style write_pc = dsc->u.branch.exchange
 				 ? BX_WRITE_PC : BRANCH_WRITE_PC;
 
@@ -4687,29 +4705,45 @@ cleanup_branch (struct gdbarch *gdbarch, struct regcache *regs,
   if (dsc->u.branch.link)
     {
       ULONGEST pc = displaced_read_reg (regs, from, ARM_PC_REGNUM);
-      displaced_write_reg (regs, dsc, ARM_LR_REGNUM, pc - 4, CANNOT_WRITE_PC);
+
+      if (displaced_in_arm_mode (regs))
+	displaced_write_reg (regs, dsc, ARM_LR_REGNUM, pc - 4, CANNOT_WRITE_PC);
+      else
+	displaced_write_reg (regs, dsc, ARM_LR_REGNUM, (pc - 2) | 1u,
+			     CANNOT_WRITE_PC);
     }
 
   displaced_write_reg (regs, dsc, ARM_PC_REGNUM, dsc->u.branch.dest, write_pc);
 }
 
+/* Clean up branch instructions (actually perform the branch, by setting
+   PC).  */
+static void
+cleanup_branch(struct gdbarch *gdbarch, struct regcache *regs,
+	       struct displaced_step_closure *dsc)
+{
+  ULONGEST from = dsc->insn_addr;
+  uint32_t status = displaced_read_reg (regs, from, ARM_PS_REGNUM);
+  int branch_taken = condition_true (dsc->u.branch.cond, status);
+
+  cleanup_branch_1 (gdbarch, regs, dsc, branch_taken);
+}
+
+static void
+cleanup_cbz_cbnz(struct gdbarch *gdbarch, struct regcache *regs,
+	       struct displaced_step_closure *dsc)
+{
+  cleanup_branch_1 (gdbarch, regs, dsc, dsc->u.branch.cond);
+}
+
 /* Copy B/BL/BLX instructions with immediate destinations.  */
 
 static int
-copy_b_bl_blx (struct gdbarch *gdbarch, uint32_t insn,
-	       struct regcache *regs, struct displaced_step_closure *dsc)
+copy_b_bl_blx (struct gdbarch *gdbarch, unsigned int cond, int exchange,
+	       int link, long offset, struct regcache *regs,
+	       struct displaced_step_closure *dsc)
 {
-  unsigned int cond = bits (insn, 28, 31);
-  int exchange = (cond == 0xf);
-  int link = exchange || bit (insn, 24);
   CORE_ADDR from = dsc->insn_addr;
-  long offset;
-
-  if (debug_displaced)
-    fprintf_unfiltered (gdb_stdlog, "displaced: copying %s immediate insn "
-			"%.8lx\n", (exchange) ? "blx" : (link) ? "bl" : "b",
-			(unsigned long) insn);
-
   /* Implement "BL<cond> <label>" as:
 
      Preparation: cond <- instruction condition
@@ -4718,6 +4752,40 @@ copy_b_bl_blx (struct gdbarch *gdbarch, uint32_t insn,
 
      B<cond> similar, but don't set r14 in cleanup.  */
 
+
+  dsc->u.branch.cond = cond;
+  dsc->u.branch.link = link;
+  dsc->u.branch.exchange = exchange;
+
+  if (arm_pc_is_thumb (gdbarch, from))
+    {
+      /* Plus the size of THUMB_NOP and B/BL/BLX.  */
+      dsc->u.branch.dest = from + 2 + 4 + offset;
+      RECORD_MOD_16BIT_INSN (0, THUMB_NOP);
+    }
+  else
+    {
+      dsc->u.branch.dest = from + 8 + offset;
+      RECORD_MOD_32BIT_INSN (0, ARM_NOP);
+    }
+
+  dsc->cleanup = &cleanup_branch;
+
+  return 0;
+}
+static int
+arm_copy_b_bl_blx (struct gdbarch *gdbarch, uint32_t insn,
+		   struct regcache *regs, struct displaced_step_closure *dsc)
+{
+  unsigned int cond = bits (insn, 28, 31);
+  int exchange = (cond == 0xf);
+  int link = exchange || bit (insn, 24);
+  long offset;
+
+  if (debug_displaced)
+    fprintf_unfiltered (gdb_stdlog, "displaced: copying %s immediate insn "
+			"%.8lx\n", (exchange) ? "blx" : (link) ? "bl" : "b",
+			(unsigned long) insn);
   if (exchange)
     /* For BLX, set bit 0 of the destination.  The cleanup_branch function will
        then arrange the switch into Thumb mode.  */
@@ -4728,12 +4796,40 @@ copy_b_bl_blx (struct gdbarch *gdbarch, uint32_t insn,
   if (bit (offset, 25))
     offset = offset | ~0x3ffffff;
 
+  return copy_b_bl_blx (gdbarch, cond, exchange, link, offset, regs, dsc);
+}
+
+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 = link;
-  dsc->u.branch.exchange = exchange;
-  dsc->u.branch.dest = from + 8 + offset;
+  dsc->u.branch.link = 0;
+  dsc->u.branch.exchange = 0;
+  dsc->u.branch.dest = from + 4 + offset;
 
-  RECORD_MOD_32BIT_INSN (0, ARM_NOP);
+  RECORD_MOD_16BIT_INSN (0, THUMB_NOP);
 
   dsc->cleanup = &cleanup_branch;
 
@@ -4743,19 +4839,12 @@ copy_b_bl_blx (struct gdbarch *gdbarch, uint32_t insn,
 /* Copy BX/BLX with register-specified destinations.  */
 
 static int
-copy_bx_blx_reg (struct gdbarch *gdbarch, uint32_t insn,
-		 struct regcache *regs, struct displaced_step_closure *dsc)
+copy_bx_blx_reg (struct gdbarch *gdbarch, unsigned int cond, int link,
+		 unsigned int rm, struct regcache *regs,
+		 struct displaced_step_closure *dsc)
 {
-  unsigned int cond = bits (insn, 28, 31);
-  /* BX:  x12xxx1x
-     BLX: x12xxx3x.  */
-  int link = bit (insn, 5);
-  unsigned int rm = bits (insn, 0, 3);
   CORE_ADDR from = dsc->insn_addr;
-
-  if (debug_displaced)
-    fprintf_unfiltered (gdb_stdlog, "displaced: copying %s register insn "
-			"%.8lx\n", (link) ? "blx" : "bx", (unsigned long) insn);
+  int is_thumb = arm_pc_is_thumb (gdbarch, from);
 
   /* Implement {BX,BLX}<cond> <reg>" as:
 
@@ -4767,16 +4856,56 @@ copy_bx_blx_reg (struct gdbarch *gdbarch, uint32_t insn,
 
   dsc->u.branch.dest = displaced_read_reg (regs, from, rm);
 
-  dsc->u.branch.cond = cond;
   dsc->u.branch.link = link;
   dsc->u.branch.exchange = 1;
 
-  RECORD_MOD_32BIT_INSN (0, ARM_NOP);
+  if (is_thumb)
+    {
+      /* Always true for thumb.  */
+      dsc->u.branch.cond = INST_AL;
+      RECORD_MOD_16BIT_INSN (0, THUMB_NOP);
+    }
+  else
+    {
+      dsc->u.branch.cond = cond;
+      RECORD_MOD_32BIT_INSN (0, ARM_NOP);
+    }
 
   dsc->cleanup = &cleanup_branch;
 
   return 0;
 }
+static int
+arm_copy_bx_blx_reg (struct gdbarch *gdbarch, uint32_t insn,
+		     struct regcache *regs, struct displaced_step_closure *dsc)
+{
+  unsigned int cond = bits (insn, 28, 31);
+  /* BX:  x12xxx1x
+     BLX: x12xxx3x.  */
+  int link = bit (insn, 5);
+  unsigned int rm = bits (insn, 0, 3);
+
+  if (debug_displaced)
+    fprintf_unfiltered (gdb_stdlog, "displaced: copying %s register insn "
+			"%.8lx\n", (link) ? "blx" : "bx", (unsigned long) insn);
+
+  return copy_bx_blx_reg (gdbarch, cond, link, rm, regs, dsc);
+}
+
+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 %s register insn "
+			"%.4x\n", (link) ? "blx" : "bx", (unsigned short) insn);
+
+  return copy_bx_blx_reg (gdbarch, INST_AL, link, rm, regs, dsc);
+}
 
 /* Copy/cleanup arithmetic/logic instruction with immediate RHS. */
 
@@ -5171,7 +5300,7 @@ copy_ldr_str_ldrb_strb (struct gdbarch *gdbarch, uint32_t insn,
      Otherwise we don't know what value to write for PC, since the offset is
      architecture-dependent (sometimes PC+8, sometimes PC+12).  */
 
-  if (load || rt != 15)
+  if (load || rt != ARM_PC_REGNUM)
     {
       dsc->u.ldst.restore_r4 = 0;
 
@@ -5694,7 +5823,7 @@ decode_unconditional (struct gdbarch *gdbarch, uint32_t insn,
       return copy_unmodified (gdbarch, insn, "rfe", dsc);
 
     case 0x4: case 0x5: case 0x6: case 0x7:
-      return copy_b_bl_blx (gdbarch, insn, regs, dsc);
+      return arm_copy_b_bl_blx (gdbarch, insn, regs, dsc);
 
     case 0x8:
       switch ((insn & 0xe00000) >> 21)
@@ -5777,7 +5906,7 @@ decode_miscellaneous (struct gdbarch *gdbarch, uint32_t insn,
 
     case 0x1:
       if (op == 0x1)  /* bx.  */
-	return copy_bx_blx_reg (gdbarch, insn, regs, dsc);
+	return arm_copy_bx_blx_reg (gdbarch, insn, regs, dsc);
       else if (op == 0x3)
 	return copy_unmodified (gdbarch, insn, "clz", dsc);
       else
@@ -5791,8 +5920,8 @@ decode_miscellaneous (struct gdbarch *gdbarch, uint32_t insn,
 	return copy_undef (gdbarch, insn, dsc);
 
     case 0x3:
-      if (op == 0x1)
-	return copy_bx_blx_reg (gdbarch, insn, regs, dsc);  /* blx register.  */
+      if (op == 0x1) /* blx register.  */
+	return arm_copy_bx_blx_reg (gdbarch, insn, regs, dsc);
       else
 	return copy_undef (gdbarch, insn, dsc);
 
@@ -5955,7 +6084,7 @@ decode_b_bl_ldmstm (struct gdbarch *gdbarch, int32_t insn,
 		    struct regcache *regs, struct displaced_step_closure *dsc)
 {
   if (bit (insn, 25))
-    return copy_b_bl_blx (gdbarch, insn, regs, dsc);
+    return arm_copy_b_bl_blx (gdbarch, insn, regs, dsc);
   else
     return copy_block_xfer (gdbarch, insn, regs, dsc);
 }
@@ -6036,12 +6165,231 @@ decode_svc_copro (struct gdbarch *gdbarch, uint32_t insn, CORE_ADDR to,
     return copy_undef (gdbarch, insn, dsc);  /* Possibly unreachable.  */
 }
 
+static int
+thumb_decode_dp (struct gdbarch *gdbarch, unsigned short insn,
+		 struct displaced_step_closure *dsc)
+{
+  /* 16-bit data-processing insns are not related to PC.  */
+  return thumb_copy_unmodified_16bit (gdbarch, insn,"data-processing", dsc);
+}
+
+static int
+thumb_decode_pc_relative (struct gdbarch *gdbarch, unsigned short insn,
+			  struct regcache *regs,
+			  struct displaced_step_closure *dsc)
+{
+  unsigned int rd = bits (insn, 8, 10);
+  unsigned int imm8 = bits (insn, 0, 7);
+  CORE_ADDR from = dsc->insn_addr;
+  int val;
+
+  /* ADR Rd, #imm8
+
+     Rewrite as:
+
+     Preparation: Rd <- PC
+     Insn: ADD Rd, #imm8
+     Cleanup: Null.
+   */
+
+
+  if (debug_displaced)
+    fprintf_unfiltered (gdb_stdlog,
+			"displaced: copying thumb adr r%d, #%d insn %.4x\n",
+			rd, imm8, insn);
+
+  /* Rd <- PC */
+  val = displaced_read_reg (regs, from, ARM_PC_REGNUM);
+  displaced_write_reg (regs, dsc, rd, val, CANNOT_WRITE_PC);
+
+  /* ADDS Rd, #imm8 */
+  RECORD_MOD_32BIT_INSN (0, 0x3000 | (rd << 8) | imm8);
+
+  return 0;
+}
+
+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, from, 0);
+  dsc->tmp[2] = displaced_read_reg (regs, from, 2);
+  dsc->tmp[3] = displaced_read_reg (regs, from, 3);
+  pc = displaced_read_reg (regs, from, 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;
+
+  RECORD_MOD_16BIT_INSN (0, 0x58d0); /* ldr r0, [r2, r3]*/
+
+  dsc->cleanup = &cleanup_load;
+
+  return 0;
+}
+
+static int
+thumb_copy_cbnz_cbz (struct gdbarch *gdbarch, unsigned short 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, from, rn);
+
+  dsc->u.branch.cond = (rn_val && non_zero) || (!rn_val && !non_zero);
+  dsc->u.branch.link = 0;
+  dsc->u.branch.exchange = 0;
+
+  dsc->u.branch.dest = from + 4 + 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);
+
+  RECORD_MOD_16BIT_INSN (0, THUMB_NOP);
+
+  dsc->cleanup = &cleanup_cbz_cbnz;
+  return 0;
+}
+
+static void
+thumb_process_displaced_16bit_insn (struct gdbarch *gdbarch,
+				    unsigned short insn1, CORE_ADDR to,
+				    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,"", dsc);
+      break;
+    case 4:
+      switch (op_bit_10_11)
+	{
+	case 0: /* Data-processing */
+	  err = thumb_decode_dp (gdbarch, insn1, 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
+	      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 (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;
+	  default:
+	    err = thumb_copy_unmodified_16bit (gdbarch, insn1,"", 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_unmodified_16bit (gdbarch, insn1,"svc", dsc);
+      break;
+    case 14: /* Unconditional branch */
+      err = thumb_copy_b (gdbarch, insn1, dsc);
+      break;
+    default:
+      internal_error (__FILE__, __LINE__,
+		      _("thumb_process_displaced_insn: Instruction decode error"));
+    }
+
+  if (err)
+    internal_error (__FILE__, __LINE__,
+		    _("thumb_process_displaced_insn: Instruction decode error"));
+}
+
+static void
+thumb_process_displaced_32bit_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 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);
+  unsigned short 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);
+
+  if ((bits (insn1, 13, 15) == 7) && (bits (insn1, 11, 12)))
+    thumb_process_displaced_32bit_insn(gdbarch, from, to, regs, dsc);
+  else
+    thumb_process_displaced_16bit_insn(gdbarch, insn1, to, regs, dsc);
 }
 
 void
diff --git a/gdb/testsuite/gdb.arch/arm-disp-step.S b/gdb/testsuite/gdb.arch/arm-disp-step.S
index d748718..2e5b7ba 100644
--- a/gdb/testsuite/gdb.arch/arm-disp-step.S
+++ b/gdb/testsuite/gdb.arch/arm-disp-step.S
@@ -48,6 +48,26 @@ test_ret_end:
 	bl test_ldm_stm_pc
 #endif
 
+	/* Test ldrX literal in ARM and Thumb-2 */
+#if !defined (__thumb__)
+	bl test_ldr_literal
+#endif
+
+	/* Test ldr literal in Thumb */
+#if defined(__thumb__)
+	bl test_ldr_literal_16
+#endif
+
+	/* Test cbnz/cbz in Thumb-2 */
+#if defined(__thumb2__)
+	bl test_cbz_cbnz
+#endif
+
+	/* Test adr in Thumb and Thumb-2 */
+#if defined(__thumb) || defined(__thumb2__)
+	bl test_adr
+#endif
+	
 	/* Return */
 	mov     sp, r7
 	sub     sp, sp, #4
@@ -118,3 +138,75 @@ test_ldm_stm_pc_ret:
 	.word	test_ldm_stm_pc_ret
 	.size test_ldm_stm_pc, .-test_ldm_stm_pc
 #endif
+	
+#if !defined (__thumb__)
+	.global test_ldr_literal
+	.type test_ldr_literal, %function
+test_ldr_literal:
+	ldrh	r0, [pc]
+	.global test_ldrsb_literal
+test_ldrsb_literal:
+	ldrsb	r0, [pc]
+	.global test_ldrsh_literal
+test_ldrsh_literal:
+	ldrsh	r0, [pc]
+	.global test_ldr_literal_end
+test_ldr_literal_end:
+	bx lr
+	.size test_ldr_literal, .-test_ldr_literal
+#endif
+
+#if defined(__thumb__)
+	.global test_ldr_literal_16
+	.code   16
+	.thumb_func
+test_ldr_literal_16:
+	ldr	r0, .L2
+	.global test_ldr_literal_16_end
+test_ldr_literal_16_end:
+	bx lr
+	.align	2
+.L2:
+	.word	test_ldr_literal_16
+	.size test_ldr_literal_16, .-test_ldr_literal_16
+#endif
+
+#if defined(__thumb2__)
+	.global test_cbz_cbnz
+	.code   16
+	.thumb_func
+test_cbz_cbnz:
+	movs 	r0, #0
+	.global test_zero_cbnz
+test_zero_cbnz:
+	cbnz	r0, .L3
+	.global test_zero_cbz
+test_zero_cbz:
+	cbz	r0, .L3
+.L3:
+	movs	r0, #1
+	.global test_non_zero_cbz
+test_non_zero_cbz:
+	cbz	r0, .L4
+	.global test_non_zero_cbnz
+test_non_zero_cbnz:
+	cbnz	r0, .L4
+	nop
+.L4:
+	.global test_cbz_cbnz_end
+test_cbz_cbnz_end:
+	bx lr
+	.size test_cbz_cbnz, .-test_cbz_cbnz
+#endif
+
+#if defined(__thumb) || defined(__thumb2__)
+	.global test_adr
+	.code   16
+	.thumb_func
+test_adr:
+	adr	r0, #1
+	.global test_adr_end
+test_adr_end:
+	bx lr
+	.size test_adr, .-test_adr
+#endif
\ No newline at end of file
diff --git a/gdb/testsuite/gdb.arch/arm-disp-step.exp b/gdb/testsuite/gdb.arch/arm-disp-step.exp
index 826f728..51b7951 100644
--- a/gdb/testsuite/gdb.arch/arm-disp-step.exp
+++ b/gdb/testsuite/gdb.arch/arm-disp-step.exp
@@ -37,6 +37,22 @@ if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable [list
     return -1
 }
 
+# Try to resume program with displaced stepping.  If displaced stepping is
+# not supported, turn it off, and continue.
+
+proc try_continue_with_displaced_step { msg loc } {
+    gdb_test_no_output "set displaced-stepping on"
+    gdb_test_multiple "continue" "continue to test_call_end" {
+	-re ".*$loc.*" {
+	    pass "continue to $msg"
+	}
+	-re "Displaced stepping is only supported in.*" {
+	    gdb_test_no_output "set displaced-stepping off"
+	    gdb_test "continue" ".*"
+	    kfail "gdb/NNNN" $msg
+	}
+    }
+}
 
 #########################################
 # Test ldm/stm related to PC.
@@ -50,7 +66,11 @@ proc test_ldm_stm_pc {} {
 	}
 	-re "Function \"test_ldm_stm_pc\" not defined\..*Make breakpoint pending on future shared library load.*y or .n.. $" {
 	    gdb_test "n" "" "Test case is compiled in Thumb mode"
-	    return
+	    return 0
+	}
+	-re "No symbol.*" {
+	    pass "break test_ldm_stm_pc"
+	    return 0
 	}
     }
 
@@ -68,10 +88,75 @@ proc test_ldm_stm_pc {} {
     gdb_continue_to_breakpoint "continue to test_ldm_stm_pc_ret" \
 	".*bx lr.*"
 }
+
+#########################################
+# Test ldrX literal
+proc test_ldr_literal {} {
+    global srcfile
+    # Try to set breakpoint on test_ldm_stm_pc.  If symbol 'test_ldm_stm_pc'
+    # can't be resolved, test case is compiled in Thumb mode, skip it.
+    gdb_test_multiple "break *test_ldr_literal" "break test_ldr_literal" {
+	-re "Breakpoint.*at.* file .*$srcfile, line.*" {
+	    pass "break test_ldr_literal"
+	}
+	-re "Function \"test_ldr_literal\" not defined\..*Make breakpoint pending on future shared library load.*y or .n.. $" {
+	    gdb_test "n" "" "Test case is compiled in Thumb mode"
+	    return 0
+	}
+	-re "No symbol.*" {
+	    return 0
+	}
+    }
+
+    gdb_test "break *test_ldrsb_literal" \
+	"Breakpoint.*at.* file .*$srcfile, line.*" \
+	"break test_ldrsb_literal"
+    gdb_test "break *test_ldrsh_literal" \
+	"Breakpoint.*at.* file .*$srcfile, line.*" \
+	"break test_ldrsh_literal"
+    gdb_test "break *test_ldr_literal_end" \
+	"Breakpoint.*at.* file .*$srcfile, line.*" \
+	"break test_test_ldr_literal_end"
+
+    gdb_continue_to_breakpoint "continue to test_ldr_literal" \
+	".*ldrh.*r0\,.*\[pc\].*"
+    gdb_continue_to_breakpoint "continue to test_ldrsb_literal" \
+	".*ldrsb.*r0\,.*\[pc\].*"
+    gdb_continue_to_breakpoint "continue to test_ldrsh_literal" \
+	".*ldrsh.*r0\,.*\[pc\].*"
+    gdb_continue_to_breakpoint "continue to test_ldr_literal_ret" \
+	".*bx lr.*"
+
+    gdb_test_multiple "break test_ldr_literal_16" "break test_ldr_literal_16" {
+	-re "Breakpoint.*at.* file .*$srcfile, line.*" {
+	    pass "break test_ldr_literal"
+	}
+	-re "Function \"test_ldr_literal_16\" not defined\..*Make breakpoint pending on future shared library load.*y or .n.. $" {
+	    gdb_test "n" "" "skip"
+	    return 0
+	}
+    }
+    
+    gdb_test "break *test_ldr_literal_16_end" \
+	"Breakpoint.*at.* file .*$srcfile, line.*" \
+	"break test_test_ldr_literal_16_end"
+
+    gdb_continue_to_breakpoint "continue to test_ldr_literal" \
+	".*ldr.*r0\,.*L2.*"
+    gdb_continue_to_breakpoint "continue to test_ldrsb_literal" \
+	".*bx lr.*"
+}
+
 ##########################################
 # Test call/ret.
 proc test_call_ret {} {
     global srcfile
+    global testfile
+
+    gdb_test "break *test_call" \
+	"Breakpoint.*at.* file .*$srcfile, line.*" \
+	"break test_call"
+
     gdb_test "break *test_call_end" \
 	"Breakpoint.*at.* file .*$srcfile, line.*" \
 	"break test_call_end"
@@ -82,8 +167,10 @@ proc test_call_ret {} {
 	"Breakpoint.*at.* file .*$srcfile, line.*" \
 	"break test_ret_end"
 
-    gdb_continue_to_breakpoint "continue to test_call_end" \
-	".*@ Location test_call_end.*"
+    try_continue_with_displaced_step "test_call" "bl test_call_subr"
+    try_continue_with_displaced_step "test_call_end" \
+	"@ Location test_call_end"
+
     gdb_continue_to_breakpoint "continue to test_ret" \
 	".*bx lr.*"
     gdb_continue_to_breakpoint "continue to test_ret_end" \
@@ -122,7 +209,66 @@ proc test_ldr_from_pc {} {
 
     gdb_continue_to_breakpoint "continue to test_ldr_pc" \
 	".*ldr.*r1\,.*\[pc, #0\].*"
-    gdb_continue_to_breakpoint "continue to Lbranch" \
+    gdb_continue_to_breakpoint "continue to test_ldr_pc_ret" \
+	".*bx lr.*"
+}
+
+#########################################
+
+# Test cbz and cbnz
+proc test_cbz_cbnz {} {
+    global srcfile
+
+    gdb_test_multiple "break *test_zero_cbnz" "break test_zero_cbnz" {
+	-re "Breakpoint.*at.* file .*$srcfile, line.*" {
+	    pass "break test_ldr_literal"
+	}
+	-re "No symbol.*" {
+	    return 0
+	}
+    }
+
+    gdb_test "break *test_zero_cbz" \
+	"Breakpoint.*at.* file .*$srcfile, line.*" \
+	"break test_zero_cbz"
+    gdb_test "break *test_non_zero_cbnz" \
+	"Breakpoint.*at.* file .*$srcfile, line.*" \
+	"break test_non_zero_cbnz"
+    gdb_test "break *test_non_zero_cbz" \
+	"Breakpoint.*at.* file .*$srcfile, line.*" \
+	"break test_non_zero_cbz"
+
+    gdb_continue_to_breakpoint "continue to test_zero_cbnz" \
+	".*cbnz.*r0\,.*\.L3.*"
+    gdb_continue_to_breakpoint "continue to test_zero_cbz" \
+	".*cbz.*r0\,.*\.L3.*"
+    gdb_continue_to_breakpoint "continue to test_non_zero_cbz" \
+	".*cbz.*r0\,.*\.L4.*"
+    gdb_continue_to_breakpoint "continue to test_non_zero_cbnz" \
+	".*cbnz.*r0\,.*\.L4.*"
+}
+
+# Test adr
+
+proc test_adr {} {
+    global srcfile
+
+    gdb_test_multiple "break *test_adr" "break test_adr" {
+	-re "Breakpoint.*at.* file .*$srcfile, line.*" {
+	    pass "break test_adr"
+	}
+	-re "No symbol.*" {
+	    return 0
+	}
+    }
+
+    gdb_test "break *test_adr_end" \
+	"Breakpoint.*at.* file .*$srcfile, line.*" \
+	"break test_adr_end"
+
+    gdb_continue_to_breakpoint "continue to test_adr" \
+	".*adr.*r0\,.*#1.*"
+    gdb_continue_to_breakpoint "continue to test_adr_end" \
 	".*bx lr.*"
 }
 
@@ -143,20 +289,6 @@ if ![runto_main] then {
 gdb_test_no_output "set displaced-stepping on"
 gdb_test "show displaced-stepping" ".* displaced stepping .* is on.*"
 
-gdb_test "break *test_call" \
-	"Breakpoint.*at.* file .*$srcfile, line.*" \
-	"break test_call"
-
-gdb_test_multiple "continue" "continue to test_call" {
-	-re ".*bl test_call_subr.*" {
-	    pass "continue to test_call"
-	}
-	-re "Displaced stepping is only supported in" {
-	    kfail "gdb/NNNN" $testfile
-	    return
-	}
-    }
-
 test_call_ret
 
 test_branch
@@ -165,6 +297,11 @@ test_ldr_from_pc
 
 test_ldm_stm_pc
 
+test_ldr_literal
+
+test_cbz_cbnz
+
+test_adr
 ##########################################
 
 # Done, run program to exit.

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