gdb/ 2010-12-25 Yao Qi 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 * 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