diff options
Diffstat (limited to 'gdb/arm-tdep.c')
-rw-r--r-- | gdb/arm-tdep.c | 1327 |
1 files changed, 101 insertions, 1226 deletions
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c index ebe4c40..52ce8d5 100644 --- a/gdb/arm-tdep.c +++ b/gdb/arm-tdep.c @@ -46,6 +46,7 @@ #include "observer.h" #include "arch/arm.h" +#include "arch/arm-get-next-pcs.h" #include "arm-tdep.h" #include "gdb/sim-arm.h" @@ -236,6 +237,14 @@ static void arm_neon_quad_write (struct gdbarch *gdbarch, struct regcache *regcache, int regnum, const gdb_byte *buf); +/* get_next_pcs operations. */ +static struct arm_get_next_pcs_ops arm_get_next_pcs_ops = { + arm_get_next_pcs_read_memory_unsigned_integer, + arm_get_next_pcs_syscall_next_pc, + arm_get_next_pcs_addr_bits_remove, + arm_get_next_pcs_is_thumb +}; + struct arm_prologue_cache { /* The stack pointer at the time this frame was created; i.e. the @@ -520,15 +529,6 @@ skip_prologue_function (struct gdbarch *gdbarch, CORE_ADDR pc, int is_thumb) return 0; } -/* Support routines for instruction parsing. */ -#define submask(x) ((1L << ((x) + 1)) - 1) -#define bit(obj,st) (((obj) >> (st)) & 1) -#define bits(obj,st,fn) (((obj) >> (st)) & submask ((fn) - (st))) -#define sbits(obj,st,fn) \ - ((long) (bits(obj,st,fn) | ((long) bit(obj,fn) * ~ submask (fn - st)))) -#define BranchDest(addr,instr) \ - ((CORE_ADDR) (((unsigned long) (addr)) + 8 + (sbits (instr, 0, 23) << 2))) - /* Extract the immediate from instruction movw/movt of encoding T. INSN1 is the first 16-bit of instruction, and INSN2 is the second 16-bit of instruction. */ @@ -568,128 +568,6 @@ thumb_expand_immediate (unsigned int imm) return (0x80 | (imm & 0x7f)) << (32 - count); } -/* Return 1 if the 16-bit Thumb instruction INST might change - control flow, 0 otherwise. */ - -static int -thumb_instruction_changes_pc (unsigned short inst) -{ - if ((inst & 0xff00) == 0xbd00) /* pop {rlist, pc} */ - return 1; - - if ((inst & 0xf000) == 0xd000) /* conditional branch */ - return 1; - - if ((inst & 0xf800) == 0xe000) /* unconditional branch */ - return 1; - - if ((inst & 0xff00) == 0x4700) /* bx REG, blx REG */ - return 1; - - if ((inst & 0xff87) == 0x4687) /* mov pc, REG */ - return 1; - - if ((inst & 0xf500) == 0xb100) /* CBNZ or CBZ. */ - return 1; - - return 0; -} - -/* Return 1 if the 32-bit Thumb instruction in INST1 and INST2 - might change control flow, 0 otherwise. */ - -static int -thumb2_instruction_changes_pc (unsigned short inst1, unsigned short inst2) -{ - if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000) - { - /* Branches and miscellaneous control instructions. */ - - if ((inst2 & 0x1000) != 0 || (inst2 & 0xd001) == 0xc000) - { - /* B, BL, BLX. */ - return 1; - } - else if (inst1 == 0xf3de && (inst2 & 0xff00) == 0x3f00) - { - /* SUBS PC, LR, #imm8. */ - return 1; - } - else if ((inst2 & 0xd000) == 0x8000 && (inst1 & 0x0380) != 0x0380) - { - /* Conditional branch. */ - return 1; - } - - return 0; - } - - if ((inst1 & 0xfe50) == 0xe810) - { - /* Load multiple or RFE. */ - - if (bit (inst1, 7) && !bit (inst1, 8)) - { - /* LDMIA or POP */ - if (bit (inst2, 15)) - return 1; - } - else if (!bit (inst1, 7) && bit (inst1, 8)) - { - /* LDMDB */ - if (bit (inst2, 15)) - return 1; - } - else if (bit (inst1, 7) && bit (inst1, 8)) - { - /* RFEIA */ - return 1; - } - else if (!bit (inst1, 7) && !bit (inst1, 8)) - { - /* RFEDB */ - return 1; - } - - return 0; - } - - if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00) - { - /* MOV PC or MOVS PC. */ - return 1; - } - - if ((inst1 & 0xff70) == 0xf850 && (inst2 & 0xf000) == 0xf000) - { - /* LDR PC. */ - if (bits (inst1, 0, 3) == 15) - return 1; - if (bit (inst1, 7)) - return 1; - if (bit (inst2, 11)) - return 1; - if ((inst2 & 0x0fc0) == 0x0000) - return 1; - - return 0; - } - - if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf000) - { - /* TBB. */ - return 1; - } - - if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf010) - { - /* TBH. */ - return 1; - } - - return 0; -} - /* Return 1 if the 16-bit Thumb instruction INSN restores SP in epilogue, 0 otherwise. */ @@ -1517,98 +1395,6 @@ thumb_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR prev_pc, thumb_analyze_prologue (gdbarch, prologue_start, prologue_end, cache); } -/* Return 1 if THIS_INSTR might change control flow, 0 otherwise. */ - -static int -arm_instruction_changes_pc (uint32_t this_instr) -{ - if (bits (this_instr, 28, 31) == INST_NV) - /* Unconditional instructions. */ - switch (bits (this_instr, 24, 27)) - { - case 0xa: - case 0xb: - /* Branch with Link and change to Thumb. */ - return 1; - case 0xc: - case 0xd: - case 0xe: - /* Coprocessor register transfer. */ - if (bits (this_instr, 12, 15) == 15) - error (_("Invalid update to pc in instruction")); - return 0; - default: - return 0; - } - else - switch (bits (this_instr, 25, 27)) - { - case 0x0: - if (bits (this_instr, 23, 24) == 2 && bit (this_instr, 20) == 0) - { - /* Multiplies and extra load/stores. */ - if (bit (this_instr, 4) == 1 && bit (this_instr, 7) == 1) - /* Neither multiplies nor extension load/stores are allowed - to modify PC. */ - return 0; - - /* Otherwise, miscellaneous instructions. */ - - /* BX <reg>, BXJ <reg>, BLX <reg> */ - if (bits (this_instr, 4, 27) == 0x12fff1 - || bits (this_instr, 4, 27) == 0x12fff2 - || bits (this_instr, 4, 27) == 0x12fff3) - return 1; - - /* Other miscellaneous instructions are unpredictable if they - modify PC. */ - return 0; - } - /* Data processing instruction. Fall through. */ - - case 0x1: - if (bits (this_instr, 12, 15) == 15) - return 1; - else - return 0; - - case 0x2: - case 0x3: - /* Media instructions and architecturally undefined instructions. */ - if (bits (this_instr, 25, 27) == 3 && bit (this_instr, 4) == 1) - return 0; - - /* Stores. */ - if (bit (this_instr, 20) == 0) - return 0; - - /* Loads. */ - if (bits (this_instr, 12, 15) == ARM_PC_REGNUM) - return 1; - else - return 0; - - case 0x4: - /* Load/store multiple. */ - if (bit (this_instr, 20) == 1 && bit (this_instr, 15) == 1) - return 1; - else - return 0; - - case 0x5: - /* Branch and branch with link. */ - return 1; - - case 0x6: - case 0x7: - /* Coprocessor transfers or SWIs can not affect PC. */ - return 0; - - default: - internal_error (__FILE__, __LINE__, _("bad value in switch")); - } -} - /* Return 1 if the ARM instruction INSN restores SP in epilogue, 0 otherwise. */ @@ -2827,37 +2613,26 @@ arm_exidx_unwind_sniffer (const struct frame_unwind *self, /* We also assume exception information is valid if we're currently blocked in a system call. The system library is supposed to - ensure this, so that e.g. pthread cancellation works. - - But before verifying the instruction at the point of call, make - sure this_frame is actually making a call (or, said differently, - that it is not the innermost frame). For that, we compare - this_frame's PC vs this_frame's addr_in_block. If equal, it means - there is no call (otherwise, the PC would be the return address, - which is the instruction after the call). */ - - if (get_frame_pc (this_frame) != addr_in_block) + ensure this, so that e.g. pthread cancellation works. */ + if (arm_frame_is_thumb (this_frame)) { - if (arm_frame_is_thumb (this_frame)) - { - LONGEST insn; - - if (safe_read_memory_integer (get_frame_pc (this_frame) - 2, 2, - byte_order_for_code, &insn) - && (insn & 0xff00) == 0xdf00 /* svc */) - exc_valid = 1; - } - else - { - LONGEST insn; + LONGEST insn; - if (safe_read_memory_integer (get_frame_pc (this_frame) - 4, 4, - byte_order_for_code, &insn) - && (insn & 0x0f000000) == 0x0f000000 /* svc */) - exc_valid = 1; - } + if (safe_read_memory_integer (get_frame_pc (this_frame) - 2, 2, + byte_order_for_code, &insn) + && (insn & 0xff00) == 0xdf00 /* svc */) + exc_valid = 1; } + else + { + LONGEST insn; + if (safe_read_memory_integer (get_frame_pc (this_frame) - 4, 4, + byte_order_for_code, &insn) + && (insn & 0x0f000000) == 0x0f000000 /* svc */) + exc_valid = 1; + } + /* Bail out if we don't know that exception information is valid. */ if (!exc_valid) return 0; @@ -4314,740 +4089,6 @@ convert_to_extended (const struct floatformat *fmt, void *dbl, const void *ptr, &d, dbl); } -static unsigned long -shifted_reg_val (struct regcache *regcache, unsigned long inst, int carry, - unsigned long pc_val, unsigned long status_reg) -{ - unsigned long res, shift; - int rm = bits (inst, 0, 3); - unsigned long shifttype = bits (inst, 5, 6); - - if (bit (inst, 4)) - { - int rs = bits (inst, 8, 11); - shift = (rs == 15 ? pc_val + 8 - : regcache_raw_get_unsigned (regcache, rs)) & 0xFF; - } - else - shift = bits (inst, 7, 11); - - res = (rm == ARM_PC_REGNUM - ? (pc_val + (bit (inst, 4) ? 12 : 8)) - : regcache_raw_get_unsigned (regcache, rm)); - - switch (shifttype) - { - case 0: /* LSL */ - res = shift >= 32 ? 0 : res << shift; - break; - - case 1: /* LSR */ - res = shift >= 32 ? 0 : res >> shift; - break; - - case 2: /* ASR */ - if (shift >= 32) - shift = 31; - res = ((res & 0x80000000L) - ? ~((~res) >> shift) : res >> shift); - break; - - case 3: /* ROR/RRX */ - shift &= 31; - if (shift == 0) - res = (res >> 1) | (carry ? 0x80000000L : 0); - else - res = (res >> shift) | (res << (32 - shift)); - break; - } - - return res & 0xffffffff; -} - -static int -thumb_advance_itstate (unsigned int itstate) -{ - /* Preserve IT[7:5], the first three bits of the condition. Shift - the upcoming condition flags left by one bit. */ - itstate = (itstate & 0xe0) | ((itstate << 1) & 0x1f); - - /* If we have finished the IT block, clear the state. */ - if ((itstate & 0x0f) == 0) - itstate = 0; - - return itstate; -} - -/* Find the next PC after the current instruction executes. In some - cases we can not statically determine the answer (see the IT state - handling in this function); in that case, a breakpoint may be - inserted in addition to the returned PC, which will be used to set - another breakpoint by our caller. */ - -static CORE_ADDR -thumb_get_next_pc_raw (struct regcache *regcache, CORE_ADDR pc) -{ - struct gdbarch *gdbarch = get_regcache_arch (regcache); - struct address_space *aspace = get_regcache_aspace (regcache); - enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); - enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch); - unsigned long pc_val = ((unsigned long) pc) + 4; /* PC after prefetch */ - unsigned short inst1; - CORE_ADDR nextpc = pc + 2; /* Default is next instruction. */ - unsigned long offset; - ULONGEST status, itstate; - - nextpc = MAKE_THUMB_ADDR (nextpc); - pc_val = MAKE_THUMB_ADDR (pc_val); - - 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 = regcache_raw_get_unsigned (regcache, ARM_PS_REGNUM); - itstate = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3); - - /* If-Then handling. 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 we set it on a skipped conditional - instruction. Because conditional instructions can change the - flags, affecting the execution of further instructions, we may - need to set two breakpoints. */ - - if (gdbarch_tdep (gdbarch)->thumb2_breakpoint != NULL) - { - if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0) - { - /* An IT instruction. Because this instruction does not - modify the flags, we can accurately predict the next - executed instruction. */ - itstate = inst1 & 0x00ff; - pc += thumb_insn_size (inst1); - - while (itstate != 0 && ! condition_true (itstate >> 4, status)) - { - inst1 = read_memory_unsigned_integer (pc, 2, - byte_order_for_code); - pc += thumb_insn_size (inst1); - itstate = thumb_advance_itstate (itstate); - } - - return MAKE_THUMB_ADDR (pc); - } - else if (itstate != 0) - { - /* We are in a conditional block. Check the condition. */ - if (! condition_true (itstate >> 4, status)) - { - /* Advance to the next executed instruction. */ - pc += thumb_insn_size (inst1); - itstate = thumb_advance_itstate (itstate); - - while (itstate != 0 && ! condition_true (itstate >> 4, status)) - { - inst1 = read_memory_unsigned_integer (pc, 2, - byte_order_for_code); - pc += thumb_insn_size (inst1); - itstate = thumb_advance_itstate (itstate); - } - - return MAKE_THUMB_ADDR (pc); - } - else if ((itstate & 0x0f) == 0x08) - { - /* This is the last instruction of the conditional - block, and it is executed. We can handle it normally - because the following instruction is not conditional, - and we must handle it normally because it is - permitted to branch. Fall through. */ - } - else - { - int cond_negated; - - /* There are conditional instructions after this one. - If this instruction modifies the flags, then we can - not predict what the next executed instruction will - be. Fortunately, this instruction is architecturally - forbidden to branch; we know it will fall through. - Start by skipping past it. */ - pc += thumb_insn_size (inst1); - itstate = thumb_advance_itstate (itstate); - - /* Set a breakpoint on the following instruction. */ - gdb_assert ((itstate & 0x0f) != 0); - arm_insert_single_step_breakpoint (gdbarch, aspace, - MAKE_THUMB_ADDR (pc)); - cond_negated = (itstate >> 4) & 1; - - /* Skip all following instructions with the same - condition. If there is a later instruction in the IT - block with the opposite condition, set the other - breakpoint there. If not, then set a breakpoint on - the instruction after the IT block. */ - do - { - inst1 = read_memory_unsigned_integer (pc, 2, - byte_order_for_code); - pc += thumb_insn_size (inst1); - itstate = thumb_advance_itstate (itstate); - } - while (itstate != 0 && ((itstate >> 4) & 1) == cond_negated); - - return MAKE_THUMB_ADDR (pc); - } - } - } - else if (itstate & 0x0f) - { - /* We are in a conditional block. Check the condition. */ - int cond = itstate >> 4; - - if (! condition_true (cond, status)) - /* Advance to the next instruction. All the 32-bit - instructions share a common prefix. */ - return MAKE_THUMB_ADDR (pc + thumb_insn_size (inst1)); - - /* Otherwise, handle the instruction normally. */ - } - - if ((inst1 & 0xff00) == 0xbd00) /* pop {rlist, pc} */ - { - CORE_ADDR sp; - - /* Fetch the saved PC from the stack. It's stored above - all of the other registers. */ - offset = bitcount (bits (inst1, 0, 7)) * INT_REGISTER_SIZE; - sp = regcache_raw_get_unsigned (regcache, ARM_SP_REGNUM); - nextpc = read_memory_unsigned_integer (sp + offset, 4, byte_order); - } - else if ((inst1 & 0xf000) == 0xd000) /* conditional branch */ - { - unsigned long cond = bits (inst1, 8, 11); - if (cond == 0x0f) /* 0x0f = SWI */ - { - struct gdbarch_tdep *tdep; - tdep = gdbarch_tdep (gdbarch); - - if (tdep->syscall_next_pc != NULL) - nextpc = tdep->syscall_next_pc (regcache); - - } - else if (cond != 0x0f && condition_true (cond, status)) - nextpc = pc_val + (sbits (inst1, 0, 7) << 1); - } - else if ((inst1 & 0xf800) == 0xe000) /* unconditional branch */ - { - nextpc = pc_val + (sbits (inst1, 0, 10) << 1); - } - else if (thumb_insn_size (inst1) == 4) /* 32-bit instruction */ - { - unsigned short inst2; - inst2 = read_memory_unsigned_integer (pc + 2, 2, byte_order_for_code); - - /* Default to the next instruction. */ - nextpc = pc + 4; - nextpc = MAKE_THUMB_ADDR (nextpc); - - 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 = regcache_raw_get_unsigned (regcache, ARM_LR_REGNUM); - nextpc -= inst2 & 0x00ff; - } - else if ((inst2 & 0xd000) == 0x8000 && (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 = regcache_raw_get_unsigned (regcache, rn); - nextpc = read_memory_unsigned_integer (addr + offset, 4, - byte_order); - } - } - else if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00) - { - /* MOV PC or MOVS PC. */ - nextpc = regcache_raw_get_unsigned (regcache, bits (inst2, 0, 3)); - nextpc = MAKE_THUMB_ADDR (nextpc); - } - else if ((inst1 & 0xff70) == 0xf850 && (inst2 & 0xf000) == 0xf000) - { - /* LDR PC. */ - CORE_ADDR base; - int rn, load_pc = 1; - - rn = bits (inst1, 0, 3); - base = regcache_raw_get_unsigned (regcache, rn); - if (rn == ARM_PC_REGNUM) - { - 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 += regcache_raw_get_unsigned (regcache, rm) << shift; - } - else - /* Reserved. */ - load_pc = 0; - - if (load_pc) - nextpc = read_memory_unsigned_integer (base, 4, byte_order); - } - else if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf000) - { - /* TBB. */ - CORE_ADDR tbl_reg, table, offset, length; - - tbl_reg = bits (inst1, 0, 3); - if (tbl_reg == 0x0f) - table = pc + 4; /* Regcache copy of PC isn't right yet. */ - else - table = regcache_raw_get_unsigned (regcache, tbl_reg); - - offset = regcache_raw_get_unsigned (regcache, bits (inst2, 0, 3)); - length = 2 * read_memory_unsigned_integer (table + offset, 1, - byte_order); - nextpc = pc_val + length; - } - else if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf010) - { - /* TBH. */ - CORE_ADDR tbl_reg, table, offset, length; - - tbl_reg = bits (inst1, 0, 3); - if (tbl_reg == 0x0f) - table = pc + 4; /* Regcache copy of PC isn't right yet. */ - else - table = regcache_raw_get_unsigned (regcache, tbl_reg); - - offset = 2 * regcache_raw_get_unsigned (regcache, bits (inst2, 0, 3)); - length = 2 * read_memory_unsigned_integer (table + offset, 2, - byte_order); - nextpc = pc_val + length; - } - } - else if ((inst1 & 0xff00) == 0x4700) /* bx REG, blx REG */ - { - if (bits (inst1, 3, 6) == 0x0f) - nextpc = UNMAKE_THUMB_ADDR (pc_val); - else - nextpc = regcache_raw_get_unsigned (regcache, bits (inst1, 3, 6)); - } - else if ((inst1 & 0xff87) == 0x4687) /* mov pc, REG */ - { - if (bits (inst1, 3, 6) == 0x0f) - nextpc = pc_val; - else - nextpc = regcache_raw_get_unsigned (regcache, bits (inst1, 3, 6)); - - nextpc = MAKE_THUMB_ADDR (nextpc); - } - else if ((inst1 & 0xf500) == 0xb100) - { - /* CBNZ or CBZ. */ - int imm = (bit (inst1, 9) << 6) + (bits (inst1, 3, 7) << 1); - ULONGEST reg = regcache_raw_get_unsigned (regcache, 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; -} - -/* Get the raw next address. PC is the current program counter, in - FRAME, which is assumed to be executing in ARM mode. - - The value returned has the execution state of the next instruction - encoded in it. Use IS_THUMB_ADDR () to see whether the instruction is - in Thumb-State, and gdbarch_addr_bits_remove () to get the plain memory - address. */ - -static CORE_ADDR -arm_get_next_pc_raw (struct regcache *regcache, CORE_ADDR pc) -{ - struct gdbarch *gdbarch = get_regcache_arch (regcache); - enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); - enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch); - unsigned long pc_val; - unsigned long this_instr; - unsigned long status; - CORE_ADDR nextpc; - - pc_val = (unsigned long) pc; - this_instr = read_memory_unsigned_integer (pc, 4, byte_order_for_code); - - status = regcache_raw_get_unsigned (regcache, ARM_PS_REGNUM); - nextpc = (CORE_ADDR) (pc_val + 4); /* Default case */ - - if (bits (this_instr, 28, 31) == INST_NV) - switch (bits (this_instr, 24, 27)) - { - case 0xa: - case 0xb: - { - /* Branch with Link and change to Thumb. */ - nextpc = BranchDest (pc, this_instr); - nextpc |= bit (this_instr, 24) << 1; - nextpc = MAKE_THUMB_ADDR (nextpc); - break; - } - case 0xc: - case 0xd: - case 0xe: - /* Coprocessor register transfer. */ - if (bits (this_instr, 12, 15) == 15) - error (_("Invalid update to pc in instruction")); - break; - } - else if (condition_true (bits (this_instr, 28, 31), status)) - { - switch (bits (this_instr, 24, 27)) - { - case 0x0: - case 0x1: /* data processing */ - case 0x2: - case 0x3: - { - unsigned long operand1, operand2, result = 0; - unsigned long rn; - int c; - - if (bits (this_instr, 12, 15) != 15) - break; - - if (bits (this_instr, 22, 25) == 0 - && bits (this_instr, 4, 7) == 9) /* multiply */ - error (_("Invalid update to pc in instruction")); - - /* BX <reg>, BLX <reg> */ - if (bits (this_instr, 4, 27) == 0x12fff1 - || bits (this_instr, 4, 27) == 0x12fff3) - { - rn = bits (this_instr, 0, 3); - nextpc = ((rn == ARM_PC_REGNUM) - ? (pc_val + 8) - : regcache_raw_get_unsigned (regcache, rn)); - - return nextpc; - } - - /* Multiply into PC. */ - c = (status & FLAG_C) ? 1 : 0; - rn = bits (this_instr, 16, 19); - operand1 = ((rn == ARM_PC_REGNUM) - ? (pc_val + 8) - : regcache_raw_get_unsigned (regcache, rn)); - - if (bit (this_instr, 25)) - { - unsigned long immval = bits (this_instr, 0, 7); - unsigned long rotate = 2 * bits (this_instr, 8, 11); - operand2 = ((immval >> rotate) | (immval << (32 - rotate))) - & 0xffffffff; - } - else /* operand 2 is a shifted register. */ - operand2 = shifted_reg_val (regcache, this_instr, c, - pc_val, status); - - switch (bits (this_instr, 21, 24)) - { - case 0x0: /*and */ - result = operand1 & operand2; - break; - - case 0x1: /*eor */ - result = operand1 ^ operand2; - break; - - case 0x2: /*sub */ - result = operand1 - operand2; - break; - - case 0x3: /*rsb */ - result = operand2 - operand1; - break; - - case 0x4: /*add */ - result = operand1 + operand2; - break; - - case 0x5: /*adc */ - result = operand1 + operand2 + c; - break; - - case 0x6: /*sbc */ - result = operand1 - operand2 + c; - break; - - case 0x7: /*rsc */ - result = operand2 - operand1 + c; - break; - - case 0x8: - case 0x9: - case 0xa: - case 0xb: /* tst, teq, cmp, cmn */ - result = (unsigned long) nextpc; - break; - - case 0xc: /*orr */ - result = operand1 | operand2; - break; - - case 0xd: /*mov */ - /* Always step into a function. */ - result = operand2; - break; - - case 0xe: /*bic */ - result = operand1 & ~operand2; - break; - - case 0xf: /*mvn */ - result = ~operand2; - break; - } - - /* In 26-bit APCS the bottom two bits of the result are - ignored, and we always end up in ARM state. */ - if (!arm_apcs_32) - nextpc = arm_addr_bits_remove (gdbarch, result); - else - nextpc = result; - - break; - } - - case 0x4: - case 0x5: /* data transfer */ - case 0x6: - case 0x7: - if (bits (this_instr, 25, 27) == 0x3 && bit (this_instr, 4) == 1) - { - /* Media instructions and architecturally undefined - instructions. */ - break; - } - - if (bit (this_instr, 20)) - { - /* load */ - if (bits (this_instr, 12, 15) == 15) - { - /* rd == pc */ - unsigned long rn; - unsigned long base; - - if (bit (this_instr, 22)) - error (_("Invalid update to pc in instruction")); - - /* byte write to PC */ - rn = bits (this_instr, 16, 19); - base = ((rn == ARM_PC_REGNUM) - ? (pc_val + 8) - : regcache_raw_get_unsigned (regcache, rn)); - - if (bit (this_instr, 24)) - { - /* pre-indexed */ - int c = (status & FLAG_C) ? 1 : 0; - unsigned long offset = - (bit (this_instr, 25) - ? shifted_reg_val (regcache, this_instr, c, pc_val, - status) - : bits (this_instr, 0, 11)); - - if (bit (this_instr, 23)) - base += offset; - else - base -= offset; - } - nextpc = - (CORE_ADDR) read_memory_unsigned_integer ((CORE_ADDR) base, - 4, byte_order); - } - } - break; - - case 0x8: - case 0x9: /* block transfer */ - if (bit (this_instr, 20)) - { - /* LDM */ - if (bit (this_instr, 15)) - { - /* loading pc */ - int offset = 0; - unsigned long rn_val - = regcache_raw_get_unsigned (regcache, - bits (this_instr, 16, 19)); - - if (bit (this_instr, 23)) - { - /* up */ - unsigned long reglist = bits (this_instr, 0, 14); - offset = bitcount (reglist) * 4; - if (bit (this_instr, 24)) /* pre */ - offset += 4; - } - else if (bit (this_instr, 24)) - offset = -4; - - nextpc = - (CORE_ADDR) read_memory_unsigned_integer ((CORE_ADDR) - (rn_val + offset), - 4, byte_order); - } - } - break; - - case 0xb: /* branch & link */ - case 0xa: /* branch */ - { - nextpc = BranchDest (pc, this_instr); - break; - } - - case 0xc: - case 0xd: - case 0xe: /* coproc ops */ - break; - case 0xf: /* SWI */ - { - struct gdbarch_tdep *tdep; - tdep = gdbarch_tdep (gdbarch); - - if (tdep->syscall_next_pc != NULL) - nextpc = tdep->syscall_next_pc (regcache); - - } - break; - - default: - fprintf_filtered (gdb_stderr, _("Bad bit-field extraction\n")); - return (pc); - } - } - - return nextpc; -} - -/* Determine next PC after current instruction executes. Will call either - arm_get_next_pc_raw or thumb_get_next_pc_raw. Error out if infinite - loop is detected. */ - -CORE_ADDR -arm_get_next_pc (struct regcache *regcache, CORE_ADDR pc) -{ - CORE_ADDR nextpc; - - if (arm_is_thumb (regcache)) - nextpc = thumb_get_next_pc_raw (regcache, pc); - else - nextpc = arm_get_next_pc_raw (regcache, pc); - - return nextpc; -} - /* Like insert_single_step_breakpoint, but make sure we use a breakpoint of the appropriate mode (as encoded in the PC value), even if this differs from what would be expected according to the symbol tables. */ @@ -5068,248 +4109,6 @@ arm_insert_single_step_breakpoint (struct gdbarch *gdbarch, do_cleanups (old_chain); } -/* Checks for an atomic sequence of instructions beginning with a LDREX{,B,H,D} - instruction and ending with a STREX{,B,H,D} instruction. If such a sequence - is found, attempt to step through it. A breakpoint is placed at the end of - the sequence. */ - -static int -thumb_deal_with_atomic_sequence_raw (struct regcache *regcache) -{ - struct gdbarch *gdbarch = get_regcache_arch (regcache); - struct address_space *aspace = get_regcache_aspace (regcache); - enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch); - CORE_ADDR pc = regcache_read_pc (regcache); - CORE_ADDR breaks[2] = {-1, -1}; - CORE_ADDR loc = pc; - unsigned short insn1, insn2; - int insn_count; - int index; - int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed). */ - const int atomic_sequence_length = 16; /* Instruction sequence length. */ - ULONGEST status, itstate; - - /* We currently do not support atomic sequences within an IT block. */ - status = regcache_raw_get_unsigned (regcache, ARM_PS_REGNUM); - itstate = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3); - if (itstate & 0x0f) - return 0; - - /* Assume all atomic sequences start with a ldrex{,b,h,d} instruction. */ - insn1 = read_memory_unsigned_integer (loc, 2, byte_order_for_code); - loc += 2; - if (thumb_insn_size (insn1) != 4) - return 0; - - insn2 = read_memory_unsigned_integer (loc, 2, byte_order_for_code); - loc += 2; - if (!((insn1 & 0xfff0) == 0xe850 - || ((insn1 & 0xfff0) == 0xe8d0 && (insn2 & 0x00c0) == 0x0040))) - return 0; - - /* Assume that no atomic sequence is longer than "atomic_sequence_length" - instructions. */ - for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count) - { - insn1 = read_memory_unsigned_integer (loc, 2, byte_order_for_code); - loc += 2; - - if (thumb_insn_size (insn1) != 4) - { - /* Assume that there is at most one conditional branch in the - atomic sequence. If a conditional branch is found, put a - breakpoint in its destination address. */ - if ((insn1 & 0xf000) == 0xd000 && bits (insn1, 8, 11) != 0x0f) - { - if (last_breakpoint > 0) - return 0; /* More than one conditional branch found, - fallback to the standard code. */ - - breaks[1] = loc + 2 + (sbits (insn1, 0, 7) << 1); - last_breakpoint++; - } - - /* We do not support atomic sequences that use any *other* - instructions but conditional branches to change the PC. - Fall back to standard code to avoid losing control of - execution. */ - else if (thumb_instruction_changes_pc (insn1)) - return 0; - } - else - { - insn2 = read_memory_unsigned_integer (loc, 2, byte_order_for_code); - loc += 2; - - /* Assume that there is at most one conditional branch in the - atomic sequence. If a conditional branch is found, put a - breakpoint in its destination address. */ - if ((insn1 & 0xf800) == 0xf000 - && (insn2 & 0xd000) == 0x8000 - && (insn1 & 0x0380) != 0x0380) - { - int sign, j1, j2, imm1, imm2; - unsigned int offset; - - sign = sbits (insn1, 10, 10); - imm1 = bits (insn1, 0, 5); - imm2 = bits (insn2, 0, 10); - j1 = bit (insn2, 13); - j2 = bit (insn2, 11); - - offset = (sign << 20) + (j2 << 19) + (j1 << 18); - offset += (imm1 << 12) + (imm2 << 1); - - if (last_breakpoint > 0) - return 0; /* More than one conditional branch found, - fallback to the standard code. */ - - breaks[1] = loc + offset; - last_breakpoint++; - } - - /* We do not support atomic sequences that use any *other* - instructions but conditional branches to change the PC. - Fall back to standard code to avoid losing control of - execution. */ - else if (thumb2_instruction_changes_pc (insn1, insn2)) - return 0; - - /* If we find a strex{,b,h,d}, we're done. */ - if ((insn1 & 0xfff0) == 0xe840 - || ((insn1 & 0xfff0) == 0xe8c0 && (insn2 & 0x00c0) == 0x0040)) - break; - } - } - - /* If we didn't find the strex{,b,h,d}, we cannot handle the sequence. */ - if (insn_count == atomic_sequence_length) - return 0; - - /* Insert a breakpoint right after the end of the atomic sequence. */ - breaks[0] = loc; - - /* Check for duplicated breakpoints. Check also for a breakpoint - placed (branch instruction's destination) anywhere in sequence. */ - if (last_breakpoint - && (breaks[1] == breaks[0] - || (breaks[1] >= pc && breaks[1] < loc))) - last_breakpoint = 0; - - /* Effectively inserts the breakpoints. */ - for (index = 0; index <= last_breakpoint; index++) - arm_insert_single_step_breakpoint (gdbarch, aspace, - MAKE_THUMB_ADDR (breaks[index])); - - return 1; -} - -static int -arm_deal_with_atomic_sequence_raw (struct regcache *regcache) -{ - struct gdbarch *gdbarch = get_regcache_arch (regcache); - struct address_space *aspace = get_regcache_aspace (regcache); - enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch); - CORE_ADDR pc = regcache_read_pc (regcache); - CORE_ADDR breaks[2] = {-1, -1}; - CORE_ADDR loc = pc; - unsigned int insn; - int insn_count; - int index; - int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed). */ - const int atomic_sequence_length = 16; /* Instruction sequence length. */ - - /* Assume all atomic sequences start with a ldrex{,b,h,d} instruction. - Note that we do not currently support conditionally executed atomic - instructions. */ - insn = read_memory_unsigned_integer (loc, 4, byte_order_for_code); - loc += 4; - if ((insn & 0xff9000f0) != 0xe1900090) - return 0; - - /* Assume that no atomic sequence is longer than "atomic_sequence_length" - instructions. */ - for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count) - { - insn = read_memory_unsigned_integer (loc, 4, byte_order_for_code); - loc += 4; - - /* Assume that there is at most one conditional branch in the atomic - sequence. If a conditional branch is found, put a breakpoint in - its destination address. */ - if (bits (insn, 24, 27) == 0xa) - { - if (last_breakpoint > 0) - return 0; /* More than one conditional branch found, fallback - to the standard single-step code. */ - - breaks[1] = BranchDest (loc - 4, insn); - last_breakpoint++; - } - - /* We do not support atomic sequences that use any *other* instructions - but conditional branches to change the PC. Fall back to standard - code to avoid losing control of execution. */ - else if (arm_instruction_changes_pc (insn)) - return 0; - - /* If we find a strex{,b,h,d}, we're done. */ - if ((insn & 0xff9000f0) == 0xe1800090) - break; - } - - /* If we didn't find the strex{,b,h,d}, we cannot handle the sequence. */ - if (insn_count == atomic_sequence_length) - return 0; - - /* Insert a breakpoint right after the end of the atomic sequence. */ - breaks[0] = loc; - - /* Check for duplicated breakpoints. Check also for a breakpoint - placed (branch instruction's destination) anywhere in sequence. */ - if (last_breakpoint - && (breaks[1] == breaks[0] - || (breaks[1] >= pc && breaks[1] < loc))) - last_breakpoint = 0; - - /* Effectively inserts the breakpoints. */ - for (index = 0; index <= last_breakpoint; index++) - arm_insert_single_step_breakpoint (gdbarch, aspace, breaks[index]); - - return 1; -} - -int -arm_deal_with_atomic_sequence (struct regcache *regcache) -{ - if (arm_is_thumb (regcache)) - return thumb_deal_with_atomic_sequence_raw (regcache); - else - return arm_deal_with_atomic_sequence_raw (regcache); -} - -/* single_step() is called just before we want to resume the inferior, - if we want to single-step it but there is no hardware or kernel - single-step support. We find the target of the coming instruction - and breakpoint it. */ - -int -arm_software_single_step (struct frame_info *frame) -{ - struct regcache *regcache = get_current_regcache (); - struct gdbarch *gdbarch = get_regcache_arch (regcache); - struct address_space *aspace = get_regcache_aspace (regcache); - CORE_ADDR next_pc; - - if (arm_deal_with_atomic_sequence (regcache)) - return 1; - - next_pc = arm_get_next_pc (regcache, regcache_read_pc (regcache)); - arm_insert_single_step_breakpoint (gdbarch, aspace, next_pc); - - return 1; -} - /* Given BUF, which is OLD_LEN bytes ending at ENDADDR, expand the buffer to be NEW_LEN bytes ending at ENDADDR. Return NULL if an error occurs. BUF is freed. */ @@ -5400,6 +4199,7 @@ arm_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr) break; } } + if (any == 0) { xfree (buf); @@ -7320,6 +6120,81 @@ thumb2_copy_block_xfer (struct gdbarch *gdbarch, uint16_t insn1, uint16_t insn2, return 0; } +/* Wrapper over read_memory_unsigned_integer for use in arm_get_next_pcs. + This is used to avoid a dependency on BFD's bfd_endian enum. */ + +ULONGEST +arm_get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr, int len, + int byte_order) +{ + return read_memory_unsigned_integer (memaddr, len, byte_order); +} + +/* Wrapper over gdbarch_addr_bits_remove for use in arm_get_next_pcs. */ + +CORE_ADDR +arm_get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self, + CORE_ADDR val) +{ + return gdbarch_addr_bits_remove (get_regcache_arch (self->regcache), val); +} + +/* Wrapper over syscall_next_pc for use in get_next_pcs. */ + +CORE_ADDR +arm_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self, CORE_ADDR pc) +{ + struct gdbarch_tdep *tdep; + + tdep = gdbarch_tdep (get_regcache_arch (self->regcache)); + if (tdep->syscall_next_pc != NULL) + return tdep->syscall_next_pc (self->regcache); + + return 0; +} + +/* Wrapper over arm_is_thumb for use in arm_get_next_pcs. */ + +int +arm_get_next_pcs_is_thumb (struct arm_get_next_pcs *self) +{ + return arm_is_thumb (self->regcache); +} + +/* single_step() is called just before we want to resume the inferior, + if we want to single-step it but there is no hardware or kernel + single-step support. We find the target of the coming instructions + and breakpoint them. */ + +int +arm_software_single_step (struct frame_info *frame) +{ + struct regcache *regcache = get_current_regcache (); + struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct address_space *aspace = get_regcache_aspace (regcache); + struct arm_get_next_pcs next_pcs_ctx; + CORE_ADDR pc; + int i; + VEC (CORE_ADDR) *next_pcs = NULL; + struct cleanup *old_chain = make_cleanup (VEC_cleanup (CORE_ADDR), &next_pcs); + + arm_get_next_pcs_ctor (&next_pcs_ctx, + &arm_get_next_pcs_ops, + gdbarch_byte_order (gdbarch), + gdbarch_byte_order_for_code (gdbarch), + gdbarch_tdep (gdbarch)->thumb2_breakpoint, + regcache); + + next_pcs = arm_get_next_pcs (&next_pcs_ctx, regcache_read_pc (regcache)); + + for (i = 0; VEC_iterate (CORE_ADDR, next_pcs, i, pc); i++) + arm_insert_single_step_breakpoint (gdbarch, aspace, pc); + + do_cleanups (old_chain); + + return 1; +} + /* Cleanup/copy SVC (SWI) instructions. These two functions are overridden for Linux, where some SVC instructions must be treated specially. */ |