diff options
-rw-r--r-- | gdb/loongarch-tdep.c | 132 |
1 files changed, 131 insertions, 1 deletions
diff --git a/gdb/loongarch-tdep.c b/gdb/loongarch-tdep.c index 88a8382..e30ef80 100644 --- a/gdb/loongarch-tdep.c +++ b/gdb/loongarch-tdep.c @@ -22,11 +22,131 @@ #include "dwarf2/frame.h" #include "elf-bfd.h" #include "frame-unwind.h" +#include "gdbcore.h" #include "loongarch-tdep.h" +#include "target.h" #include "target-descriptions.h" #include "trad-frame.h" #include "user-regs.h" +/* Fetch the instruction at PC. */ + +static insn_t +loongarch_fetch_instruction (CORE_ADDR pc) +{ + size_t insn_len = loongarch_insn_length (0); + gdb_byte buf[insn_len]; + int err; + + err = target_read_memory (pc, buf, insn_len); + if (err) + memory_error (TARGET_XFER_E_IO, pc); + + return extract_unsigned_integer (buf, insn_len, BFD_ENDIAN_LITTLE); +} + +/* Return TRUE if INSN is a branch instruction, otherwise return FALSE. */ + +static bool +loongarch_insn_is_branch (insn_t insn) +{ + if ((insn & 0xfc000000) == 0x4c000000 /* jirl rd, rj, offs16 */ + || (insn & 0xfc000000) == 0x50000000 /* b offs26 */ + || (insn & 0xfc000000) == 0x54000000 /* bl offs26 */ + || (insn & 0xfc000000) == 0x58000000 /* beq rj, rd, offs16 */ + || (insn & 0xfc000000) == 0x5c000000 /* bne rj, rd, offs16 */ + || (insn & 0xfc000000) == 0x60000000 /* blt rj, rd, offs16 */ + || (insn & 0xfc000000) == 0x64000000 /* bge rj, rd, offs16 */ + || (insn & 0xfc000000) == 0x68000000 /* bltu rj, rd, offs16 */ + || (insn & 0xfc000000) == 0x6c000000 /* bgeu rj, rd, offs16 */ + || (insn & 0xfc000000) == 0x40000000 /* beqz rj, offs21 */ + || (insn & 0xfc000000) == 0x44000000) /* bnez rj, offs21 */ + return true; + return false; +} + +/* Analyze the function prologue from START_PC to LIMIT_PC. + Return the address of the first instruction past the prologue. */ + +static CORE_ADDR +loongarch_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc, + CORE_ADDR limit_pc, struct frame_info *this_frame, + struct trad_frame_cache *this_cache) +{ + CORE_ADDR cur_pc = start_pc, prologue_end = 0; + loongarch_gdbarch_tdep *tdep = (loongarch_gdbarch_tdep *) gdbarch_tdep (gdbarch); + auto regs = tdep->regs; + int32_t sp = regs.r + 3; + int32_t fp = regs.r + 22; + int32_t reg_value[32] = {0}; + int32_t reg_used[32] = {1, 0}; + + while (cur_pc < limit_pc) + { + insn_t insn = loongarch_fetch_instruction (cur_pc); + size_t insn_len = loongarch_insn_length (insn); + int32_t rd = loongarch_decode_imm ("0:5", insn, 0); + int32_t rj = loongarch_decode_imm ("5:5", insn, 0); + int32_t rk = loongarch_decode_imm ("10:5", insn, 0); + int32_t si12 = loongarch_decode_imm ("10:12", insn, 1); + int32_t si20 = loongarch_decode_imm ("5:20", insn, 1); + + if ((insn & 0xffc00000) == 0x02c00000 /* addi.d sp,sp,si12 */ + && rd == sp && rj == sp && si12 < 0) + { + prologue_end = cur_pc + insn_len; + } + else if ((insn & 0xffc00000) == 0x02c00000 /* addi.d fp,sp,si12 */ + && rd == fp && rj == sp && si12 > 0) + { + prologue_end = cur_pc + insn_len; + } + else if ((insn & 0xffc00000) == 0x29c00000 /* st.d rd,sp,si12 */ + && rj == sp) + { + prologue_end = cur_pc + insn_len; + } + else if ((insn & 0xff000000) == 0x27000000 /* stptr.d rd,sp,si14 */ + && rj == sp) + { + prologue_end = cur_pc + insn_len; + } + else if ((insn & 0xfe000000) == 0x14000000) /* lu12i.w rd,si20 */ + { + reg_value[rd] = si20 << 12; + reg_used[rd] = 1; + } + else if ((insn & 0xffc00000) == 0x03800000) /* ori rd,rj,si12 */ + { + if (reg_used[rj]) + { + reg_value[rd] = reg_value[rj] | (si12 & 0xfff); + reg_used[rd] = 1; + } + } + else if ((insn & 0xffff8000) == 0x00108000 /* add.d sp,sp,rk */ + && rd == sp && rj == sp) + { + if (reg_used[rk] == 1 && reg_value[rk] < 0) + { + prologue_end = cur_pc + insn_len; + break; + } + } + else if (loongarch_insn_is_branch (insn)) + { + break; + } + + cur_pc += insn_len; + } + + if (prologue_end == 0) + prologue_end = cur_pc; + + return prologue_end; +} + /* Implement the loongarch_skip_prologue gdbarch method. */ static CORE_ADDR @@ -45,7 +165,17 @@ loongarch_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) return std::max (pc, post_prologue_pc); } - return 0; + /* Can't determine prologue from the symbol table, need to examine + instructions. */ + + /* Find an upper limit on the function prologue using the debug + information. If the debug information could not be used to provide + that bound, then use an arbitrary large number as the upper bound. */ + CORE_ADDR limit_pc = skip_prologue_using_sal (gdbarch, pc); + if (limit_pc == 0) + limit_pc = pc + 100; /* Arbitrary large number. */ + + return loongarch_scan_prologue (gdbarch, pc, limit_pc, nullptr, nullptr); } /* Adjust the address downward (direction of stack growth) so that it |