diff options
Diffstat (limited to 'gdb/mips-tdep.c')
-rw-r--r-- | gdb/mips-tdep.c | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c index 6df6c61..8be42fe 100644 --- a/gdb/mips-tdep.c +++ b/gdb/mips-tdep.c @@ -1500,6 +1500,7 @@ mips16_scan_prologue (CORE_ADDR start_pc, CORE_ADDR limit_pc, unsigned short prev_inst = 0; /* saved copy of previous instruction */ unsigned inst = 0; /* current instruction */ unsigned entry_inst = 0; /* the entry instruction */ + unsigned save_inst = 0; /* the save instruction */ int reg, offset; int extend_bytes = 0; @@ -1603,6 +1604,12 @@ mips16_scan_prologue (CORE_ADDR start_pc, CORE_ADDR limit_pc, else if ((inst & 0xf81f) == 0xe809 && (inst & 0x700) != 0x700) /* entry */ entry_inst = inst; /* save for later processing */ + else if ((inst & 0xff80) == 0x6480) /* save */ + { + save_inst = inst; /* save for later processing */ + if (prev_extend_bytes) /* extend */ + save_inst |= prev_inst << 16; + } else if ((inst & 0xf800) == 0x1800) /* jal(x) */ cur_pc += MIPS_INSN16_SIZE; /* 32-bit instruction */ else if ((inst & 0xff1c) == 0x6704) /* move reg,$a0-$a3 */ @@ -1661,6 +1668,101 @@ mips16_scan_prologue (CORE_ADDR start_pc, CORE_ADDR limit_pc, } } + /* The SAVE instruction is similar to ENTRY, except that defined by the + MIPS16e ASE of the MIPS Architecture. Unlike with ENTRY though, the + size of the frame is specified as an immediate field of instruction + and an extended variation exists which lets additional registers and + frame space to be specified. The instruction always treats registers + as 32-bit so its usefulness for 64-bit ABIs is questionable. */ + if (save_inst != 0 && mips_abi_regsize (gdbarch) == 4) + { + static int args_table[16] = { + 0, 0, 0, 0, 1, 1, 1, 1, + 2, 2, 2, 0, 3, 3, 4, -1, + }; + static int astatic_table[16] = { + 0, 1, 2, 3, 0, 1, 2, 3, + 0, 1, 2, 4, 0, 1, 0, -1, + }; + int aregs = (save_inst >> 16) & 0xf; + int xsregs = (save_inst >> 24) & 0x7; + int args = args_table[aregs]; + int astatic = astatic_table[aregs]; + long frame_size; + + if (args < 0) + { + warning (_("Invalid number of argument registers encoded in SAVE.")); + args = 0; + } + if (astatic < 0) + { + warning (_("Invalid number of static registers encoded in SAVE.")); + astatic = 0; + } + + /* For standard SAVE the frame size of 0 means 128. */ + frame_size = ((save_inst >> 16) & 0xf0) | (save_inst & 0xf); + if (frame_size == 0 && (save_inst >> 16) == 0) + frame_size = 16; + frame_size *= 8; + frame_offset += frame_size; + + /* Now we can calculate what the SP must have been at the + start of the function prologue. */ + sp += frame_offset; + + /* Check if A0-A3 were saved in the caller's argument save area. */ + for (reg = MIPS_A0_REGNUM, offset = 0; reg < args + 4; reg++) + { + set_reg_offset (this_cache, reg, sp + offset); + offset += mips_abi_regsize (gdbarch); + } + + offset = -4; + + /* Check if the RA register was pushed on the stack. */ + if (save_inst & 0x40) + { + set_reg_offset (this_cache, MIPS_RA_REGNUM, sp + offset); + offset -= mips_abi_regsize (gdbarch); + } + + /* Check if the S8 register was pushed on the stack. */ + if (xsregs > 6) + { + set_reg_offset (this_cache, 30, sp + offset); + offset -= mips_abi_regsize (gdbarch); + xsregs--; + } + /* Check if S2-S7 were pushed on the stack. */ + for (reg = 18 + xsregs - 1; reg > 18 - 1; reg--) + { + set_reg_offset (this_cache, reg, sp + offset); + offset -= mips_abi_regsize (gdbarch); + } + + /* Check if the S1 register was pushed on the stack. */ + if (save_inst & 0x10) + { + set_reg_offset (this_cache, 17, sp + offset); + offset -= mips_abi_regsize (gdbarch); + } + /* Check if the S0 register was pushed on the stack. */ + if (save_inst & 0x20) + { + set_reg_offset (this_cache, 16, sp + offset); + offset -= mips_abi_regsize (gdbarch); + } + + /* Check if A0-A3 were pushed on the stack. */ + for (reg = MIPS_A0_REGNUM + 3; reg > MIPS_A0_REGNUM + 3 - astatic; reg--) + { + set_reg_offset (this_cache, reg, sp + offset); + offset -= mips_abi_regsize (gdbarch); + } + } + if (this_cache != NULL) { this_cache->base = |