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

Yao Qi
2011-05-11T13:06:47Z

[-- Attachment #1: Type: text/plain, Size: 3048 bytes --]

On 05/10/2011 09:58 PM, Ulrich Weigand wrote:
>> > +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]
> It seems this approach is actually incorrect. If you do a
> POP {rX, rY, ..., rZ, PC}
> the value at the SP gets restored into rX, the value at rX+4
> into rY and so on, and the value at the highest address gets
> restored into PC.
>
> With your replacement sequence, it would appear that you
> instead get the value at the *lowest* address (just SP)
> restored into the PC ...
>

Hmmm, you are right. In test case, I put the same address on the first
two slots on stack, and pop them to r1 and pc respectively. That is the
reason why this bug is not found by test case.

In my new patch, there are three different cases to handle POP instruction,
1. register list is full, no free register. The code sequence I am
using is like

POP {r0, r1, ...., r6};
POP {r7};
MOV r8, r7;
POP {r7};

after execution of this sequence, PC's value is stored in r7, and r7's
value is stored in r8. In cleanup, we can set PC, r7, and r8 accordingly.

2. register list is not full, and not empty. In this case, we scan the
code to find a free register, rN. Run the follow code sequence,

POP {rX, rY, ...., rZ};
POP {rN};

After execution of this sequence, PC's value is stored in rN. In
cleanup, we can set PC from rN.

3. register list is empty. This case is relative simple.

POP {r0}

In cleanup, we store r0's value to PC.

The testcase is update according to these three cases, and value of PC
after each POP instruction is checked. I'll resend test case patch later.

>
> Apart from this, just a few minor issues:
>
>> > +/* Common copy routine for svc instruciton. */
>> >
>> > +static int
>> > +copy_svc (struct gdbarch *gdbarch, struct regcache *regs,
>> > + struct displaced_step_closure *dsc)
>> > +{
> I guess to keep in sync with terminology in the rest of the file,
> this really should be called install_svc ...
>

Well, the function `copy_svc' is a little bit different from other
install_* routines. Anyway, I am OK to name it to install_svc. Fixed.

>> > + err = thumb_copy_unmodified_16bit (gdbarch, insn1,"shift/add/sub/mov/cmp",
> Formatting: space after the comma (here and at a couple other places).
>

Fixed.

>> > + thumb_process_displaced_32bit_insn(gdbarch, insn1, insn2, regs, dsc);
>> > + }
>> > + else
>> > + thumb_process_displaced_16bit_insn(gdbarch, insn1, regs, dsc);
> Formatting: space before (

Fixed.

--
Yao (é½å°§)

[-- Attachment #2: 0001-Support-displaced-stepping-for-Thumb-16-bit-insns.patch --]
[-- Type: text/x-patch, Size: 18214 bytes --]

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 ...
(install_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 | 524 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 512 insertions(+), 12 deletions(-)

diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 2dd8c9e..1421168 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) /* Encoding T2 */
+ {
+ offset = sbits ((insn << 1), 0, 11);
+ 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
+install_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 install_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 install_svc (gdbarch, regs, dsc);
}

/* Copy undefined instructions. */
@@ -6929,11 +7052,388 @@ 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)
+{
+
+ if (dsc->u.block.regmask == 0xff)
+ {
+ /* PC <- r7 */
+ int val = displaced_read_reg (regs, dsc, 7);
+ displaced_write_reg (regs, dsc, ARM_PC_REGNUM, val, BX_WRITE_PC);
+
+ /* r7 <- r8 */
+ val = displaced_read_reg (regs, dsc, 8);
+ displaced_write_reg (regs, dsc, 7, val, CANNOT_WRITE_PC);
+
+ /* r8 <- tmp[0] */
+ displaced_write_reg (regs, dsc, 8, dsc->tmp[0], CANNOT_WRITE_PC);
+ }
+ else /* Cleanup procedure of case #2 and case #3 can be unified. */
+ {
+ int rx = 0;
+ int rx_val = 0;
+
+ if (dsc->u.block.regmask)
+ {
+ for (rx = 0; rx < 8; rx++)
+ if ((dsc->u.block.regmask & (1 << rx)) == 0)
+ break;
+ }
+ else
+ rx = 0;
+
+ 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 full, that is, r0-r7 are used.
+ Prepare: tmp[0] <- r8
+
+ POP {r0, r1, ...., r6}; remove PC and r7 from reglist
+ POP {r7};
+ MOV r8, r7; Move value of r7 to r8;
+ POP {r7}; Store PC value into r7.
+
+ Cleanup: PC <- r7, r7 <- r8, r8 <-tmp[0]
+
+ (2) register list is not empty, but no full,
+ Prepare: tmp[0] <- rN, rN is the first free register.
+
+ POP {rX, rY, ...., rZ}; remove PC from reglist.
+ POP {rN}; PC is stored in rN
+
+ Cleanup: PC <-rN, rN <- tmp[0]
+
+ (3) 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 == 0xff)
+ {
+ dsc->tmp[0] = displaced_read_reg (regs, dsc, 8);
+
+ dsc->modinsn[0] = (insn1 & 0xfe7f); /* POP {r0,r1,...,r6} */
+ dsc->modinsn[1] = 0xbc80; /* POP {r7} */
+ dsc->modinsn[2] = 0x46b8; /* MOV r8, r7 */
+ dsc->modinsn[3] = 0xbc80; /* POP {r7} */
+
+ dsc->numinsns = 4;
+ }
+ else if (dsc->u.block.regmask != 0)
+ {
+ int rn = 0;
+
+ /* Look for the first register not in register list. */
+ for (rn = 0; rn < 8; rn++)
+ if ((dsc->u.block.regmask & (1 << rn)) == 0)
+ break;
+ dsc->tmp[0] = displaced_read_reg (regs, dsc, rn);
+
+ dsc->modinsn[0] = (insn1 & 0xfeff); /* POP {rX, rY, ..., rZ} */
+ dsc->modinsn[1] = (0xbc00 | (1 << rn)); /* POP {rN} */
+
+ dsc->numinsns = 2;
+ }
+ else
+ {
+ dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
+
+ dsc->modinsn[0] = 0xbc01; /* POP {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