diff options
Diffstat (limited to 'gdb/aarch64-tdep.c')
-rw-r--r-- | gdb/aarch64-tdep.c | 426 |
1 files changed, 83 insertions, 343 deletions
diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index d84c034..4bdd227 100644 --- a/gdb/aarch64-tdep.c +++ b/gdb/aarch64-tdep.c @@ -194,302 +194,6 @@ show_aarch64_debug (struct ui_file *file, int from_tty, fprintf_filtered (file, _("AArch64 debugging is %s.\n"), value); } -/* Extract a signed value from a bit field within an instruction - encoding. - - INSN is the instruction opcode. - - WIDTH specifies the width of the bit field to extract (in bits). - - OFFSET specifies the least significant bit of the field where bits - are numbered zero counting from least to most significant. */ - -static int32_t -extract_signed_bitfield (uint32_t insn, unsigned width, unsigned offset) -{ - unsigned shift_l = sizeof (int32_t) * 8 - (offset + width); - unsigned shift_r = sizeof (int32_t) * 8 - width; - - return ((int32_t) insn << shift_l) >> shift_r; -} - -/* Determine if specified bits within an instruction opcode matches a - specific pattern. - - INSN is the instruction opcode. - - MASK specifies the bits within the opcode that are to be tested - agsinst for a match with PATTERN. */ - -static int -decode_masked_match (uint32_t insn, uint32_t mask, uint32_t pattern) -{ - return (insn & mask) == pattern; -} - -/* Decode an opcode if it represents an immediate ADD or SUB instruction. - - ADDR specifies the address of the opcode. - INSN specifies the opcode to test. - RD receives the 'rd' field from the decoded instruction. - RN receives the 'rn' field from the decoded instruction. - - Return 1 if the opcodes matches and is decoded, otherwise 0. */ -static int -aarch64_decode_add_sub_imm (CORE_ADDR addr, uint32_t insn, unsigned *rd, - unsigned *rn, int32_t *imm) -{ - if ((insn & 0x9f000000) == 0x91000000) - { - unsigned shift; - unsigned op_is_sub; - - *rd = (insn >> 0) & 0x1f; - *rn = (insn >> 5) & 0x1f; - *imm = (insn >> 10) & 0xfff; - shift = (insn >> 22) & 0x3; - op_is_sub = (insn >> 30) & 0x1; - - switch (shift) - { - case 0: - break; - case 1: - *imm <<= 12; - break; - default: - /* UNDEFINED */ - return 0; - } - - if (op_is_sub) - *imm = -*imm; - - if (aarch64_debug) - { - debug_printf ("decode: 0x%s 0x%x add x%u, x%u, #%d\n", - core_addr_to_string_nz (addr), insn, *rd, *rn, - *imm); - } - return 1; - } - return 0; -} - -/* Decode an opcode if it represents a branch via register instruction. - - ADDR specifies the address of the opcode. - INSN specifies the opcode to test. - IS_BLR receives the 'op' bit from the decoded instruction. - RN receives the 'rn' field from the decoded instruction. - - Return 1 if the opcodes matches and is decoded, otherwise 0. */ - -static int -aarch64_decode_br (CORE_ADDR addr, uint32_t insn, int *is_blr, - unsigned *rn) -{ - /* 8 4 0 6 2 8 4 0 */ - /* blr 110101100011111100000000000rrrrr */ - /* br 110101100001111100000000000rrrrr */ - if (decode_masked_match (insn, 0xffdffc1f, 0xd61f0000)) - { - *is_blr = (insn >> 21) & 1; - *rn = (insn >> 5) & 0x1f; - - if (aarch64_debug) - { - debug_printf ("decode: 0x%s 0x%x %s 0x%x\n", - core_addr_to_string_nz (addr), insn, - *is_blr ? "blr" : "br", *rn); - } - - return 1; - } - return 0; -} - -/* Decode an opcode if it represents a ERET instruction. - - ADDR specifies the address of the opcode. - INSN specifies the opcode to test. - - Return 1 if the opcodes matches and is decoded, otherwise 0. */ - -static int -aarch64_decode_eret (CORE_ADDR addr, uint32_t insn) -{ - /* eret 1101 0110 1001 1111 0000 0011 1110 0000 */ - if (insn == 0xd69f03e0) - { - if (aarch64_debug) - { - debug_printf ("decode: 0x%s 0x%x eret\n", - core_addr_to_string_nz (addr), insn); - } - return 1; - } - return 0; -} - -/* Decode an opcode if it represents a MOVZ instruction. - - ADDR specifies the address of the opcode. - INSN specifies the opcode to test. - RD receives the 'rd' field from the decoded instruction. - - Return 1 if the opcodes matches and is decoded, otherwise 0. */ - -static int -aarch64_decode_movz (CORE_ADDR addr, uint32_t insn, unsigned *rd) -{ - if (decode_masked_match (insn, 0xff800000, 0x52800000)) - { - *rd = (insn >> 0) & 0x1f; - - if (aarch64_debug) - { - debug_printf ("decode: 0x%s 0x%x movz x%u, #?\n", - core_addr_to_string_nz (addr), insn, *rd); - } - return 1; - } - return 0; -} - -/* Decode an opcode if it represents a ORR (shifted register) - instruction. - - ADDR specifies the address of the opcode. - INSN specifies the opcode to test. - RD receives the 'rd' field from the decoded instruction. - RN receives the 'rn' field from the decoded instruction. - RM receives the 'rm' field from the decoded instruction. - IMM receives the 'imm6' field from the decoded instruction. - - Return 1 if the opcodes matches and is decoded, otherwise 0. */ - -static int -aarch64_decode_orr_shifted_register_x (CORE_ADDR addr, uint32_t insn, - unsigned *rd, unsigned *rn, - unsigned *rm, int32_t *imm) -{ - if (decode_masked_match (insn, 0xff200000, 0xaa000000)) - { - *rd = (insn >> 0) & 0x1f; - *rn = (insn >> 5) & 0x1f; - *rm = (insn >> 16) & 0x1f; - *imm = (insn >> 10) & 0x3f; - - if (aarch64_debug) - { - debug_printf ("decode: 0x%s 0x%x orr x%u, x%u, x%u, #%u\n", - core_addr_to_string_nz (addr), insn, *rd, *rn, - *rm, *imm); - } - return 1; - } - return 0; -} - -/* Decode an opcode if it represents a RET instruction. - - ADDR specifies the address of the opcode. - INSN specifies the opcode to test. - RN receives the 'rn' field from the decoded instruction. - - Return 1 if the opcodes matches and is decoded, otherwise 0. */ - -static int -aarch64_decode_ret (CORE_ADDR addr, uint32_t insn, unsigned *rn) -{ - if (decode_masked_match (insn, 0xfffffc1f, 0xd65f0000)) - { - *rn = (insn >> 5) & 0x1f; - if (aarch64_debug) - { - debug_printf ("decode: 0x%s 0x%x ret x%u\n", - core_addr_to_string_nz (addr), insn, *rn); - } - return 1; - } - return 0; -} - -/* Decode an opcode if it represents the following instructions: - - STP rt, rt2, [rn, #imm] - STP rt, rt2, [rn, #imm]! - - ADDR specifies the address of the opcode. - INSN specifies the opcode to test. - RT1 receives the 'rt' field from the decoded instruction. - RT2 receives the 'rt2' field from the decoded instruction. - RN receives the 'rn' field from the decoded instruction. - IMM receives the 'imm' field from the decoded instruction. - *WBACK receives the bit 23 from the decoded instruction. - - Return 1 if the opcodes matches and is decoded, otherwise 0. */ - -static int -aarch64_decode_stp_offset (CORE_ADDR addr, uint32_t insn, unsigned *rt1, - unsigned *rt2, unsigned *rn, int32_t *imm, - int *wback) -{ - if (decode_masked_match (insn, 0xff400000, 0xa9000000)) - { - *rt1 = (insn >> 0) & 0x1f; - *rn = (insn >> 5) & 0x1f; - *rt2 = (insn >> 10) & 0x1f; - *imm = extract_signed_bitfield (insn, 7, 15); - *imm <<= 3; - *wback = bit (insn, 23); - - if (aarch64_debug) - { - debug_printf ("decode: 0x%s 0x%x stp x%u, x%u, [x%u + #%d]%s\n", - core_addr_to_string_nz (addr), insn, *rt1, *rt2, - *rn, *imm, *wback ? "" : "!"); - } - return 1; - } - return 0; -} - -/* Decode an opcode if it represents the following instruction: - STUR rt, [rn, #imm] - - ADDR specifies the address of the opcode. - INSN specifies the opcode to test. - IS64 receives size field from the decoded instruction. - RT receives the 'rt' field from the decoded instruction. - RN receives the 'rn' field from the decoded instruction. - IMM receives the 'imm' field from the decoded instruction. - - Return 1 if the opcodes matches and is decoded, otherwise 0. */ - -static int -aarch64_decode_stur (CORE_ADDR addr, uint32_t insn, int *is64, - unsigned *rt, unsigned *rn, int32_t *imm) -{ - if (decode_masked_match (insn, 0xbfe00c00, 0xb8000000)) - { - *is64 = (insn >> 30) & 1; - *rt = (insn >> 0) & 0x1f; - *rn = (insn >> 5) & 0x1f; - *imm = extract_signed_bitfield (insn, 9, 12); - - if (aarch64_debug) - { - debug_printf ("decode: 0x%s 0x%x stur %c%u, [x%u + #%d]\n", - core_addr_to_string_nz (addr), insn, - *is64 ? 'x' : 'w', *rt, *rn, *imm); - } - return 1; - } - return 0; -} - /* Analyze a prologue, looking for a recognizable stack frame and frame pointer. Scan until we encounter a store that could clobber the stack frame unexpectedly, or an unknown instruction. */ @@ -513,63 +217,82 @@ aarch64_analyze_prologue (struct gdbarch *gdbarch, for (; start < limit; start += 4) { uint32_t insn; - unsigned rd; - unsigned rn; - unsigned rm; - unsigned rt; - unsigned rt1; - unsigned rt2; - int op_is_sub; - int wback; - int32_t imm; - unsigned cond; - int is64; - int is_link; - int is_cbnz; - int is_tbnz; - unsigned bit; - int is_adrp; - int32_t offset; + aarch64_inst inst; insn = read_memory_unsigned_integer (start, 4, byte_order_for_code); - if (aarch64_decode_add_sub_imm (start, insn, &rd, &rn, &imm)) - regs[rd] = pv_add_constant (regs[rn], imm); - else if (aarch64_decode_adr (start, insn, &is_adrp, &rd, &offset) - && is_adrp) - regs[rd] = pv_unknown (); - else if (aarch64_decode_b (start, insn, &is_link, &offset)) + if (aarch64_decode_insn (insn, &inst, 1) != 0) + break; + + if (inst.opcode->iclass == addsub_imm + && (inst.opcode->op == OP_ADD + || strcmp ("sub", inst.opcode->name) == 0)) { - /* Stop analysis on branch. */ - break; + unsigned rd = inst.operands[0].reg.regno; + unsigned rn = inst.operands[1].reg.regno; + + gdb_assert (aarch64_num_of_operands (inst.opcode) == 3); + gdb_assert (inst.operands[0].type == AARCH64_OPND_Rd_SP); + gdb_assert (inst.operands[1].type == AARCH64_OPND_Rn_SP); + gdb_assert (inst.operands[2].type == AARCH64_OPND_AIMM); + + if (inst.opcode->op == OP_ADD) + { + regs[rd] = pv_add_constant (regs[rn], + inst.operands[2].imm.value); + } + else + { + regs[rd] = pv_add_constant (regs[rn], + -inst.operands[2].imm.value); + } + } + else if (inst.opcode->iclass == pcreladdr + && inst.operands[1].type == AARCH64_OPND_ADDR_ADRP) + { + gdb_assert (aarch64_num_of_operands (inst.opcode) == 2); + gdb_assert (inst.operands[0].type == AARCH64_OPND_Rd); + + regs[inst.operands[0].reg.regno] = pv_unknown (); } - else if (aarch64_decode_bcond (start, insn, &cond, &offset)) + else if (inst.opcode->iclass == branch_imm) { /* Stop analysis on branch. */ break; } - else if (aarch64_decode_br (start, insn, &is_link, &rn)) + else if (inst.opcode->iclass == condbranch) { /* Stop analysis on branch. */ break; } - else if (aarch64_decode_cb (start, insn, &is64, &is_cbnz, &rn, - &offset)) + else if (inst.opcode->iclass == branch_reg) { /* Stop analysis on branch. */ break; } - else if (aarch64_decode_eret (start, insn)) + else if (inst.opcode->iclass == compbranch) { /* Stop analysis on branch. */ break; } - else if (aarch64_decode_movz (start, insn, &rd)) - regs[rd] = pv_unknown (); - else if (aarch64_decode_orr_shifted_register_x (start, insn, &rd, - &rn, &rm, &imm)) + else if (inst.opcode->op == OP_MOVZ) { - if (imm == 0 && rn == 31) + gdb_assert (inst.operands[0].type == AARCH64_OPND_Rd); + regs[inst.operands[0].reg.regno] = pv_unknown (); + } + else if (inst.opcode->iclass == log_shift + && strcmp (inst.opcode->name, "orr") == 0) + { + unsigned rd = inst.operands[0].reg.regno; + unsigned rn = inst.operands[1].reg.regno; + unsigned rm = inst.operands[2].reg.regno; + + gdb_assert (inst.operands[0].type == AARCH64_OPND_Rd); + gdb_assert (inst.operands[1].type == AARCH64_OPND_Rn); + gdb_assert (inst.operands[2].type == AARCH64_OPND_Rm_SFT); + + if (inst.operands[2].shifter.amount == 0 + && rn == AARCH64_SP_REGNUM) regs[rd] = regs[rm]; else { @@ -582,19 +305,37 @@ aarch64_analyze_prologue (struct gdbarch *gdbarch, break; } } - else if (aarch64_decode_ret (start, insn, &rn)) - { - /* Stop analysis on branch. */ - break; - } - else if (aarch64_decode_stur (start, insn, &is64, &rt, &rn, &offset)) + else if (inst.opcode->op == OP_STUR) { - pv_area_store (stack, pv_add_constant (regs[rn], offset), + unsigned rt = inst.operands[0].reg.regno; + unsigned rn = inst.operands[1].addr.base_regno; + int is64 + = (aarch64_get_qualifier_esize (inst.operands[0].qualifier) == 8); + + gdb_assert (aarch64_num_of_operands (inst.opcode) == 2); + gdb_assert (inst.operands[0].type == AARCH64_OPND_Rt); + gdb_assert (inst.operands[1].type == AARCH64_OPND_ADDR_SIMM9); + gdb_assert (!inst.operands[1].addr.offset.is_reg); + + pv_area_store (stack, pv_add_constant (regs[rn], + inst.operands[1].addr.offset.imm), is64 ? 8 : 4, regs[rt]); } - else if (aarch64_decode_stp_offset (start, insn, &rt1, &rt2, &rn, - &imm, &wback)) + else if ((inst.opcode->iclass == ldstpair_off + || inst.opcode->iclass == ldstpair_indexed) + && inst.operands[2].addr.preind + && strcmp ("stp", inst.opcode->name) == 0) { + unsigned rt1 = inst.operands[0].reg.regno; + unsigned rt2 = inst.operands[1].reg.regno; + unsigned rn = inst.operands[2].addr.base_regno; + int32_t imm = inst.operands[2].addr.offset.imm; + + gdb_assert (inst.operands[0].type == AARCH64_OPND_Rt); + gdb_assert (inst.operands[1].type == AARCH64_OPND_Rt2); + gdb_assert (inst.operands[2].type == AARCH64_OPND_ADDR_SIMM7); + gdb_assert (!inst.operands[2].addr.offset.is_reg); + /* If recording this store would invalidate the store area (perhaps because rn is not known) then we should abandon further prologue analysis. */ @@ -611,12 +352,11 @@ aarch64_analyze_prologue (struct gdbarch *gdbarch, pv_area_store (stack, pv_add_constant (regs[rn], imm + 8), 8, regs[rt2]); - if (wback) + if (inst.operands[2].addr.writeback) regs[rn] = pv_add_constant (regs[rn], imm); } - else if (aarch64_decode_tb (start, insn, &is_tbnz, &bit, &rn, - &offset)) + else if (inst.opcode->iclass == testbranch) { /* Stop analysis on branch. */ break; |