diff options
author | Joel Brobecker <brobecker@gnat.com> | 2004-10-15 07:25:04 +0000 |
---|---|---|
committer | Joel Brobecker <brobecker@gnat.com> | 2004-10-15 07:25:04 +0000 |
commit | 296391227bd7f0c0b1804f5a7afced16daf90c37 (patch) | |
tree | a52a5f981b5b8bb0764f171c05733013985c0ffd | |
parent | a65bbe4404711745c6f3359139cbceaebb110c12 (diff) | |
download | gdb-296391227bd7f0c0b1804f5a7afced16daf90c37.zip gdb-296391227bd7f0c0b1804f5a7afced16daf90c37.tar.gz gdb-296391227bd7f0c0b1804f5a7afced16daf90c37.tar.bz2 |
* mips-tdep.c (mips32_scan_prologue): Move the implementation up
a bit to avoid the necessity of an advance declaration. Remove
declaration.
(set_reg_offset): Move implemenation up.
(mips16_get_imm): Likewise.
(mips16_scan_prologue): Likewise.
(reset_saved_regs): Likewise.
(mips32_scan_prologue): Likewise.
-rw-r--r-- | gdb/ChangeLog | 11 | ||||
-rw-r--r-- | gdb/mips-tdep.c | 957 |
2 files changed, 484 insertions, 484 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index cbb02ba..4895c14 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,14 @@ +2004-10-15 Joel Brobecker <brobecker@gnat.com> + + * mips-tdep.c (mips32_scan_prologue): Move the implementation up + a bit to avoid the necessity of an advance declaration. Remove + declaration. + (set_reg_offset): Move implemenation up. + (mips16_get_imm): Likewise. + (mips16_scan_prologue): Likewise. + (reset_saved_regs): Likewise. + (mips32_scan_prologue): Likewise. + 2004-10-14 Joel Brobecker <brobecker@gnat.com> * mips-tdep.c (mips32_scan_prologue): Add advance declaration. diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c index 7ad6ad1..4f249b8 100644 --- a/gdb/mips-tdep.c +++ b/gdb/mips-tdep.c @@ -442,17 +442,6 @@ static struct type *mips_double_register_type (void); static struct cmd_list_element *setmipscmdlist = NULL; static struct cmd_list_element *showmipscmdlist = NULL; -/* FIXME: brobecker/2004-10-15: I suspect these two declarations can - be removed by a better ordering of the functions below. But I want - to do that as a separate change later in order to separate real - changes and changes that just move some code around. */ -static CORE_ADDR mips32_scan_prologue (CORE_ADDR start_pc, CORE_ADDR limit_pc, - struct frame_info *next_frame, - struct mips_frame_cache *this_cache); -static CORE_ADDR mips16_scan_prologue (CORE_ADDR start_pc, CORE_ADDR limit_pc, - struct frame_info *next_frame, - struct mips_frame_cache *this_cache); - /* Integer registers 0 thru 31 are handled explicitly by mips_register_name(). Processor specific registers 32 and above are listed in the followign tables. */ @@ -1721,6 +1710,262 @@ mips_mdebug_frame_base_sniffer (struct frame_info *next_frame) return NULL; } +/* Set a register's saved stack address in temp_saved_regs. If an + address has already been set for this register, do nothing; this + way we will only recognize the first save of a given register in a + function prologue. + + For simplicity, save the address in both [0 .. NUM_REGS) and + [NUM_REGS .. 2*NUM_REGS). Strictly speaking, only the second range + is used as it is only second range (the ABI instead of ISA + registers) that comes into play when finding saved registers in a + frame. */ + +static void +set_reg_offset (struct mips_frame_cache *this_cache, int regnum, + CORE_ADDR offset) +{ + if (this_cache != NULL + && this_cache->saved_regs[regnum].addr == -1) + { + this_cache->saved_regs[regnum + 0 * NUM_REGS].addr = offset; + this_cache->saved_regs[regnum + 1 * NUM_REGS].addr = offset; + } +} + + +/* Fetch the immediate value from a MIPS16 instruction. + If the previous instruction was an EXTEND, use it to extend + the upper bits of the immediate value. This is a helper function + for mips16_scan_prologue. */ + +static int +mips16_get_imm (unsigned short prev_inst, /* previous instruction */ + unsigned short inst, /* current instruction */ + int nbits, /* number of bits in imm field */ + int scale, /* scale factor to be applied to imm */ + int is_signed) /* is the imm field signed? */ +{ + int offset; + + if ((prev_inst & 0xf800) == 0xf000) /* prev instruction was EXTEND? */ + { + offset = ((prev_inst & 0x1f) << 11) | (prev_inst & 0x7e0); + if (offset & 0x8000) /* check for negative extend */ + offset = 0 - (0x10000 - (offset & 0xffff)); + return offset | (inst & 0x1f); + } + else + { + int max_imm = 1 << nbits; + int mask = max_imm - 1; + int sign_bit = max_imm >> 1; + + offset = inst & mask; + if (is_signed && (offset & sign_bit)) + offset = 0 - (max_imm - offset); + return offset * scale; + } +} + + +/* Analyze the function prologue from START_PC to LIMIT_PC. Builds + the associated FRAME_CACHE if not null. + Return the address of the first instruction past the prologue. */ + +static CORE_ADDR +mips16_scan_prologue (CORE_ADDR start_pc, CORE_ADDR limit_pc, + struct frame_info *next_frame, + struct mips_frame_cache *this_cache) +{ + CORE_ADDR cur_pc; + CORE_ADDR frame_addr = 0; /* Value of $r17, used as frame pointer */ + CORE_ADDR sp; + long frame_offset = 0; /* Size of stack frame. */ + long frame_adjust = 0; /* Offset of FP from SP. */ + int frame_reg = MIPS_SP_REGNUM; + unsigned short prev_inst = 0; /* saved copy of previous instruction */ + unsigned inst = 0; /* current instruction */ + unsigned entry_inst = 0; /* the entry instruction */ + int reg, offset; + + int extend_bytes = 0; + int prev_extend_bytes; + CORE_ADDR end_prologue_addr = 0; + + /* Can be called when there's no process, and hence when there's no + NEXT_FRAME. */ + if (next_frame != NULL) + sp = read_next_frame_reg (next_frame, NUM_REGS + MIPS_SP_REGNUM); + else + sp = 0; + + if (limit_pc > start_pc + 200) + limit_pc = start_pc + 200; + + for (cur_pc = start_pc; cur_pc < limit_pc; cur_pc += MIPS16_INSTLEN) + { + /* Save the previous instruction. If it's an EXTEND, we'll extract + the immediate offset extension from it in mips16_get_imm. */ + prev_inst = inst; + + /* Fetch and decode the instruction. */ + inst = (unsigned short) mips_fetch_instruction (cur_pc); + + /* Normally we ignore extend instructions. However, if it is + not followed by a valid prologue instruction, then this + instruction is not part of the prologue either. We must + remember in this case to adjust the end_prologue_addr back + over the extend. */ + if ((inst & 0xf800) == 0xf000) /* extend */ + { + extend_bytes = MIPS16_INSTLEN; + continue; + } + + prev_extend_bytes = extend_bytes; + extend_bytes = 0; + + if ((inst & 0xff00) == 0x6300 /* addiu sp */ + || (inst & 0xff00) == 0xfb00) /* daddiu sp */ + { + offset = mips16_get_imm (prev_inst, inst, 8, 8, 1); + if (offset < 0) /* negative stack adjustment? */ + frame_offset -= offset; + else + /* Exit loop if a positive stack adjustment is found, which + usually means that the stack cleanup code in the function + epilogue is reached. */ + break; + } + else if ((inst & 0xf800) == 0xd000) /* sw reg,n($sp) */ + { + offset = mips16_get_imm (prev_inst, inst, 8, 4, 0); + reg = mips16_to_32_reg[(inst & 0x700) >> 8]; + set_reg_offset (this_cache, reg, sp + offset); + } + else if ((inst & 0xff00) == 0xf900) /* sd reg,n($sp) */ + { + offset = mips16_get_imm (prev_inst, inst, 5, 8, 0); + reg = mips16_to_32_reg[(inst & 0xe0) >> 5]; + set_reg_offset (this_cache, reg, sp + offset); + } + else if ((inst & 0xff00) == 0x6200) /* sw $ra,n($sp) */ + { + offset = mips16_get_imm (prev_inst, inst, 8, 4, 0); + set_reg_offset (this_cache, RA_REGNUM, sp + offset); + } + else if ((inst & 0xff00) == 0xfa00) /* sd $ra,n($sp) */ + { + offset = mips16_get_imm (prev_inst, inst, 8, 8, 0); + set_reg_offset (this_cache, RA_REGNUM, sp + offset); + } + else if (inst == 0x673d) /* move $s1, $sp */ + { + frame_addr = sp; + frame_reg = 17; + } + else if ((inst & 0xff00) == 0x0100) /* addiu $s1,sp,n */ + { + offset = mips16_get_imm (prev_inst, inst, 8, 4, 0); + frame_addr = sp + offset; + frame_reg = 17; + frame_adjust = offset; + } + else if ((inst & 0xFF00) == 0xd900) /* sw reg,offset($s1) */ + { + offset = mips16_get_imm (prev_inst, inst, 5, 4, 0); + reg = mips16_to_32_reg[(inst & 0xe0) >> 5]; + set_reg_offset (this_cache, reg, frame_addr + offset); + } + else if ((inst & 0xFF00) == 0x7900) /* sd reg,offset($s1) */ + { + offset = mips16_get_imm (prev_inst, inst, 5, 8, 0); + reg = mips16_to_32_reg[(inst & 0xe0) >> 5]; + set_reg_offset (this_cache, reg, frame_addr + offset); + } + else if ((inst & 0xf81f) == 0xe809 + && (inst & 0x700) != 0x700) /* entry */ + entry_inst = inst; /* save for later processing */ + else if ((inst & 0xf800) == 0x1800) /* jal(x) */ + cur_pc += MIPS16_INSTLEN; /* 32-bit instruction */ + else if ((inst & 0xff1c) == 0x6704) /* move reg,$a0-$a3 */ + { + /* This instruction is part of the prologue, but we don't + need to do anything special to handle it. */ + } + else + { + /* This instruction is not an instruction typically found + in a prologue, so we must have reached the end of the + prologue. */ + if (end_prologue_addr == 0) + end_prologue_addr = cur_pc - prev_extend_bytes; + } + } + + /* The entry instruction is typically the first instruction in a function, + and it stores registers at offsets relative to the value of the old SP + (before the prologue). But the value of the sp parameter to this + function is the new SP (after the prologue has been executed). So we + can't calculate those offsets until we've seen the entire prologue, + and can calculate what the old SP must have been. */ + if (entry_inst != 0) + { + int areg_count = (entry_inst >> 8) & 7; + int sreg_count = (entry_inst >> 6) & 3; + + /* The entry instruction always subtracts 32 from the SP. */ + frame_offset += 32; + + /* 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 = 4, offset = 0; reg < areg_count + 4; reg++) + { + set_reg_offset (this_cache, reg, sp + offset); + offset += mips_abi_regsize (current_gdbarch); + } + + /* Check if the ra register was pushed on the stack. */ + offset = -4; + if (entry_inst & 0x20) + { + set_reg_offset (this_cache, RA_REGNUM, sp + offset); + offset -= mips_abi_regsize (current_gdbarch); + } + + /* Check if the s0 and s1 registers were pushed on the stack. */ + for (reg = 16; reg < sreg_count + 16; reg++) + { + set_reg_offset (this_cache, reg, sp + offset); + offset -= mips_abi_regsize (current_gdbarch); + } + } + + if (this_cache != NULL) + { + this_cache->base = + (frame_unwind_register_signed (next_frame, NUM_REGS + frame_reg) + + frame_offset - frame_adjust); + /* FIXME: brobecker/2004-10-10: Just as in the mips32 case, we should + be able to get rid of the assignment below, evetually. But it's + still needed for now. */ + this_cache->saved_regs[NUM_REGS + mips_regnum (current_gdbarch)->pc] + = this_cache->saved_regs[NUM_REGS + RA_REGNUM]; + } + + /* If we didn't reach the end of the prologue when scanning the function + instructions, then set end_prologue_addr to the address of the + instruction immediately after the last one we scanned. */ + if (end_prologue_addr == 0) + end_prologue_addr = cur_pc; + + return end_prologue_addr; +} + /* Heuristic unwinder for 16-bit MIPS instruction set (aka MIPS16). Procedures that use the 32-bit instruction set are handled by the mips_insn32 unwinder. */ @@ -1822,6 +2067,223 @@ mips_insn16_frame_base_sniffer (struct frame_info *next_frame) return NULL; } +/* Mark all the registers as unset in the saved_regs array + of THIS_CACHE. Do nothing if THIS_CACHE is null. */ + +void +reset_saved_regs (struct mips_frame_cache *this_cache) +{ + if (this_cache == NULL || this_cache->saved_regs == NULL) + return; + + { + const int num_regs = NUM_REGS; + int i; + + for (i = 0; i < num_regs; i++) + { + this_cache->saved_regs[i].addr = -1; + } + } +} + +/* Analyze the function prologue from START_PC to LIMIT_PC. Builds + the associated FRAME_CACHE if not null. + Return the address of the first instruction past the prologue. */ + +static CORE_ADDR +mips32_scan_prologue (CORE_ADDR start_pc, CORE_ADDR limit_pc, + struct frame_info *next_frame, + struct mips_frame_cache *this_cache) +{ + CORE_ADDR cur_pc; + CORE_ADDR frame_addr = 0; /* Value of $r30. Used by gcc for frame-pointer */ + CORE_ADDR sp; + long frame_offset; + int frame_reg = MIPS_SP_REGNUM; + + CORE_ADDR end_prologue_addr = 0; + int seen_sp_adjust = 0; + int load_immediate_bytes = 0; + + /* Can be called when there's no process, and hence when there's no + NEXT_FRAME. */ + if (next_frame != NULL) + sp = read_next_frame_reg (next_frame, NUM_REGS + MIPS_SP_REGNUM); + else + sp = 0; + + if (limit_pc > start_pc + 200) + limit_pc = start_pc + 200; + +restart: + + frame_offset = 0; + for (cur_pc = start_pc; cur_pc < limit_pc; cur_pc += MIPS_INSTLEN) + { + unsigned long inst, high_word, low_word; + int reg; + + /* Fetch the instruction. */ + inst = (unsigned long) mips_fetch_instruction (cur_pc); + + /* Save some code by pre-extracting some useful fields. */ + high_word = (inst >> 16) & 0xffff; + low_word = inst & 0xffff; + reg = high_word & 0x1f; + + if (high_word == 0x27bd /* addiu $sp,$sp,-i */ + || high_word == 0x23bd /* addi $sp,$sp,-i */ + || high_word == 0x67bd) /* daddiu $sp,$sp,-i */ + { + if (low_word & 0x8000) /* negative stack adjustment? */ + frame_offset += 0x10000 - low_word; + else + /* Exit loop if a positive stack adjustment is found, which + usually means that the stack cleanup code in the function + epilogue is reached. */ + break; + seen_sp_adjust = 1; + } + else if ((high_word & 0xFFE0) == 0xafa0) /* sw reg,offset($sp) */ + { + set_reg_offset (this_cache, reg, sp + low_word); + } + else if ((high_word & 0xFFE0) == 0xffa0) /* sd reg,offset($sp) */ + { + /* Irix 6.2 N32 ABI uses sd instructions for saving $gp and $ra. */ + set_reg_offset (this_cache, reg, sp + low_word); + } + else if (high_word == 0x27be) /* addiu $30,$sp,size */ + { + /* Old gcc frame, r30 is virtual frame pointer. */ + if ((long) low_word != frame_offset) + frame_addr = sp + low_word; + else if (frame_reg == MIPS_SP_REGNUM) + { + unsigned alloca_adjust; + + frame_reg = 30; + frame_addr = read_next_frame_reg (next_frame, NUM_REGS + 30); + alloca_adjust = (unsigned) (frame_addr - (sp + low_word)); + if (alloca_adjust > 0) + { + /* FP > SP + frame_size. This may be because of + an alloca or somethings similar. Fix sp to + "pre-alloca" value, and try again. */ + sp += alloca_adjust; + /* Need to reset the status of all registers. Otherwise, + we will hit a guard that prevents the new address + for each register to be recomputed during the second + pass. */ + reset_saved_regs (this_cache); + goto restart; + } + } + } + /* move $30,$sp. With different versions of gas this will be either + `addu $30,$sp,$zero' or `or $30,$sp,$zero' or `daddu 30,sp,$0'. + Accept any one of these. */ + else if (inst == 0x03A0F021 || inst == 0x03a0f025 || inst == 0x03a0f02d) + { + /* New gcc frame, virtual frame pointer is at r30 + frame_size. */ + if (frame_reg == MIPS_SP_REGNUM) + { + unsigned alloca_adjust; + + frame_reg = 30; + frame_addr = read_next_frame_reg (next_frame, NUM_REGS + 30); + alloca_adjust = (unsigned) (frame_addr - sp); + if (alloca_adjust > 0) + { + /* FP > SP + frame_size. This may be because of + an alloca or somethings similar. Fix sp to + "pre-alloca" value, and try again. */ + sp = frame_addr; + /* Need to reset the status of all registers. Otherwise, + we will hit a guard that prevents the new address + for each register to be recomputed during the second + pass. */ + reset_saved_regs (this_cache); + goto restart; + } + } + } + else if ((high_word & 0xFFE0) == 0xafc0) /* sw reg,offset($30) */ + { + set_reg_offset (this_cache, reg, frame_addr + low_word); + } + else if ((high_word & 0xFFE0) == 0xE7A0 /* swc1 freg,n($sp) */ + || (high_word & 0xF3E0) == 0xA3C0 /* sx reg,n($s8) */ + || (inst & 0xFF9F07FF) == 0x00800021 /* move reg,$a0-$a3 */ + || high_word == 0x3c1c /* lui $gp,n */ + || high_word == 0x279c /* addiu $gp,$gp,n */ + || inst == 0x0399e021 /* addu $gp,$gp,$t9 */ + || inst == 0x033ce021 /* addu $gp,$t9,$gp */ + ) + { + /* These instructions are part of the prologue, but we don't + need to do anything special to handle them. */ + } + /* The instructions below load $at or $t0 with an immediate + value in preparation for a stack adjustment via + subu $sp,$sp,[$at,$t0]. These instructions could also + initialize a local variable, so we accept them only before + a stack adjustment instruction was seen. */ + else if (!seen_sp_adjust + && (high_word == 0x3c01 /* lui $at,n */ + || high_word == 0x3c08 /* lui $t0,n */ + || high_word == 0x3421 /* ori $at,$at,n */ + || high_word == 0x3508 /* ori $t0,$t0,n */ + || high_word == 0x3401 /* ori $at,$zero,n */ + || high_word == 0x3408 /* ori $t0,$zero,n */ + )) + { + load_immediate_bytes += MIPS_INSTLEN; /* FIXME!! */ + } + else + { + /* This instruction is not an instruction typically found + in a prologue, so we must have reached the end of the + prologue. */ + /* FIXME: brobecker/2004-10-10: Can't we just break out of this + loop now? Why would we need to continue scanning the function + instructions? */ + if (end_prologue_addr == 0) + end_prologue_addr = cur_pc; + } + } + + if (this_cache != NULL) + { + this_cache->base = + (frame_unwind_register_signed (next_frame, NUM_REGS + frame_reg) + + frame_offset); + /* FIXME: brobecker/2004-09-15: We should be able to get rid of + this assignment below, eventually. But it's still needed + for now. */ + this_cache->saved_regs[NUM_REGS + mips_regnum (current_gdbarch)->pc] + = this_cache->saved_regs[NUM_REGS + RA_REGNUM]; + } + + /* If we didn't reach the end of the prologue when scanning the function + instructions, then set end_prologue_addr to the address of the + instruction immediately after the last one we scanned. */ + /* brobecker/2004-10-10: I don't think this would ever happen, but + we may as well be careful and do our best if we have a null + end_prologue_addr. */ + if (end_prologue_addr == 0) + end_prologue_addr = cur_pc; + + /* In a frameless function, we might have incorrectly + skipped some load immediate instructions. Undo the skipping + if the load immediate was not followed by a stack adjustment. */ + if (load_immediate_bytes && !seen_sp_adjust) + end_prologue_addr -= load_immediate_bytes; + + return end_prologue_addr; +} + /* Heuristic unwinder for procedures using 32-bit instructions (covers both 32-bit and 64-bit MIPS ISAs). Procedures using 16-bit instructions (a.k.a. MIPS16) are handled by the mips_insn16 @@ -2092,30 +2554,6 @@ mips_software_single_step (enum target_signal sig, int insert_breakpoints_p) static struct mips_extra_func_info temp_proc_desc; -/* Set a register's saved stack address in temp_saved_regs. If an - address has already been set for this register, do nothing; this - way we will only recognize the first save of a given register in a - function prologue. - - For simplicity, save the address in both [0 .. NUM_REGS) and - [NUM_REGS .. 2*NUM_REGS). Strictly speaking, only the second range - is used as it is only second range (the ABI instead of ISA - registers) that comes into play when finding saved registers in a - frame. */ - -static void -set_reg_offset (struct mips_frame_cache *this_cache, int regnum, - CORE_ADDR offset) -{ - if (this_cache != NULL - && this_cache->saved_regs[regnum].addr == -1) - { - this_cache->saved_regs[regnum + 0 * NUM_REGS].addr = offset; - this_cache->saved_regs[regnum + 1 * NUM_REGS].addr = offset; - } -} - - /* Test whether the PC points to the return instruction at the end of a function. */ @@ -2229,455 +2667,6 @@ heuristic-fence-post' command.\n", paddr_nz (pc), paddr_nz (pc)); return start_pc; } -/* Fetch the immediate value from a MIPS16 instruction. - If the previous instruction was an EXTEND, use it to extend - the upper bits of the immediate value. This is a helper function - for mips16_scan_prologue. */ - -static int -mips16_get_imm (unsigned short prev_inst, /* previous instruction */ - unsigned short inst, /* current instruction */ - int nbits, /* number of bits in imm field */ - int scale, /* scale factor to be applied to imm */ - int is_signed) /* is the imm field signed? */ -{ - int offset; - - if ((prev_inst & 0xf800) == 0xf000) /* prev instruction was EXTEND? */ - { - offset = ((prev_inst & 0x1f) << 11) | (prev_inst & 0x7e0); - if (offset & 0x8000) /* check for negative extend */ - offset = 0 - (0x10000 - (offset & 0xffff)); - return offset | (inst & 0x1f); - } - else - { - int max_imm = 1 << nbits; - int mask = max_imm - 1; - int sign_bit = max_imm >> 1; - - offset = inst & mask; - if (is_signed && (offset & sign_bit)) - offset = 0 - (max_imm - offset); - return offset * scale; - } -} - - -/* Analyze the function prologue from START_PC to LIMIT_PC. Builds - the associated FRAME_CACHE if not null. - Return the address of the first instruction past the prologue. */ - -static CORE_ADDR -mips16_scan_prologue (CORE_ADDR start_pc, CORE_ADDR limit_pc, - struct frame_info *next_frame, - struct mips_frame_cache *this_cache) -{ - CORE_ADDR cur_pc; - CORE_ADDR frame_addr = 0; /* Value of $r17, used as frame pointer */ - CORE_ADDR sp; - long frame_offset = 0; /* Size of stack frame. */ - long frame_adjust = 0; /* Offset of FP from SP. */ - int frame_reg = MIPS_SP_REGNUM; - unsigned short prev_inst = 0; /* saved copy of previous instruction */ - unsigned inst = 0; /* current instruction */ - unsigned entry_inst = 0; /* the entry instruction */ - int reg, offset; - - int extend_bytes = 0; - int prev_extend_bytes; - CORE_ADDR end_prologue_addr = 0; - - /* Can be called when there's no process, and hence when there's no - NEXT_FRAME. */ - if (next_frame != NULL) - sp = read_next_frame_reg (next_frame, NUM_REGS + MIPS_SP_REGNUM); - else - sp = 0; - - if (limit_pc > start_pc + 200) - limit_pc = start_pc + 200; - - for (cur_pc = start_pc; cur_pc < limit_pc; cur_pc += MIPS16_INSTLEN) - { - /* Save the previous instruction. If it's an EXTEND, we'll extract - the immediate offset extension from it in mips16_get_imm. */ - prev_inst = inst; - - /* Fetch and decode the instruction. */ - inst = (unsigned short) mips_fetch_instruction (cur_pc); - - /* Normally we ignore extend instructions. However, if it is - not followed by a valid prologue instruction, then this - instruction is not part of the prologue either. We must - remember in this case to adjust the end_prologue_addr back - over the extend. */ - if ((inst & 0xf800) == 0xf000) /* extend */ - { - extend_bytes = MIPS16_INSTLEN; - continue; - } - - prev_extend_bytes = extend_bytes; - extend_bytes = 0; - - if ((inst & 0xff00) == 0x6300 /* addiu sp */ - || (inst & 0xff00) == 0xfb00) /* daddiu sp */ - { - offset = mips16_get_imm (prev_inst, inst, 8, 8, 1); - if (offset < 0) /* negative stack adjustment? */ - frame_offset -= offset; - else - /* Exit loop if a positive stack adjustment is found, which - usually means that the stack cleanup code in the function - epilogue is reached. */ - break; - } - else if ((inst & 0xf800) == 0xd000) /* sw reg,n($sp) */ - { - offset = mips16_get_imm (prev_inst, inst, 8, 4, 0); - reg = mips16_to_32_reg[(inst & 0x700) >> 8]; - set_reg_offset (this_cache, reg, sp + offset); - } - else if ((inst & 0xff00) == 0xf900) /* sd reg,n($sp) */ - { - offset = mips16_get_imm (prev_inst, inst, 5, 8, 0); - reg = mips16_to_32_reg[(inst & 0xe0) >> 5]; - set_reg_offset (this_cache, reg, sp + offset); - } - else if ((inst & 0xff00) == 0x6200) /* sw $ra,n($sp) */ - { - offset = mips16_get_imm (prev_inst, inst, 8, 4, 0); - set_reg_offset (this_cache, RA_REGNUM, sp + offset); - } - else if ((inst & 0xff00) == 0xfa00) /* sd $ra,n($sp) */ - { - offset = mips16_get_imm (prev_inst, inst, 8, 8, 0); - set_reg_offset (this_cache, RA_REGNUM, sp + offset); - } - else if (inst == 0x673d) /* move $s1, $sp */ - { - frame_addr = sp; - frame_reg = 17; - } - else if ((inst & 0xff00) == 0x0100) /* addiu $s1,sp,n */ - { - offset = mips16_get_imm (prev_inst, inst, 8, 4, 0); - frame_addr = sp + offset; - frame_reg = 17; - frame_adjust = offset; - } - else if ((inst & 0xFF00) == 0xd900) /* sw reg,offset($s1) */ - { - offset = mips16_get_imm (prev_inst, inst, 5, 4, 0); - reg = mips16_to_32_reg[(inst & 0xe0) >> 5]; - set_reg_offset (this_cache, reg, frame_addr + offset); - } - else if ((inst & 0xFF00) == 0x7900) /* sd reg,offset($s1) */ - { - offset = mips16_get_imm (prev_inst, inst, 5, 8, 0); - reg = mips16_to_32_reg[(inst & 0xe0) >> 5]; - set_reg_offset (this_cache, reg, frame_addr + offset); - } - else if ((inst & 0xf81f) == 0xe809 - && (inst & 0x700) != 0x700) /* entry */ - entry_inst = inst; /* save for later processing */ - else if ((inst & 0xf800) == 0x1800) /* jal(x) */ - cur_pc += MIPS16_INSTLEN; /* 32-bit instruction */ - else if ((inst & 0xff1c) == 0x6704) /* move reg,$a0-$a3 */ - { - /* This instruction is part of the prologue, but we don't - need to do anything special to handle it. */ - } - else - { - /* This instruction is not an instruction typically found - in a prologue, so we must have reached the end of the - prologue. */ - if (end_prologue_addr == 0) - end_prologue_addr = cur_pc - prev_extend_bytes; - } - } - - /* The entry instruction is typically the first instruction in a function, - and it stores registers at offsets relative to the value of the old SP - (before the prologue). But the value of the sp parameter to this - function is the new SP (after the prologue has been executed). So we - can't calculate those offsets until we've seen the entire prologue, - and can calculate what the old SP must have been. */ - if (entry_inst != 0) - { - int areg_count = (entry_inst >> 8) & 7; - int sreg_count = (entry_inst >> 6) & 3; - - /* The entry instruction always subtracts 32 from the SP. */ - frame_offset += 32; - - /* 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 = 4, offset = 0; reg < areg_count + 4; reg++) - { - set_reg_offset (this_cache, reg, sp + offset); - offset += mips_abi_regsize (current_gdbarch); - } - - /* Check if the ra register was pushed on the stack. */ - offset = -4; - if (entry_inst & 0x20) - { - set_reg_offset (this_cache, RA_REGNUM, sp + offset); - offset -= mips_abi_regsize (current_gdbarch); - } - - /* Check if the s0 and s1 registers were pushed on the stack. */ - for (reg = 16; reg < sreg_count + 16; reg++) - { - set_reg_offset (this_cache, reg, sp + offset); - offset -= mips_abi_regsize (current_gdbarch); - } - } - - if (this_cache != NULL) - { - this_cache->base = - (frame_unwind_register_signed (next_frame, NUM_REGS + frame_reg) - + frame_offset - frame_adjust); - /* FIXME: brobecker/2004-10-10: Just as in the mips32 case, we should - be able to get rid of the assignment below, evetually. But it's - still needed for now. */ - this_cache->saved_regs[NUM_REGS + mips_regnum (current_gdbarch)->pc] - = this_cache->saved_regs[NUM_REGS + RA_REGNUM]; - } - - /* If we didn't reach the end of the prologue when scanning the function - instructions, then set end_prologue_addr to the address of the - instruction immediately after the last one we scanned. */ - if (end_prologue_addr == 0) - end_prologue_addr = cur_pc; - - return end_prologue_addr; -} - -/* Mark all the registers as unset in the saved_regs array - of THIS_CACHE. Do nothing if THIS_CACHE is null. */ - -void -reset_saved_regs (struct mips_frame_cache *this_cache) -{ - if (this_cache == NULL || this_cache->saved_regs == NULL) - return; - - { - const int num_regs = NUM_REGS; - int i; - - for (i = 0; i < num_regs; i++) - { - this_cache->saved_regs[i].addr = -1; - } - } -} - -/* Analyze the function prologue from START_PC to LIMIT_PC. Builds - the associated FRAME_CACHE if not null. - Return the address of the first instruction past the prologue. */ - -static CORE_ADDR -mips32_scan_prologue (CORE_ADDR start_pc, CORE_ADDR limit_pc, - struct frame_info *next_frame, - struct mips_frame_cache *this_cache) -{ - CORE_ADDR cur_pc; - CORE_ADDR frame_addr = 0; /* Value of $r30. Used by gcc for frame-pointer */ - CORE_ADDR sp; - long frame_offset; - int frame_reg = MIPS_SP_REGNUM; - - CORE_ADDR end_prologue_addr = 0; - int seen_sp_adjust = 0; - int load_immediate_bytes = 0; - - /* Can be called when there's no process, and hence when there's no - NEXT_FRAME. */ - if (next_frame != NULL) - sp = read_next_frame_reg (next_frame, NUM_REGS + MIPS_SP_REGNUM); - else - sp = 0; - - if (limit_pc > start_pc + 200) - limit_pc = start_pc + 200; - -restart: - - frame_offset = 0; - for (cur_pc = start_pc; cur_pc < limit_pc; cur_pc += MIPS_INSTLEN) - { - unsigned long inst, high_word, low_word; - int reg; - - /* Fetch the instruction. */ - inst = (unsigned long) mips_fetch_instruction (cur_pc); - - /* Save some code by pre-extracting some useful fields. */ - high_word = (inst >> 16) & 0xffff; - low_word = inst & 0xffff; - reg = high_word & 0x1f; - - if (high_word == 0x27bd /* addiu $sp,$sp,-i */ - || high_word == 0x23bd /* addi $sp,$sp,-i */ - || high_word == 0x67bd) /* daddiu $sp,$sp,-i */ - { - if (low_word & 0x8000) /* negative stack adjustment? */ - frame_offset += 0x10000 - low_word; - else - /* Exit loop if a positive stack adjustment is found, which - usually means that the stack cleanup code in the function - epilogue is reached. */ - break; - seen_sp_adjust = 1; - } - else if ((high_word & 0xFFE0) == 0xafa0) /* sw reg,offset($sp) */ - { - set_reg_offset (this_cache, reg, sp + low_word); - } - else if ((high_word & 0xFFE0) == 0xffa0) /* sd reg,offset($sp) */ - { - /* Irix 6.2 N32 ABI uses sd instructions for saving $gp and $ra. */ - set_reg_offset (this_cache, reg, sp + low_word); - } - else if (high_word == 0x27be) /* addiu $30,$sp,size */ - { - /* Old gcc frame, r30 is virtual frame pointer. */ - if ((long) low_word != frame_offset) - frame_addr = sp + low_word; - else if (frame_reg == MIPS_SP_REGNUM) - { - unsigned alloca_adjust; - - frame_reg = 30; - frame_addr = read_next_frame_reg (next_frame, NUM_REGS + 30); - alloca_adjust = (unsigned) (frame_addr - (sp + low_word)); - if (alloca_adjust > 0) - { - /* FP > SP + frame_size. This may be because of - an alloca or somethings similar. Fix sp to - "pre-alloca" value, and try again. */ - sp += alloca_adjust; - /* Need to reset the status of all registers. Otherwise, - we will hit a guard that prevents the new address - for each register to be recomputed during the second - pass. */ - reset_saved_regs (this_cache); - goto restart; - } - } - } - /* move $30,$sp. With different versions of gas this will be either - `addu $30,$sp,$zero' or `or $30,$sp,$zero' or `daddu 30,sp,$0'. - Accept any one of these. */ - else if (inst == 0x03A0F021 || inst == 0x03a0f025 || inst == 0x03a0f02d) - { - /* New gcc frame, virtual frame pointer is at r30 + frame_size. */ - if (frame_reg == MIPS_SP_REGNUM) - { - unsigned alloca_adjust; - - frame_reg = 30; - frame_addr = read_next_frame_reg (next_frame, NUM_REGS + 30); - alloca_adjust = (unsigned) (frame_addr - sp); - if (alloca_adjust > 0) - { - /* FP > SP + frame_size. This may be because of - an alloca or somethings similar. Fix sp to - "pre-alloca" value, and try again. */ - sp = frame_addr; - /* Need to reset the status of all registers. Otherwise, - we will hit a guard that prevents the new address - for each register to be recomputed during the second - pass. */ - reset_saved_regs (this_cache); - goto restart; - } - } - } - else if ((high_word & 0xFFE0) == 0xafc0) /* sw reg,offset($30) */ - { - set_reg_offset (this_cache, reg, frame_addr + low_word); - } - else if ((high_word & 0xFFE0) == 0xE7A0 /* swc1 freg,n($sp) */ - || (high_word & 0xF3E0) == 0xA3C0 /* sx reg,n($s8) */ - || (inst & 0xFF9F07FF) == 0x00800021 /* move reg,$a0-$a3 */ - || high_word == 0x3c1c /* lui $gp,n */ - || high_word == 0x279c /* addiu $gp,$gp,n */ - || inst == 0x0399e021 /* addu $gp,$gp,$t9 */ - || inst == 0x033ce021 /* addu $gp,$t9,$gp */ - ) - { - /* These instructions are part of the prologue, but we don't - need to do anything special to handle them. */ - } - /* The instructions below load $at or $t0 with an immediate - value in preparation for a stack adjustment via - subu $sp,$sp,[$at,$t0]. These instructions could also - initialize a local variable, so we accept them only before - a stack adjustment instruction was seen. */ - else if (!seen_sp_adjust - && (high_word == 0x3c01 /* lui $at,n */ - || high_word == 0x3c08 /* lui $t0,n */ - || high_word == 0x3421 /* ori $at,$at,n */ - || high_word == 0x3508 /* ori $t0,$t0,n */ - || high_word == 0x3401 /* ori $at,$zero,n */ - || high_word == 0x3408 /* ori $t0,$zero,n */ - )) - { - load_immediate_bytes += MIPS_INSTLEN; /* FIXME!! */ - } - else - { - /* This instruction is not an instruction typically found - in a prologue, so we must have reached the end of the - prologue. */ - /* FIXME: brobecker/2004-10-10: Can't we just break out of this - loop now? Why would we need to continue scanning the function - instructions? */ - if (end_prologue_addr == 0) - end_prologue_addr = cur_pc; - } - } - - if (this_cache != NULL) - { - this_cache->base = - (frame_unwind_register_signed (next_frame, NUM_REGS + frame_reg) - + frame_offset); - /* FIXME: brobecker/2004-09-15: We should be able to get rid of - this assignment below, eventually. But it's still needed - for now. */ - this_cache->saved_regs[NUM_REGS + mips_regnum (current_gdbarch)->pc] - = this_cache->saved_regs[NUM_REGS + RA_REGNUM]; - } - - /* If we didn't reach the end of the prologue when scanning the function - instructions, then set end_prologue_addr to the address of the - instruction immediately after the last one we scanned. */ - /* brobecker/2004-10-10: I don't think this would ever happen, but - we may as well be careful and do our best if we have a null - end_prologue_addr. */ - if (end_prologue_addr == 0) - end_prologue_addr = cur_pc; - - /* In a frameless function, we might have incorrectly - skipped some load immediate instructions. Undo the skipping - if the load immediate was not followed by a stack adjustment. */ - if (load_immediate_bytes && !seen_sp_adjust) - end_prologue_addr -= load_immediate_bytes; - - return end_prologue_addr; -} - static mips_extra_func_info_t heuristic_proc_desc (CORE_ADDR start_pc, CORE_ADDR limit_pc, struct frame_info *next_frame, |