From: Joel Brobecker Date: Fri, 15 Oct 2004 07:25:04 +0000 (+0000) Subject: * mips-tdep.c (mips32_scan_prologue): Move the implementation up X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=296391227bd7f0c0b1804f5a7afced16daf90c37;p=binutils-gdb.git * 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. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index cbb02ba3e03..4895c14a7bc 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,14 @@ +2004-10-15 Joel Brobecker + + * 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 * mips-tdep.c (mips32_scan_prologue): Add advance declaration. diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c index 7ad6ad1e1b6..4f249b8e439 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,961 +1710,961 @@ mips_mdebug_frame_base_sniffer (struct frame_info *next_frame) return NULL; } -/* 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. */ - -static struct mips_frame_cache * -mips_insn16_frame_cache (struct frame_info *next_frame, void **this_cache) -{ - struct mips_frame_cache *cache; - - if ((*this_cache) != NULL) - return (*this_cache); - cache = FRAME_OBSTACK_ZALLOC (struct mips_frame_cache); - (*this_cache) = cache; - cache->saved_regs = trad_frame_alloc_saved_regs (next_frame); - - /* Analyze the function prologue. */ - { - const CORE_ADDR pc = frame_pc_unwind (next_frame); - CORE_ADDR start_addr; - - find_pc_partial_function (pc, NULL, &start_addr, NULL); - if (start_addr == 0) - start_addr = heuristic_proc_start (pc); - /* We can't analyze the prologue if we couldn't find the begining - of the function. */ - if (start_addr == 0) - return cache; - - mips16_scan_prologue (start_addr, pc, next_frame, *this_cache); - } - - /* SP_REGNUM, contains the value and not the address. */ - trad_frame_set_value (cache->saved_regs, NUM_REGS + MIPS_SP_REGNUM, cache->base); - - return (*this_cache); -} +/* 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. -static void -mips_insn16_frame_this_id (struct frame_info *next_frame, void **this_cache, - struct frame_id *this_id) -{ - struct mips_frame_cache *info = mips_insn16_frame_cache (next_frame, - this_cache); - (*this_id) = frame_id_build (info->base, frame_func_unwind (next_frame)); -} + 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 -mips_insn16_frame_prev_register (struct frame_info *next_frame, - void **this_cache, - int regnum, int *optimizedp, - enum lval_type *lvalp, CORE_ADDR *addrp, - int *realnump, void *valuep) +set_reg_offset (struct mips_frame_cache *this_cache, int regnum, + CORE_ADDR offset) { - struct mips_frame_cache *info = mips_insn16_frame_cache (next_frame, - this_cache); - trad_frame_get_prev_register (next_frame, info->saved_regs, regnum, - optimizedp, lvalp, addrp, realnump, valuep); + 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; + } } -static const struct frame_unwind mips_insn16_frame_unwind = -{ - NORMAL_FRAME, - mips_insn16_frame_this_id, - mips_insn16_frame_prev_register -}; - -static const struct frame_unwind * -mips_insn16_frame_sniffer (struct frame_info *next_frame) -{ - CORE_ADDR pc = frame_pc_unwind (next_frame); - if (pc_is_mips16 (pc)) - return &mips_insn16_frame_unwind; - return NULL; -} -static CORE_ADDR -mips_insn16_frame_base_address (struct frame_info *next_frame, - void **this_cache) -{ - struct mips_frame_cache *info = mips_insn16_frame_cache (next_frame, - this_cache); - return info->base; -} +/* 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 const struct frame_base mips_insn16_frame_base = +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? */ { - &mips_insn16_frame_unwind, - mips_insn16_frame_base_address, - mips_insn16_frame_base_address, - mips_insn16_frame_base_address -}; + int offset; -static const struct frame_base * -mips_insn16_frame_base_sniffer (struct frame_info *next_frame) -{ - if (mips_insn16_frame_sniffer (next_frame) != NULL) - return &mips_insn16_frame_base; + 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 - return NULL; -} - -/* 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 - unwinder. */ + { + int max_imm = 1 << nbits; + int mask = max_imm - 1; + int sign_bit = max_imm >> 1; -static struct mips_frame_cache * -mips_insn32_frame_cache (struct frame_info *next_frame, void **this_cache) -{ - struct mips_frame_cache *cache; + offset = inst & mask; + if (is_signed && (offset & sign_bit)) + offset = 0 - (max_imm - offset); + return offset * scale; + } +} - if ((*this_cache) != NULL) - return (*this_cache); - cache = FRAME_OBSTACK_ZALLOC (struct mips_frame_cache); - (*this_cache) = cache; - cache->saved_regs = trad_frame_alloc_saved_regs (next_frame); +/* 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. */ - /* Analyze the function prologue. */ - { - const CORE_ADDR pc = frame_pc_unwind (next_frame); - CORE_ADDR start_addr; +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; - find_pc_partial_function (pc, NULL, &start_addr, NULL); - if (start_addr == 0) - start_addr = heuristic_proc_start (pc); - /* We can't analyze the prologue if we couldn't find the begining - of the function. */ - if (start_addr == 0) - return cache; + int extend_bytes = 0; + int prev_extend_bytes; + CORE_ADDR end_prologue_addr = 0; - mips32_scan_prologue (start_addr, pc, next_frame, *this_cache); - } - - /* SP_REGNUM, contains the value and not the address. */ - trad_frame_set_value (cache->saved_regs, NUM_REGS + MIPS_SP_REGNUM, cache->base); + /* 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; - return (*this_cache); -} + if (limit_pc > start_pc + 200) + limit_pc = start_pc + 200; -static void -mips_insn32_frame_this_id (struct frame_info *next_frame, void **this_cache, - struct frame_id *this_id) -{ - struct mips_frame_cache *info = mips_insn32_frame_cache (next_frame, - this_cache); - (*this_id) = frame_id_build (info->base, frame_func_unwind (next_frame)); -} + 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; -static void -mips_insn32_frame_prev_register (struct frame_info *next_frame, - void **this_cache, - int regnum, int *optimizedp, - enum lval_type *lvalp, CORE_ADDR *addrp, - int *realnump, void *valuep) -{ - struct mips_frame_cache *info = mips_insn32_frame_cache (next_frame, - this_cache); - trad_frame_get_prev_register (next_frame, info->saved_regs, regnum, - optimizedp, lvalp, addrp, realnump, valuep); -} + /* Fetch and decode the instruction. */ + inst = (unsigned short) mips_fetch_instruction (cur_pc); -static const struct frame_unwind mips_insn32_frame_unwind = -{ - NORMAL_FRAME, - mips_insn32_frame_this_id, - mips_insn32_frame_prev_register -}; + /* 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; + } -static const struct frame_unwind * -mips_insn32_frame_sniffer (struct frame_info *next_frame) -{ - CORE_ADDR pc = frame_pc_unwind (next_frame); - if (! pc_is_mips16 (pc)) - return &mips_insn32_frame_unwind; - return NULL; -} + prev_extend_bytes = extend_bytes; + extend_bytes = 0; -static CORE_ADDR -mips_insn32_frame_base_address (struct frame_info *next_frame, - void **this_cache) -{ - struct mips_frame_cache *info = mips_insn32_frame_cache (next_frame, - this_cache); - return info->base; -} + 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; + } + } -static const struct frame_base mips_insn32_frame_base = -{ - &mips_insn32_frame_unwind, - mips_insn32_frame_base_address, - mips_insn32_frame_base_address, - mips_insn32_frame_base_address -}; + /* 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; -static const struct frame_base * -mips_insn32_frame_base_sniffer (struct frame_info *next_frame) -{ - if (mips_insn32_frame_sniffer (next_frame) != NULL) - return &mips_insn32_frame_base; - else - return NULL; + /* 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; } -static struct trad_frame_cache * -mips_stub_frame_cache (struct frame_info *next_frame, void **this_cache) +/* 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. */ + +static struct mips_frame_cache * +mips_insn16_frame_cache (struct frame_info *next_frame, void **this_cache) { - CORE_ADDR pc; - CORE_ADDR start_addr; - CORE_ADDR stack_addr; - struct trad_frame_cache *this_trad_cache; + struct mips_frame_cache *cache; if ((*this_cache) != NULL) return (*this_cache); - this_trad_cache = trad_frame_cache_zalloc (next_frame); - (*this_cache) = this_trad_cache; + cache = FRAME_OBSTACK_ZALLOC (struct mips_frame_cache); + (*this_cache) = cache; + cache->saved_regs = trad_frame_alloc_saved_regs (next_frame); - /* The return address is in the link register. */ - trad_frame_set_reg_realreg (this_trad_cache, PC_REGNUM, RA_REGNUM); + /* Analyze the function prologue. */ + { + const CORE_ADDR pc = frame_pc_unwind (next_frame); + CORE_ADDR start_addr; - /* Frame ID, since it's a frameless / stackless function, no stack - space is allocated and SP on entry is the current SP. */ - pc = frame_pc_unwind (next_frame); - find_pc_partial_function (pc, NULL, &start_addr, NULL); - stack_addr = frame_unwind_register_signed (next_frame, SP_REGNUM); - trad_frame_set_id (this_trad_cache, frame_id_build (start_addr, stack_addr)); + find_pc_partial_function (pc, NULL, &start_addr, NULL); + if (start_addr == 0) + start_addr = heuristic_proc_start (pc); + /* We can't analyze the prologue if we couldn't find the begining + of the function. */ + if (start_addr == 0) + return cache; - /* Assume that the frame's base is the same as the - stack-pointer. */ - trad_frame_set_this_base (this_trad_cache, stack_addr); + mips16_scan_prologue (start_addr, pc, next_frame, *this_cache); + } + + /* SP_REGNUM, contains the value and not the address. */ + trad_frame_set_value (cache->saved_regs, NUM_REGS + MIPS_SP_REGNUM, cache->base); - return this_trad_cache; + return (*this_cache); } static void -mips_stub_frame_this_id (struct frame_info *next_frame, void **this_cache, - struct frame_id *this_id) +mips_insn16_frame_this_id (struct frame_info *next_frame, void **this_cache, + struct frame_id *this_id) { - struct trad_frame_cache *this_trad_cache - = mips_stub_frame_cache (next_frame, this_cache); - trad_frame_get_id (this_trad_cache, this_id); + struct mips_frame_cache *info = mips_insn16_frame_cache (next_frame, + this_cache); + (*this_id) = frame_id_build (info->base, frame_func_unwind (next_frame)); } static void -mips_stub_frame_prev_register (struct frame_info *next_frame, +mips_insn16_frame_prev_register (struct frame_info *next_frame, void **this_cache, int regnum, int *optimizedp, enum lval_type *lvalp, CORE_ADDR *addrp, int *realnump, void *valuep) { - struct trad_frame_cache *this_trad_cache - = mips_stub_frame_cache (next_frame, this_cache); - trad_frame_get_register (this_trad_cache, next_frame, regnum, optimizedp, - lvalp, addrp, realnump, valuep); + struct mips_frame_cache *info = mips_insn16_frame_cache (next_frame, + this_cache); + trad_frame_get_prev_register (next_frame, info->saved_regs, regnum, + optimizedp, lvalp, addrp, realnump, valuep); } -static const struct frame_unwind mips_stub_frame_unwind = +static const struct frame_unwind mips_insn16_frame_unwind = { NORMAL_FRAME, - mips_stub_frame_this_id, - mips_stub_frame_prev_register + mips_insn16_frame_this_id, + mips_insn16_frame_prev_register }; static const struct frame_unwind * -mips_stub_frame_sniffer (struct frame_info *next_frame) +mips_insn16_frame_sniffer (struct frame_info *next_frame) { CORE_ADDR pc = frame_pc_unwind (next_frame); - if (in_plt_section (pc, NULL)) - return &mips_stub_frame_unwind; - else - return NULL; + if (pc_is_mips16 (pc)) + return &mips_insn16_frame_unwind; + return NULL; } static CORE_ADDR -mips_stub_frame_base_address (struct frame_info *next_frame, - void **this_cache) +mips_insn16_frame_base_address (struct frame_info *next_frame, + void **this_cache) { - struct trad_frame_cache *this_trad_cache - = mips_stub_frame_cache (next_frame, this_cache); - return trad_frame_get_this_base (this_trad_cache); + struct mips_frame_cache *info = mips_insn16_frame_cache (next_frame, + this_cache); + return info->base; } -static const struct frame_base mips_stub_frame_base = +static const struct frame_base mips_insn16_frame_base = { - &mips_stub_frame_unwind, - mips_stub_frame_base_address, - mips_stub_frame_base_address, - mips_stub_frame_base_address + &mips_insn16_frame_unwind, + mips_insn16_frame_base_address, + mips_insn16_frame_base_address, + mips_insn16_frame_base_address }; static const struct frame_base * -mips_stub_frame_base_sniffer (struct frame_info *next_frame) +mips_insn16_frame_base_sniffer (struct frame_info *next_frame) { - if (mips_stub_frame_sniffer (next_frame) != NULL) - return &mips_stub_frame_base; + if (mips_insn16_frame_sniffer (next_frame) != NULL) + return &mips_insn16_frame_base; else return NULL; } -static CORE_ADDR -read_next_frame_reg (struct frame_info *fi, int regno) +/* 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) { - /* Always a pseudo. */ - gdb_assert (regno >= NUM_REGS); - if (fi == NULL) - { - LONGEST val; - regcache_cooked_read_signed (current_regcache, regno, &val); - return val; - } - else - return frame_unwind_register_signed (fi, regno); + 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; + } + } } -/* mips_addr_bits_remove - remove useless address bits */ +/* 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 -mips_addr_bits_remove (CORE_ADDR addr) +mips32_scan_prologue (CORE_ADDR start_pc, CORE_ADDR limit_pc, + struct frame_info *next_frame, + struct mips_frame_cache *this_cache) { - struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); - if (mips_mask_address_p (tdep) && (((ULONGEST) addr) >> 32 == 0xffffffffUL)) - /* This hack is a work-around for existing boards using PMON, the - simulator, and any other 64-bit targets that doesn't have true - 64-bit addressing. On these targets, the upper 32 bits of - addresses are ignored by the hardware. Thus, the PC or SP are - likely to have been sign extended to all 1s by instruction - sequences that load 32-bit addresses. For example, a typical - piece of code that loads an address is this: + 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; - lui $r2, - ori $r2, + CORE_ADDR end_prologue_addr = 0; + int seen_sp_adjust = 0; + int load_immediate_bytes = 0; - But the lui sign-extends the value such that the upper 32 bits - may be all 1s. The workaround is simply to mask off these - bits. In the future, gcc may be changed to support true 64-bit - addressing, and this masking will have to be disabled. */ - return addr &= 0xffffffffUL; + /* 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 - return addr; -} - -/* mips_software_single_step() is called just before we want to resume - the inferior, if we want to single-step it but there is no hardware - or kernel single-step support (MIPS on GNU/Linux for example). We find - the target of the coming instruction and breakpoint it. + sp = 0; - single_step is also called just after the inferior stops. If we had - set up a simulated single-step, we undo our damage. */ + if (limit_pc > start_pc + 200) + limit_pc = start_pc + 200; -void -mips_software_single_step (enum target_signal sig, int insert_breakpoints_p) -{ - static CORE_ADDR next_pc; - typedef char binsn_quantum[BREAKPOINT_MAX]; - static binsn_quantum break_mem; - CORE_ADDR pc; +restart: - if (insert_breakpoints_p) + frame_offset = 0; + for (cur_pc = start_pc; cur_pc < limit_pc; cur_pc += MIPS_INSTLEN) { - pc = read_register (mips_regnum (current_gdbarch)->pc); - next_pc = mips_next_pc (pc); + unsigned long inst, high_word, low_word; + int reg; - target_insert_breakpoint (next_pc, break_mem); - } - else - target_remove_breakpoint (next_pc, break_mem); -} + /* Fetch the instruction. */ + inst = (unsigned long) mips_fetch_instruction (cur_pc); -static struct mips_extra_func_info temp_proc_desc; + /* Save some code by pre-extracting some useful fields. */ + high_word = (inst >> 16) & 0xffff; + low_word = inst & 0xffff; + reg = high_word & 0x1f; -/* 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. + 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; - 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. */ + 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; -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; + 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]; + } -/* Test whether the PC points to the return instruction at the - end of a function. */ + /* 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; -static int -mips_about_to_return (CORE_ADDR pc) -{ - if (pc_is_mips16 (pc)) - /* This mips16 case isn't necessarily reliable. Sometimes the compiler - generates a "jr $ra"; other times it generates code to load - the return address from the stack to an accessible register (such - as $a3), then a "jr" using that register. This second case - is almost impossible to distinguish from an indirect jump - used for switch statements, so we don't even try. */ - return mips_fetch_instruction (pc) == 0xe820; /* jr $ra */ - else - return mips_fetch_instruction (pc) == 0x3e00008; /* jr $ra */ + 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 + unwinder. */ -/* This fencepost looks highly suspicious to me. Removing it also - seems suspicious as it could affect remote debugging across serial - lines. */ - -static CORE_ADDR -heuristic_proc_start (CORE_ADDR pc) +static struct mips_frame_cache * +mips_insn32_frame_cache (struct frame_info *next_frame, void **this_cache) { - CORE_ADDR start_pc; - CORE_ADDR fence; - int instlen; - int seen_adjsp = 0; - - pc = ADDR_BITS_REMOVE (pc); - start_pc = pc; - fence = start_pc - heuristic_fence_post; - if (start_pc == 0) - return 0; - - if (heuristic_fence_post == UINT_MAX || fence < VM_MIN_ADDRESS) - fence = VM_MIN_ADDRESS; - - instlen = pc_is_mips16 (pc) ? MIPS16_INSTLEN : MIPS_INSTLEN; + struct mips_frame_cache *cache; - /* search back for previous return */ - for (start_pc -= instlen;; start_pc -= instlen) - if (start_pc < fence) - { - /* It's not clear to me why we reach this point when - stop_soon, but with this test, at least we - don't print out warnings for every child forked (eg, on - decstation). 22apr93 rich@cygnus.com. */ - if (stop_soon == NO_STOP_QUIETLY) - { - static int blurb_printed = 0; + if ((*this_cache) != NULL) + return (*this_cache); - warning ("GDB can't find the start of the function at 0x%s.", - paddr_nz (pc)); + cache = FRAME_OBSTACK_ZALLOC (struct mips_frame_cache); + (*this_cache) = cache; + cache->saved_regs = trad_frame_alloc_saved_regs (next_frame); - if (!blurb_printed) - { - /* This actually happens frequently in embedded - development, when you first connect to a board - and your stack pointer and pc are nowhere in - particular. This message needs to give people - in that situation enough information to - determine that it's no big deal. */ - printf_filtered ("\n\ - GDB is unable to find the start of the function at 0x%s\n\ -and thus can't determine the size of that function's stack frame.\n\ -This means that GDB may be unable to access that stack frame, or\n\ -the frames below it.\n\ - This problem is most likely caused by an invalid program counter or\n\ -stack pointer.\n\ - However, if you think GDB should simply search farther back\n\ -from 0x%s for code which looks like the beginning of a\n\ -function, you can increase the range of the search using the `set\n\ -heuristic-fence-post' command.\n", paddr_nz (pc), paddr_nz (pc)); - blurb_printed = 1; - } - } + /* Analyze the function prologue. */ + { + const CORE_ADDR pc = frame_pc_unwind (next_frame); + CORE_ADDR start_addr; - return 0; - } - else if (pc_is_mips16 (start_pc)) - { - unsigned short inst; + find_pc_partial_function (pc, NULL, &start_addr, NULL); + if (start_addr == 0) + start_addr = heuristic_proc_start (pc); + /* We can't analyze the prologue if we couldn't find the begining + of the function. */ + if (start_addr == 0) + return cache; - /* On MIPS16, any one of the following is likely to be the - start of a function: - entry - addiu sp,-n - daddiu sp,-n - extend -n followed by 'addiu sp,+n' or 'daddiu sp,+n' */ - inst = mips_fetch_instruction (start_pc); - if (((inst & 0xf81f) == 0xe809 && (inst & 0x700) != 0x700) /* entry */ - || (inst & 0xff80) == 0x6380 /* addiu sp,-n */ - || (inst & 0xff80) == 0xfb80 /* daddiu sp,-n */ - || ((inst & 0xf810) == 0xf010 && seen_adjsp)) /* extend -n */ - break; - else if ((inst & 0xff00) == 0x6300 /* addiu sp */ - || (inst & 0xff00) == 0xfb00) /* daddiu sp */ - seen_adjsp = 1; - else - seen_adjsp = 0; - } - else if (mips_about_to_return (start_pc)) - { - start_pc += 2 * MIPS_INSTLEN; /* skip return, and its delay slot */ - break; - } + mips32_scan_prologue (start_addr, pc, next_frame, *this_cache); + } + + /* SP_REGNUM, contains the value and not the address. */ + trad_frame_set_value (cache->saved_regs, NUM_REGS + MIPS_SP_REGNUM, cache->base); - return start_pc; + return (*this_cache); } -/* 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? */ +static void +mips_insn32_frame_this_id (struct frame_info *next_frame, void **this_cache, + struct frame_id *this_id) { - 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; + struct mips_frame_cache *info = mips_insn32_frame_cache (next_frame, + this_cache); + (*this_id) = frame_id_build (info->base, frame_func_unwind (next_frame)); +} - offset = inst & mask; - if (is_signed && (offset & sign_bit)) - offset = 0 - (max_imm - offset); - return offset * scale; - } +static void +mips_insn32_frame_prev_register (struct frame_info *next_frame, + void **this_cache, + int regnum, int *optimizedp, + enum lval_type *lvalp, CORE_ADDR *addrp, + int *realnump, void *valuep) +{ + struct mips_frame_cache *info = mips_insn32_frame_cache (next_frame, + this_cache); + trad_frame_get_prev_register (next_frame, info->saved_regs, regnum, + optimizedp, lvalp, addrp, realnump, valuep); } +static const struct frame_unwind mips_insn32_frame_unwind = +{ + NORMAL_FRAME, + mips_insn32_frame_this_id, + mips_insn32_frame_prev_register +}; -/* 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 const struct frame_unwind * +mips_insn32_frame_sniffer (struct frame_info *next_frame) +{ + CORE_ADDR pc = frame_pc_unwind (next_frame); + if (! pc_is_mips16 (pc)) + return &mips_insn32_frame_unwind; + return NULL; +} 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) +mips_insn32_frame_base_address (struct frame_info *next_frame, + void **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; + struct mips_frame_cache *info = mips_insn32_frame_cache (next_frame, + this_cache); + return info->base; +} - int extend_bytes = 0; - int prev_extend_bytes; - CORE_ADDR end_prologue_addr = 0; +static const struct frame_base mips_insn32_frame_base = +{ + &mips_insn32_frame_unwind, + mips_insn32_frame_base_address, + mips_insn32_frame_base_address, + mips_insn32_frame_base_address +}; - /* 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); +static const struct frame_base * +mips_insn32_frame_base_sniffer (struct frame_info *next_frame) +{ + if (mips_insn32_frame_sniffer (next_frame) != NULL) + return &mips_insn32_frame_base; else - sp = 0; - - if (limit_pc > start_pc + 200) - limit_pc = start_pc + 200; + return NULL; +} - 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; +static struct trad_frame_cache * +mips_stub_frame_cache (struct frame_info *next_frame, void **this_cache) +{ + CORE_ADDR pc; + CORE_ADDR start_addr; + CORE_ADDR stack_addr; + struct trad_frame_cache *this_trad_cache; - /* Fetch and decode the instruction. */ - inst = (unsigned short) mips_fetch_instruction (cur_pc); + if ((*this_cache) != NULL) + return (*this_cache); + this_trad_cache = trad_frame_cache_zalloc (next_frame); + (*this_cache) = this_trad_cache; - /* 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; - } + /* The return address is in the link register. */ + trad_frame_set_reg_realreg (this_trad_cache, PC_REGNUM, RA_REGNUM); - prev_extend_bytes = extend_bytes; - extend_bytes = 0; + /* Frame ID, since it's a frameless / stackless function, no stack + space is allocated and SP on entry is the current SP. */ + pc = frame_pc_unwind (next_frame); + find_pc_partial_function (pc, NULL, &start_addr, NULL); + stack_addr = frame_unwind_register_signed (next_frame, SP_REGNUM); + trad_frame_set_id (this_trad_cache, frame_id_build (start_addr, stack_addr)); - 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; - } - } + /* Assume that the frame's base is the same as the + stack-pointer. */ + trad_frame_set_this_base (this_trad_cache, stack_addr); - /* 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; + return this_trad_cache; +} - /* The entry instruction always subtracts 32 from the SP. */ - frame_offset += 32; +static void +mips_stub_frame_this_id (struct frame_info *next_frame, void **this_cache, + struct frame_id *this_id) +{ + struct trad_frame_cache *this_trad_cache + = mips_stub_frame_cache (next_frame, this_cache); + trad_frame_get_id (this_trad_cache, this_id); +} - /* Now we can calculate what the SP must have been at the - start of the function prologue. */ - sp += frame_offset; +static void +mips_stub_frame_prev_register (struct frame_info *next_frame, + void **this_cache, + int regnum, int *optimizedp, + enum lval_type *lvalp, CORE_ADDR *addrp, + int *realnump, void *valuep) +{ + struct trad_frame_cache *this_trad_cache + = mips_stub_frame_cache (next_frame, this_cache); + trad_frame_get_register (this_trad_cache, next_frame, regnum, optimizedp, + lvalp, addrp, realnump, valuep); +} - /* 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); - } +static const struct frame_unwind mips_stub_frame_unwind = +{ + NORMAL_FRAME, + mips_stub_frame_this_id, + mips_stub_frame_prev_register +}; - /* 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); - } +static const struct frame_unwind * +mips_stub_frame_sniffer (struct frame_info *next_frame) +{ + CORE_ADDR pc = frame_pc_unwind (next_frame); + if (in_plt_section (pc, NULL)) + return &mips_stub_frame_unwind; + else + return NULL; +} - /* 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); - } - } +static CORE_ADDR +mips_stub_frame_base_address (struct frame_info *next_frame, + void **this_cache) +{ + struct trad_frame_cache *this_trad_cache + = mips_stub_frame_cache (next_frame, this_cache); + return trad_frame_get_this_base (this_trad_cache); +} - if (this_cache != NULL) +static const struct frame_base mips_stub_frame_base = +{ + &mips_stub_frame_unwind, + mips_stub_frame_base_address, + mips_stub_frame_base_address, + mips_stub_frame_base_address +}; + +static const struct frame_base * +mips_stub_frame_base_sniffer (struct frame_info *next_frame) +{ + if (mips_stub_frame_sniffer (next_frame) != NULL) + return &mips_stub_frame_base; + else + return NULL; +} + +static CORE_ADDR +read_next_frame_reg (struct frame_info *fi, int regno) +{ + /* Always a pseudo. */ + gdb_assert (regno >= NUM_REGS); + if (fi == 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]; + LONGEST val; + regcache_cooked_read_signed (current_regcache, regno, &val); + return val; } + else + return frame_unwind_register_signed (fi, regno); - /* 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. */ +/* mips_addr_bits_remove - remove useless address bits */ -void -reset_saved_regs (struct mips_frame_cache *this_cache) +static CORE_ADDR +mips_addr_bits_remove (CORE_ADDR addr) { - if (this_cache == NULL || this_cache->saved_regs == NULL) - return; + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); + if (mips_mask_address_p (tdep) && (((ULONGEST) addr) >> 32 == 0xffffffffUL)) + /* This hack is a work-around for existing boards using PMON, the + simulator, and any other 64-bit targets that doesn't have true + 64-bit addressing. On these targets, the upper 32 bits of + addresses are ignored by the hardware. Thus, the PC or SP are + likely to have been sign extended to all 1s by instruction + sequences that load 32-bit addresses. For example, a typical + piece of code that loads an address is this: - { - const int num_regs = NUM_REGS; - int i; + lui $r2, + ori $r2, - for (i = 0; i < num_regs; i++) - { - this_cache->saved_regs[i].addr = -1; - } - } + But the lui sign-extends the value such that the upper 32 bits + may be all 1s. The workaround is simply to mask off these + bits. In the future, gcc may be changed to support true 64-bit + addressing, and this masking will have to be disabled. */ + return addr &= 0xffffffffUL; + else + return addr; } -/* 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. */ +/* mips_software_single_step() is called just before we want to resume + the inferior, if we want to single-step it but there is no hardware + or kernel single-step support (MIPS on GNU/Linux for example). We find + the target of the coming instruction and breakpoint it. -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) + single_step is also called just after the inferior stops. If we had + set up a simulated single-step, we undo our damage. */ + +void +mips_software_single_step (enum target_signal sig, int insert_breakpoints_p) { - 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; + static CORE_ADDR next_pc; + typedef char binsn_quantum[BREAKPOINT_MAX]; + static binsn_quantum break_mem; + CORE_ADDR pc; - CORE_ADDR end_prologue_addr = 0; - int seen_sp_adjust = 0; - int load_immediate_bytes = 0; + if (insert_breakpoints_p) + { + pc = read_register (mips_regnum (current_gdbarch)->pc); + next_pc = mips_next_pc (pc); - /* 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); + target_insert_breakpoint (next_pc, break_mem); + } else - sp = 0; + target_remove_breakpoint (next_pc, break_mem); +} - if (limit_pc > start_pc + 200) - limit_pc = start_pc + 200; +static struct mips_extra_func_info temp_proc_desc; -restart: +/* Test whether the PC points to the return instruction at the + end of a function. */ - 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; +static int +mips_about_to_return (CORE_ADDR pc) +{ + if (pc_is_mips16 (pc)) + /* This mips16 case isn't necessarily reliable. Sometimes the compiler + generates a "jr $ra"; other times it generates code to load + the return address from the stack to an accessible register (such + as $a3), then a "jr" using that register. This second case + is almost impossible to distinguish from an indirect jump + used for switch statements, so we don't even try. */ + return mips_fetch_instruction (pc) == 0xe820; /* jr $ra */ + else + return mips_fetch_instruction (pc) == 0x3e00008; /* jr $ra */ +} - /* 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; +/* This fencepost looks highly suspicious to me. Removing it also + seems suspicious as it could affect remote debugging across serial + lines. */ - 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; +static CORE_ADDR +heuristic_proc_start (CORE_ADDR pc) +{ + CORE_ADDR start_pc; + CORE_ADDR fence; + int instlen; + int seen_adjsp = 0; - 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; + pc = ADDR_BITS_REMOVE (pc); + start_pc = pc; + fence = start_pc - heuristic_fence_post; + if (start_pc == 0) + return 0; - 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 (heuristic_fence_post == UINT_MAX || fence < VM_MIN_ADDRESS) + fence = VM_MIN_ADDRESS; - 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]; - } + instlen = pc_is_mips16 (pc) ? MIPS16_INSTLEN : MIPS_INSTLEN; - /* 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; + /* search back for previous return */ + for (start_pc -= instlen;; start_pc -= instlen) + if (start_pc < fence) + { + /* It's not clear to me why we reach this point when + stop_soon, but with this test, at least we + don't print out warnings for every child forked (eg, on + decstation). 22apr93 rich@cygnus.com. */ + if (stop_soon == NO_STOP_QUIETLY) + { + static int blurb_printed = 0; - return end_prologue_addr; + warning ("GDB can't find the start of the function at 0x%s.", + paddr_nz (pc)); + + if (!blurb_printed) + { + /* This actually happens frequently in embedded + development, when you first connect to a board + and your stack pointer and pc are nowhere in + particular. This message needs to give people + in that situation enough information to + determine that it's no big deal. */ + printf_filtered ("\n\ + GDB is unable to find the start of the function at 0x%s\n\ +and thus can't determine the size of that function's stack frame.\n\ +This means that GDB may be unable to access that stack frame, or\n\ +the frames below it.\n\ + This problem is most likely caused by an invalid program counter or\n\ +stack pointer.\n\ + However, if you think GDB should simply search farther back\n\ +from 0x%s for code which looks like the beginning of a\n\ +function, you can increase the range of the search using the `set\n\ +heuristic-fence-post' command.\n", paddr_nz (pc), paddr_nz (pc)); + blurb_printed = 1; + } + } + + return 0; + } + else if (pc_is_mips16 (start_pc)) + { + unsigned short inst; + + /* On MIPS16, any one of the following is likely to be the + start of a function: + entry + addiu sp,-n + daddiu sp,-n + extend -n followed by 'addiu sp,+n' or 'daddiu sp,+n' */ + inst = mips_fetch_instruction (start_pc); + if (((inst & 0xf81f) == 0xe809 && (inst & 0x700) != 0x700) /* entry */ + || (inst & 0xff80) == 0x6380 /* addiu sp,-n */ + || (inst & 0xff80) == 0xfb80 /* daddiu sp,-n */ + || ((inst & 0xf810) == 0xf010 && seen_adjsp)) /* extend -n */ + break; + else if ((inst & 0xff00) == 0x6300 /* addiu sp */ + || (inst & 0xff00) == 0xfb00) /* daddiu sp */ + seen_adjsp = 1; + else + seen_adjsp = 0; + } + else if (mips_about_to_return (start_pc)) + { + start_pc += 2 * MIPS_INSTLEN; /* skip return, and its delay slot */ + break; + } + + return start_pc; } static mips_extra_func_info_t