--- .pc/arm-displaced-stepping/gdb/arm-linux-tdep.c 2009-01-20 13:22:42.000000000 -0800 +++ gdb/arm-linux-tdep.c 2009-01-20 13:23:59.000000000 -0800 @@ -37,6 +37,7 @@ #include "arm-tdep.h" #include "arm-linux-tdep.h" #include "glibc-tdep.h" +#include "arch-utils.h" #include "gdb_string.h" @@ -647,6 +648,14 @@ arm_linux_init_abi (struct gdbarch_info /* Core file support. */ set_gdbarch_regset_from_core_section (gdbarch, arm_linux_regset_from_core_section); + + /* Displaced stepping. */ + set_gdbarch_displaced_step_copy_insn (gdbarch, + arm_displaced_step_copy_insn); + set_gdbarch_displaced_step_fixup (gdbarch, arm_displaced_step_fixup); + set_gdbarch_displaced_step_free_closure (gdbarch, + simple_displaced_step_free_closure); + set_gdbarch_displaced_step_location (gdbarch, displaced_step_at_entry_point); } void --- .pc/arm-displaced-stepping/gdb/arm-tdep.c 2009-01-20 13:22:42.000000000 -0800 +++ gdb/arm-tdep.c 2009-01-20 13:33:10.000000000 -0800 @@ -2175,6 +2175,1456 @@ arm_software_single_step (struct frame_i return 1; } +/* Displaced stepping support. */ + +/* The maximum number of temporaries available for displaced instructions. */ +#define DISPLACED_TEMPS 5 +/* The maximum number of modified instructions generated for one single-stepped + instruction. */ +#define DISPLACED_MODIFIED_INSNS 5 + +struct displaced_step_closure +{ + ULONGEST tmp[DISPLACED_TEMPS]; + int rd; + int wrote_to_pc; + union + { + struct + { + int xfersize; + int rn; /* Writeback register. */ + unsigned int immed : 1; /* Offset is immediate. */ + unsigned int writeback : 1; /* Perform base-register writeback. */ + } ldst; + + struct + { + unsigned long dest; + unsigned int link : 1; + unsigned int exchange : 1; + } branch; + + struct + { + unsigned int regmask; + int rn; + CORE_ADDR xfer_addr; + unsigned int load : 1; + unsigned int user : 1; + unsigned int increment : 1; + unsigned int before : 1; + unsigned int writeback : 1; + } block; + + struct + { + unsigned int immed : 1; + } preload; + } u; + unsigned long modinsn[DISPLACED_MODIFIED_INSNS]; + int numinsns; + CORE_ADDR insn_addr; + void (*cleanup) (struct regcache *, struct displaced_step_closure *); +}; + +static void cleanup_branch (struct regcache *, struct displaced_step_closure *); +static void cleanup_dp_imm (struct regcache *, struct displaced_step_closure *); +static void cleanup_dp_reg (struct regcache *, struct displaced_step_closure *); +static void cleanup_dp_shifted_reg (struct regcache *, + struct displaced_step_closure *); +static void cleanup_load (struct regcache *, struct displaced_step_closure *); +static void cleanup_store (struct regcache *, struct displaced_step_closure *); +static void cleanup_block_xfer (struct regcache *, + struct displaced_step_closure *); +static void cleanup_svc (struct regcache *, struct displaced_step_closure *); +static void cleanup_kernel_helper_return (struct regcache *, + struct displaced_step_closure *); +static void cleanup_preload (struct regcache *, + struct displaced_step_closure *); +static void cleanup_copro_load_store (struct regcache *, + struct displaced_step_closure *); + +ULONGEST +displaced_read_reg (struct regcache *regs, CORE_ADDR from, int regno) +{ + ULONGEST ret; + + if (regno == 15) + { + if (debug_displaced) + fprintf_unfiltered (gdb_stdlog, "displaced: read pc value %.8lx\n", + (unsigned long) from + 8); + return (ULONGEST) from + 8; /* Pipeline offset. */ + } + else + { + regcache_cooked_read_unsigned (regs, regno, &ret); + if (debug_displaced) + fprintf_unfiltered (gdb_stdlog, "displaced: read r%d value %.8lx\n", + regno, (unsigned long) ret); + return ret; + } +} + +static void arm_write_pc (struct regcache *, CORE_ADDR); + +void +displaced_write_reg (struct regcache *regs, struct displaced_step_closure *dsc, + int regno, ULONGEST val) +{ + if (regno == 15) + { + if (debug_displaced) + fprintf_unfiltered (gdb_stdlog, "displaced: writing pc %.8lx\n", + (unsigned long) val); + arm_write_pc (regs, val); + dsc->wrote_to_pc = 1; + } + else + { + if (debug_displaced) + fprintf_unfiltered (gdb_stdlog, "displaced: writing r%d value %.8lx\n", + regno, (unsigned long) val); + regcache_cooked_write_unsigned (regs, regno, val); + } +} + +static int +copy_unmodified (unsigned long insn, const char *iname, + struct displaced_step_closure *dsc) +{ + if (debug_displaced) + fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.8lx, " + "opcode/class '%s'\n", insn, iname); + + dsc->modinsn[0] = insn; + + return 0; +} + +static int +copy_preload (unsigned long insn, struct regcache *regs, + struct displaced_step_closure *dsc) +{ + unsigned int rn = bits (insn, 16, 19); + ULONGEST rn_val; + CORE_ADDR from = dsc->insn_addr; + + if (debug_displaced) + fprintf_unfiltered (gdb_stdlog, "displaced: copying preload insn %.8lx\n", + insn); + + /* Preload instructions: + + {pli/pld} [rn, #+/-imm] + -> + {pli/pld} [r0, #+/-imm]. */ + + dsc->tmp[0] = displaced_read_reg (regs, from, 0); + rn_val = displaced_read_reg (regs, from, rn); + displaced_write_reg (regs, dsc, 0, rn_val); + + dsc->u.preload.immed = 1; + + dsc->modinsn[0] = insn & 0xfff0ffff; + + dsc->cleanup = &cleanup_preload; + + return 0; +} + +static int +copy_preload_reg (unsigned long insn, struct regcache *regs, + struct displaced_step_closure *dsc) +{ + unsigned int rn = bits (insn, 16, 19); + unsigned int rm = bits (insn, 0, 3); + ULONGEST rn_val, rm_val; + CORE_ADDR from = dsc->insn_addr; + + if (debug_displaced) + fprintf_unfiltered (gdb_stdlog, "displaced: copying preload insn %.8lx\n", + insn); + + /* Preload register-offset instructions: + + {pli/pld} [rn, rm {, shift}] + -> + {pli/pld} [r0, r1 {, shift}]. */ + + dsc->tmp[0] = displaced_read_reg (regs, from, 0); + dsc->tmp[1] = displaced_read_reg (regs, from, 1); + rn_val = displaced_read_reg (regs, from, rn); + rm_val = displaced_read_reg (regs, from, rm); + displaced_write_reg (regs, dsc, 0, rn_val); + displaced_write_reg (regs, dsc, 1, rm_val); + + dsc->u.preload.immed = 0; + + dsc->modinsn[0] = (insn & 0xfff0fff0) | 0x1; + + dsc->cleanup = &cleanup_preload; + + return 0; +} + +static void +cleanup_preload (struct regcache *regs, struct displaced_step_closure *dsc) +{ + displaced_write_reg (regs, dsc, 0, dsc->tmp[0]); + if (!dsc->u.preload.immed) + displaced_write_reg (regs, dsc, 1, dsc->tmp[1]); +} + +static int +copy_copro_load_store (unsigned long insn, struct regcache *regs, + struct displaced_step_closure *dsc) +{ + unsigned int rn = bits (insn, 16, 19); + ULONGEST rn_val; + CORE_ADDR from = dsc->insn_addr; + + if (debug_displaced) + fprintf_unfiltered (gdb_stdlog, "displaced: copying coprocessor " + "load/store insn %.8lx\n", insn); + + /* Coprocessor load/store instructions: + + {stc/stc2} [, #+/-imm] (and other immediate addressing modes) + -> + {stc/stc2} [r0, #+/-imm]. + + ldc/ldc2 are handled identically. */ + + dsc->tmp[0] = displaced_read_reg (regs, from, 0); + rn_val = displaced_read_reg (regs, from, rn); + displaced_write_reg (regs, dsc, 0, rn_val); + + dsc->u.ldst.writeback = bit (insn, 25); + dsc->u.ldst.rn = rn; + + dsc->modinsn[0] = insn & 0xfff0ffff; + + dsc->cleanup = &cleanup_copro_load_store; + + return 0; +} + +static void +cleanup_copro_load_store (struct regcache *regs, + struct displaced_step_closure *dsc) +{ + ULONGEST rn_val = displaced_read_reg (regs, dsc->insn_addr, 0); + + displaced_write_reg (regs, dsc, 0, dsc->tmp[0]); + + if (dsc->u.ldst.writeback) + displaced_write_reg (regs, dsc, dsc->u.ldst.rn, rn_val); +} + +static int +copy_b_bl_blx (unsigned long 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); + 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", + insn); + + offset = bits (insn, 0, 23) << 2; + if (bit (offset, 25)) + offset = offset | ~0x3ffffff; + + /* Implement "BL