diff options
author | Daniel Jacobowitz <drow@false.org> | 2009-10-13 22:48:45 +0000 |
---|---|---|
committer | Daniel Jacobowitz <drow@false.org> | 2009-10-13 22:48:45 +0000 |
commit | 9dca5578310237d7f0c9c399179435501748bda8 (patch) | |
tree | c63a1d356fcbe0c8027d872dc9f1e0fd96240471 /gdb/arm-tdep.c | |
parent | c658516291815dcfa19290644b366d8261c3fab9 (diff) | |
download | gdb-9dca5578310237d7f0c9c399179435501748bda8.zip gdb-9dca5578310237d7f0c9c399179435501748bda8.tar.gz gdb-9dca5578310237d7f0c9c399179435501748bda8.tar.bz2 |
* arm-tdep.c (arm_push_dummy_call): Set the low bit of LR for
a Thumb entry point.
(thumb_get_next_pc): Handle Thumb-2 and ARM v6 instructions. Refuse
to single step into IT blocks.
Diffstat (limited to 'gdb/arm-tdep.c')
-rw-r--r-- | gdb/arm-tdep.c | 212 |
1 files changed, 204 insertions, 8 deletions
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c index d59e2aa..21d65f3 100644 --- a/gdb/arm-tdep.c +++ b/gdb/arm-tdep.c @@ -1627,7 +1627,8 @@ arm_push_dummy_call (struct gdbarch *gdbarch, struct value *function, /* Set the return address. For the ARM, the return breakpoint is always at BP_ADDR. */ - /* XXX Fix for Thumb. */ + if (arm_pc_is_thumb (bp_addr)) + bp_addr |= 1; regcache_cooked_write_unsigned (regcache, ARM_LR_REGNUM, bp_addr); /* Walk through the list of args and determine how large a temporary @@ -2265,9 +2266,43 @@ thumb_get_next_pc (struct frame_info *frame, CORE_ADDR pc) unsigned short inst1; CORE_ADDR nextpc = pc + 2; /* default is next instruction */ unsigned long offset; + ULONGEST status, it; inst1 = read_memory_unsigned_integer (pc, 2, byte_order_for_code); + /* Thumb-2 conditional execution support. There are eight bits in + the CPSR which describe conditional execution state. Once + reconstructed (they're in a funny order), the low five bits + describe the low bit of the condition for each instruction and + how many instructions remain. The high three bits describe the + base condition. One of the low four bits will be set if an IT + block is active. These bits read as zero on earlier + processors. */ + status = get_frame_register_unsigned (frame, ARM_PS_REGNUM); + it = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3); + + /* On GNU/Linux, where this routine is used, we use an undefined + instruction as a breakpoint. Unlike BKPT, IT can disable execution + of the undefined instruction. So we might miss the breakpoint! */ + if ((inst1 & 0xff00) == 0xbf00 || (it & 0x0f)) + error (_("Stepping through Thumb-2 IT blocks is not yet supported")); + + if (it & 0x0f) + { + /* We are in a conditional block. Check the condition. */ + int cond = it >> 4; + + if (! condition_true (cond, status)) + { + /* Advance to the next instruction. All the 32-bit + instructions share a common prefix. */ + if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0) + return pc + 4; + else + return pc + 2; + } + } + if ((inst1 & 0xff00) == 0xbd00) /* pop {rlist, pc} */ { CORE_ADDR sp; @@ -2283,7 +2318,6 @@ thumb_get_next_pc (struct frame_info *frame, CORE_ADDR pc) } else if ((inst1 & 0xf000) == 0xd000) /* conditional branch */ { - unsigned long status = get_frame_register_unsigned (frame, ARM_PS_REGNUM); unsigned long cond = bits (inst1, 8, 11); if (cond != 0x0f && condition_true (cond, status)) /* 0x0f = SWI */ nextpc = pc_val + (sbits (inst1, 0, 7) << 1); @@ -2292,15 +2326,166 @@ thumb_get_next_pc (struct frame_info *frame, CORE_ADDR pc) { nextpc = pc_val + (sbits (inst1, 0, 10) << 1); } - else if ((inst1 & 0xf800) == 0xf000) /* long branch with link, and blx */ + else if ((inst1 & 0xe000) == 0xe000) /* 32-bit instruction */ { unsigned short inst2; inst2 = read_memory_unsigned_integer (pc + 2, 2, byte_order_for_code); - offset = (sbits (inst1, 0, 10) << 12) + (bits (inst2, 0, 10) << 1); - nextpc = pc_val + offset; - /* For BLX make sure to clear the low bits. */ - if (bits (inst2, 11, 12) == 1) - nextpc = nextpc & 0xfffffffc; + + /* Default to the next instruction. */ + nextpc = pc + 4; + + if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000) + { + /* Branches and miscellaneous control instructions. */ + + if ((inst2 & 0x1000) != 0 || (inst2 & 0xd001) == 0xc000) + { + /* B, BL, BLX. */ + int j1, j2, imm1, imm2; + + imm1 = sbits (inst1, 0, 10); + imm2 = bits (inst2, 0, 10); + j1 = bit (inst2, 13); + j2 = bit (inst2, 11); + + offset = ((imm1 << 12) + (imm2 << 1)); + offset ^= ((!j2) << 22) | ((!j1) << 23); + + nextpc = pc_val + offset; + /* For BLX make sure to clear the low bits. */ + if (bit (inst2, 12) == 0) + nextpc = nextpc & 0xfffffffc; + } + else if (inst1 == 0xf3de && (inst2 & 0xff00) == 0x3f00) + { + /* SUBS PC, LR, #imm8. */ + nextpc = get_frame_register_unsigned (frame, ARM_LR_REGNUM); + nextpc -= inst2 & 0x00ff; + } + else if ((inst2 & 0xd000) == 0xc000 && (inst1 & 0x0380) != 0x0380) + { + /* Conditional branch. */ + if (condition_true (bits (inst1, 6, 9), status)) + { + int sign, j1, j2, imm1, imm2; + + sign = sbits (inst1, 10, 10); + imm1 = bits (inst1, 0, 5); + imm2 = bits (inst2, 0, 10); + j1 = bit (inst2, 13); + j2 = bit (inst2, 11); + + offset = (sign << 20) + (j2 << 19) + (j1 << 18); + offset += (imm1 << 12) + (imm2 << 1); + + nextpc = pc_val + offset; + } + } + } + else if ((inst1 & 0xfe50) == 0xe810) + { + /* Load multiple or RFE. */ + int rn, offset, load_pc = 1; + + rn = bits (inst1, 0, 3); + if (bit (inst1, 7) && !bit (inst1, 8)) + { + /* LDMIA or POP */ + if (!bit (inst2, 15)) + load_pc = 0; + offset = bitcount (inst2) * 4 - 4; + } + else if (!bit (inst1, 7) && bit (inst1, 8)) + { + /* LDMDB */ + if (!bit (inst2, 15)) + load_pc = 0; + offset = -4; + } + else if (bit (inst1, 7) && bit (inst1, 8)) + { + /* RFEIA */ + offset = 0; + } + else if (!bit (inst1, 7) && !bit (inst1, 8)) + { + /* RFEDB */ + offset = -8; + } + else + load_pc = 0; + + if (load_pc) + { + CORE_ADDR addr = get_frame_register_unsigned (frame, rn); + nextpc = get_frame_memory_unsigned (frame, addr + offset, 4); + } + } + else if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00) + { + /* MOV PC or MOVS PC. */ + nextpc = get_frame_register_unsigned (frame, bits (inst2, 0, 3)); + } + else if ((inst1 & 0xff70) == 0xf850 && (inst2 & 0xf000) == 0xf000) + { + /* LDR PC. */ + CORE_ADDR base; + int rn, load_pc = 1; + + rn = bits (inst1, 0, 3); + base = get_frame_register_unsigned (frame, rn); + if (rn == 15) + { + base = (base + 4) & ~(CORE_ADDR) 0x3; + if (bit (inst1, 7)) + base += bits (inst2, 0, 11); + else + base -= bits (inst2, 0, 11); + } + else if (bit (inst1, 7)) + base += bits (inst2, 0, 11); + else if (bit (inst2, 11)) + { + if (bit (inst2, 10)) + { + if (bit (inst2, 9)) + base += bits (inst2, 0, 7); + else + base -= bits (inst2, 0, 7); + } + } + else if ((inst2 & 0x0fc0) == 0x0000) + { + int shift = bits (inst2, 4, 5), rm = bits (inst2, 0, 3); + base += get_frame_register_unsigned (frame, rm) << shift; + } + else + /* Reserved. */ + load_pc = 0; + + if (load_pc) + nextpc = get_frame_memory_unsigned (frame, base, 4); + } + else if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf000) + { + /* TBB. */ + CORE_ADDR table, offset, length; + + table = get_frame_register_unsigned (frame, bits (inst1, 0, 3)); + offset = get_frame_register_unsigned (frame, bits (inst2, 0, 3)); + length = 2 * get_frame_memory_unsigned (frame, table + offset, 1); + nextpc = pc_val + length; + } + else if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf000) + { + /* TBH. */ + CORE_ADDR table, offset, length; + + table = get_frame_register_unsigned (frame, bits (inst1, 0, 3)); + offset = 2 * get_frame_register_unsigned (frame, bits (inst2, 0, 3)); + length = 2 * get_frame_memory_unsigned (frame, table + offset, 2); + nextpc = pc_val + length; + } } else if ((inst1 & 0xff00) == 0x4700) /* bx REG, blx REG */ { @@ -2313,6 +2498,17 @@ thumb_get_next_pc (struct frame_info *frame, CORE_ADDR pc) if (nextpc == pc) error (_("Infinite loop detected")); } + else if ((inst1 & 0xf500) == 0xb100) + { + /* CBNZ or CBZ. */ + int imm = (bit (inst1, 9) << 6) + (bits (inst1, 3, 7) << 1); + ULONGEST reg = get_frame_register_unsigned (frame, bits (inst1, 0, 2)); + + if (bit (inst1, 11) && reg != 0) + nextpc = pc_val + imm; + else if (!bit (inst1, 11) && reg == 0) + nextpc = pc_val + imm; + } return nextpc; } |