diff options
Diffstat (limited to 'gdb/arch/arm.c')
-rw-r--r-- | gdb/arch/arm.c | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/gdb/arch/arm.c b/gdb/arch/arm.c index 426377f..235c8c9 100644 --- a/gdb/arch/arm.c +++ b/gdb/arch/arm.c @@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "common-defs.h" +#include "common-regcache.h" #include "arm.h" /* See arm.h. */ @@ -87,3 +88,286 @@ condition_true (unsigned long cond, unsigned long status_reg) } return 1; } + + +/* See arm.h. */ + +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; +} + +/* See arm.h. */ + +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")); + } +} + +/* See arm.h. */ + +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; +} + + +/* See arm.h. */ + +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; +} + +/* See arm.h. */ + +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; +} |