/* Target-dependent code for the MIPS architecture, for GDB, the GNU Debugger.
- Copyright 1988, 1989, 1990, 1991, 1992, 1993, 1994
+ Copyright 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995
Free Software Foundation, Inc.
Contributed by Alessandro Forin(af@cs.cmu.edu) at CMU
and by Per Bothner(bothner@cs.wisc.edu) at U.Wisconsin.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include "defs.h"
+#include "gdb_string.h"
#include "frame.h"
#include "inferior.h"
#include "symtab.h"
static int mips_in_lenient_prologue PARAMS ((CORE_ADDR, CORE_ADDR));
#endif
+static void mips_print_register PARAMS ((int, int));
+
+static mips_extra_func_info_t
+heuristic_proc_desc PARAMS ((CORE_ADDR, CORE_ADDR, struct frame_info *));
+
+static CORE_ADDR heuristic_proc_start PARAMS ((CORE_ADDR));
+
+static int read_next_frame_reg PARAMS ((struct frame_info *, int));
+
+static void mips_set_fpu_command PARAMS ((char *, int,
+ struct cmd_list_element *));
+
+static void mips_show_fpu_command PARAMS ((char *, int,
+ struct cmd_list_element *));
+
void mips_set_processor_type_command PARAMS ((char *, int));
+int mips_set_processor_type PARAMS ((char *));
+
+static void mips_show_processor_type_command PARAMS ((char *, int));
+
static void reinit_frame_cache_sfunc PARAMS ((char *, int,
struct cmd_list_element *));
+static mips_extra_func_info_t
+ find_proc_desc PARAMS ((CORE_ADDR pc, struct frame_info *next_frame));
+
+static CORE_ADDR after_prologue PARAMS ((CORE_ADDR pc,
+ mips_extra_func_info_t proc_desc));
+
/* This value is the model of MIPS in use. It is derived from the value
of the PrID register. */
/* Some MIPS boards don't support floating point, so we permit the
user to turn it off. */
-int mips_fpu = 1;
+enum mips_fpu_type mips_fpu;
+
+static char *mips_fpu_string;
/* A set of original names, to be used when restoring back to generic
registers from a specific set. */
"", "", "ehi", "", "", "", "epc", "prid",
};
+/* Names of LSI 33k registers. */
+
+char *mips_lsi33k_reg_names[] = {
+ "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra",
+ "epc", "hi", "lo", "sr", "cause","badvaddr",
+ "dcic", "bpc", "bda", "", "", "", "", "",
+ "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "",
+ "", "", "", "",
+ "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "",
+};
+
struct {
char *name;
char **regnames;
{ "r3051", mips_r3051_reg_names },
{ "r3071", mips_r3081_reg_names },
{ "r3081", mips_r3081_reg_names },
+ { "lsi33k", mips_lsi33k_reg_names },
{ NULL, NULL }
};
} *linked_proc_desc_table = NULL;
\f
+
+/* This returns the PC of the first inst after the prologue. If we can't
+ find the prologue, then return 0. */
+
+static CORE_ADDR
+after_prologue (pc, proc_desc)
+ CORE_ADDR pc;
+ mips_extra_func_info_t proc_desc;
+{
+ struct symtab_and_line sal;
+ CORE_ADDR func_addr, func_end;
+
+ if (!proc_desc)
+ proc_desc = find_proc_desc (pc, NULL);
+
+ if (proc_desc)
+ {
+ /* If function is frameless, then we need to do it the hard way. I
+ strongly suspect that frameless always means prologueless... */
+ if (PROC_FRAME_REG (proc_desc) == SP_REGNUM
+ && PROC_FRAME_OFFSET (proc_desc) == 0)
+ return 0;
+ }
+
+ if (!find_pc_partial_function (pc, NULL, &func_addr, &func_end))
+ return 0; /* Unknown */
+
+ sal = find_pc_line (func_addr, 0);
+
+ if (sal.end < func_end)
+ return sal.end;
+
+ /* The line after the prologue is after the end of the function. In this
+ case, tell the caller to find the prologue the hard way. */
+
+ return 0;
+}
+
/* Guaranteed to set fci->saved_regs to some values (it never leaves it
NULL). */
obstack_alloc (&frame_cache_obstack, sizeof(struct frame_saved_regs));
memset (fci->saved_regs, 0, sizeof (struct frame_saved_regs));
+ /* If it is the frame for sigtramp, the saved registers are located
+ in a sigcontext structure somewhere on the stack.
+ If the stack layout for sigtramp changes we might have to change these
+ constants and the companion fixup_sigtramp in mdebugread.c */
+#ifndef SIGFRAME_BASE
+/* To satisfy alignment restrictions, sigcontext is located 4 bytes
+ above the sigtramp frame. */
+#define SIGFRAME_BASE 4
+#define SIGFRAME_PC_OFF (SIGFRAME_BASE + 2 * 4)
+#define SIGFRAME_REGSAVE_OFF (SIGFRAME_BASE + 3 * 4)
+#define SIGFRAME_FPREGSAVE_OFF (SIGFRAME_REGSAVE_OFF + 32 * 4 + 3 * 4)
+#endif
+#ifndef SIGFRAME_REG_SIZE
+#define SIGFRAME_REG_SIZE 4
+#endif
+ if (fci->signal_handler_caller)
+ {
+ for (ireg = 0; ireg < 32; ireg++)
+ {
+ reg_position = fci->frame + SIGFRAME_REGSAVE_OFF
+ + ireg * SIGFRAME_REG_SIZE;
+ fci->saved_regs->regs[ireg] = reg_position;
+ }
+ for (ireg = 0; ireg < 32; ireg++)
+ {
+ reg_position = fci->frame + SIGFRAME_FPREGSAVE_OFF
+ + ireg * SIGFRAME_REG_SIZE;
+ fci->saved_regs->regs[FP0_REGNUM + ireg] = reg_position;
+ }
+ fci->saved_regs->regs[PC_REGNUM] = fci->frame + SIGFRAME_PC_OFF;
+ return;
+ }
+
proc_desc = fci->proc_desc;
if (proc_desc == NULL)
/* I'm not sure how/whether this can happen. Normally when we can't
/* In a dummy frame we know exactly where things are saved. */
&& !PROC_DESC_IS_DUMMY (proc_desc)
+ /* Don't bother unless we are inside a function prologue. Outside the
+ prologue, we know where everything is. */
+
+ && in_prologue (fci->pc, PROC_LOW_ADDR (proc_desc))
+
/* Not sure exactly what kernel_trap means, but if it means
the kernel saves the registers without a prologue doing it,
we better not examine the prologue to see whether registers
struct frame_info *fi;
int regno;
{
- /* If it is the frame for sigtramp we have a complete sigcontext
- somewhere above the frame and we get the saved registers from there.
- If the stack layout for sigtramp changes we might have to change these
- constants and the companion fixup_sigtramp in mdebugread.c */
-#ifndef SIGFRAME_BASE
-/* To satisfy alignment restrictions the sigcontext is located 4 bytes
- above the sigtramp frame. */
-#define SIGFRAME_BASE 4
-#define SIGFRAME_PC_OFF (SIGFRAME_BASE + 2 * 4)
-#define SIGFRAME_REGSAVE_OFF (SIGFRAME_BASE + 3 * 4)
-#endif
-#ifndef SIGFRAME_REG_SIZE
-#define SIGFRAME_REG_SIZE 4
-#endif
for (; fi; fi = fi->next)
{
- if (fi->signal_handler_caller)
- {
- int offset;
- if (regno == PC_REGNUM) offset = SIGFRAME_PC_OFF;
- else if (regno < 32) offset = (SIGFRAME_REGSAVE_OFF
- + regno * SIGFRAME_REG_SIZE);
- else return 0;
- return read_memory_integer(fi->frame + offset, MIPS_REGSIZE);
- }
- else if (regno == SP_REGNUM) return fi->frame;
+ /* We have to get the saved sp from the sigcontext
+ if it is a signal handler frame. */
+ if (regno == SP_REGNUM && !fi->signal_handler_caller)
+ return fi->frame;
else
{
if (fi->saved_regs == NULL)
0, NULL);
}
+ /* If we never found a PDR for this function in symbol reading, then
+ examine prologues to find the information. */
+ if (sym && ((mips_extra_func_info_t) SYMBOL_VALUE (sym))->pdr.framereg == -1)
+ sym = NULL;
+
if (sym)
{
/* IF this is the topmost frame AND
if (proc_desc == &temp_proc_desc)
{
- fci->saved_regs = (struct frame_saved_regs*)
- obstack_alloc (&frame_cache_obstack,
- sizeof (struct frame_saved_regs));
- *fci->saved_regs = temp_saved_regs;
- fci->saved_regs->regs[PC_REGNUM] = fci->saved_regs->regs[RA_REGNUM];
+ char *name;
+
+ /* Do not set the saved registers for a sigtramp frame,
+ mips_find_saved_registers will do that for us.
+ We can't use fci->signal_handler_caller, it is not yet set. */
+ find_pc_partial_function (fci->pc, &name,
+ (CORE_ADDR *)NULL,(CORE_ADDR *)NULL);
+ if (!IN_SIGTRAMP (fci->pc, name))
+ {
+ fci->saved_regs = (struct frame_saved_regs*)
+ obstack_alloc (&frame_cache_obstack,
+ sizeof (struct frame_saved_regs));
+ *fci->saved_regs = temp_saved_regs;
+ fci->saved_regs->regs[PC_REGNUM]
+ = fci->saved_regs->regs[RA_REGNUM];
+ }
}
/* hack: if argument regs are saved, guess these contain args */
int fake_args = 0;
for (i = 0, m_arg = mips_args; i < nargs; i++, m_arg++) {
- value_ptr arg = value_arg_coerce (args[i]);
+ value_ptr arg = args[i];
m_arg->len = TYPE_LENGTH (VALUE_TYPE (arg));
/* This entire mips-specific routine is because doubles must be aligned
* on 8-byte boundaries. It still isn't quite right, because MIPS decided
#define GEN_REG_SAVE_COUNT 22
#define FLOAT_REG_SAVE_MASK MASK(0,19)
#define FLOAT_REG_SAVE_COUNT 20
+#define FLOAT_SINGLE_REG_SAVE_MASK \
+ ((1<<18)|(1<<16)|(1<<14)|(1<<12)|(1<<10)|(1<<8)|(1<<6)|(1<<4)|(1<<2)|(1<<0))
+#define FLOAT_SINGLE_REG_SAVE_COUNT 10
#define SPECIAL_REG_SAVE_COUNT 4
/*
* The registers we must save are all those not preserved across
* (low memory)
*/
PROC_REG_MASK(proc_desc) = GEN_REG_SAVE_MASK;
- PROC_FREG_MASK(proc_desc) = mips_fpu ? FLOAT_REG_SAVE_MASK : 0;
+ switch (mips_fpu)
+ {
+ case MIPS_FPU_DOUBLE:
+ PROC_FREG_MASK(proc_desc) = FLOAT_REG_SAVE_MASK;
+ break;
+ case MIPS_FPU_SINGLE:
+ PROC_FREG_MASK(proc_desc) = FLOAT_SINGLE_REG_SAVE_MASK;
+ break;
+ case MIPS_FPU_NONE:
+ PROC_FREG_MASK(proc_desc) = 0;
+ break;
+ }
PROC_REG_OFFSET(proc_desc) = /* offset of (Saved R31) from FP */
-sizeof(long) - 4 * SPECIAL_REG_SAVE_COUNT;
PROC_FREG_OFFSET(proc_desc) = /* offset of (Saved D18) from FP */
write_memory (sp - 8, buffer, REGISTER_RAW_SIZE (HI_REGNUM));
read_register_gen (LO_REGNUM, buffer);
write_memory (sp - 12, buffer, REGISTER_RAW_SIZE (LO_REGNUM));
- if (mips_fpu)
+ if (mips_fpu != MIPS_FPU_NONE)
read_register_gen (FCRCS_REGNUM, buffer);
else
memset (buffer, 0, REGISTER_RAW_SIZE (FCRCS_REGNUM));
write_memory (sp - 16, buffer, REGISTER_RAW_SIZE (FCRCS_REGNUM));
- sp -= 4 * (GEN_REG_SAVE_COUNT
- + (mips_fpu ? FLOAT_REG_SAVE_COUNT : 0)
- + SPECIAL_REG_SAVE_COUNT);
+ sp -= 4 * (GEN_REG_SAVE_COUNT + SPECIAL_REG_SAVE_COUNT);
+ if (mips_fpu == MIPS_FPU_DOUBLE)
+ sp -= 4 * FLOAT_REG_SAVE_COUNT;
+ else if (mips_fpu == MIPS_FPU_SINGLE)
+ sp -= 4 * FLOAT_SINGLE_REG_SAVE_COUNT;
write_register (SP_REGNUM, sp);
PROC_LOW_ADDR(proc_desc) = sp - CALL_DUMMY_SIZE + CALL_DUMMY_START_OFFSET;
PROC_HIGH_ADDR(proc_desc) = sp;
write_register (HI_REGNUM, read_memory_integer(new_sp - 8, 4));
write_register (LO_REGNUM, read_memory_integer(new_sp - 12, 4));
- if (mips_fpu)
+ if (mips_fpu != MIPS_FPU_NONE)
write_register (FCRCS_REGNUM, read_memory_integer(new_sp - 16, 4));
}
}
mips_print_register (regnum, all)
int regnum, all;
{
- unsigned char raw_buffer[MAX_REGISTER_RAW_SIZE];
- struct type *our_type =
- init_type (TYPE_CODE_INT,
- /* We will fill in the length for each register. */
- 0,
- TYPE_FLAG_UNSIGNED,
- NULL,
- NULL);
+ char raw_buffer[MAX_REGISTER_RAW_SIZE];
/* Get the data in raw format. */
if (read_relative_register_raw_bytes (regnum, raw_buffer))
/* If an even floating pointer register, also print as double. */
if (regnum >= FP0_REGNUM && regnum < FP0_REGNUM+32
- && !((regnum-FP0_REGNUM) & 1)) {
- char dbuffer[MAX_REGISTER_RAW_SIZE];
+ && !((regnum-FP0_REGNUM) & 1))
+ {
+ char dbuffer[MAX_REGISTER_RAW_SIZE];
- read_relative_register_raw_bytes (regnum, dbuffer);
- read_relative_register_raw_bytes (regnum+1, dbuffer+4);
+ read_relative_register_raw_bytes (regnum, dbuffer);
+ read_relative_register_raw_bytes (regnum+1, dbuffer+4);
#ifdef REGISTER_CONVERT_TO_TYPE
- REGISTER_CONVERT_TO_TYPE(regnum, builtin_type_double, dbuffer);
+ REGISTER_CONVERT_TO_TYPE(regnum, builtin_type_double, dbuffer);
#endif
- printf_filtered ("(d%d: ", regnum-FP0_REGNUM);
- val_print (builtin_type_double, dbuffer, 0,
- gdb_stdout, 0, 1, 0, Val_pretty_default);
- printf_filtered ("); ");
- }
+ printf_filtered ("(d%d: ", regnum-FP0_REGNUM);
+ val_print (builtin_type_double, dbuffer, 0,
+ gdb_stdout, 0, 1, 0, Val_pretty_default);
+ printf_filtered ("); ");
+ }
fputs_filtered (reg_names[regnum], gdb_stdout);
/* The problem with printing numeric register names (r26, etc.) is that
}
else
{
- int did_newline;
+ int did_newline = 0;
for (regnum = 0; regnum < NUM_REGS; )
{
int offset;
int seen_sp_adjust = 0;
int load_immediate_bytes = 0;
+ CORE_ADDR post_prologue_pc;
+
+ /* See if we can determine the end of the prologue via the symbol table.
+ If so, then return either PC, or the PC after the prologue, whichever
+ is greater. */
+
+ post_prologue_pc = after_prologue (pc, NULL);
+
+ if (post_prologue_pc != 0)
+ return max (pc, post_prologue_pc);
+
+ /* Can't determine prologue from the symbol table, need to examine
+ instructions. */
/* Skip the typical prologue instructions. These are the stack adjustment
instruction and the instructions that save registers on the stack
else if ((inst & 0xF3E00000) == 0xA3C00000 && (inst & 0x001F0000))
/* sx reg,n($s8) */
continue; /* reg != $zero */
- else if (inst == 0x03A0F021) /* move $s8,$sp */
+
+ /* move $s8,$sp. With different versions of gas this will be either
+ `addu $s8,$sp,$zero' or `or $s8,$sp,$zero'. Accept either. */
+ else if (inst == 0x03A0F021 || inst == 0x03a0f025)
continue;
+
else if ((inst & 0xFF9F07FF) == 0x00800021) /* move reg,$a0-$a3 */
continue;
else if ((inst & 0xffff0000) == 0x3c1c0000) /* lui $gp,n */
char *valbuf;
{
int regnum;
+ int offset = 0;
- regnum = TYPE_CODE (valtype) == TYPE_CODE_FLT && mips_fpu ? FP0_REGNUM : 2;
-
- memcpy (valbuf, regbuf + REGISTER_BYTE (regnum), TYPE_LENGTH (valtype));
+ regnum = 2;
+ if (TYPE_CODE (valtype) == TYPE_CODE_FLT
+ && (mips_fpu == MIPS_FPU_DOUBLE
+ || (mips_fpu == MIPS_FPU_SINGLE && TYPE_LENGTH (valtype) <= 4)))
+ regnum = FP0_REGNUM;
+
+ if (TARGET_BYTE_ORDER == BIG_ENDIAN
+ && TYPE_CODE (valtype) != TYPE_CODE_FLT
+ && TYPE_LENGTH (valtype) < REGISTER_RAW_SIZE (regnum))
+ offset = REGISTER_RAW_SIZE (regnum) - TYPE_LENGTH (valtype);
+
+ memcpy (valbuf, regbuf + REGISTER_BYTE (regnum) + offset,
+ TYPE_LENGTH (valtype));
#ifdef REGISTER_CONVERT_TO_TYPE
REGISTER_CONVERT_TO_TYPE(regnum, valtype, valbuf);
#endif
int regnum;
char raw_buffer[MAX_REGISTER_RAW_SIZE];
- regnum = TYPE_CODE (valtype) == TYPE_CODE_FLT && mips_fpu ? FP0_REGNUM : 2;
+ regnum = 2;
+ if (TYPE_CODE (valtype) == TYPE_CODE_FLT
+ && (mips_fpu == MIPS_FPU_DOUBLE
+ || (mips_fpu == MIPS_FPU_SINGLE && TYPE_LENGTH (valtype) <= 4)))
+ regnum = FP0_REGNUM;
+
memcpy(raw_buffer, valbuf, TYPE_LENGTH (valtype));
#ifdef REGISTER_CONVERT_FROM_TYPE
write_register_bytes(REGISTER_BYTE (regnum), raw_buffer, TYPE_LENGTH (valtype));
}
-/* These exist in mdebugread.c. */
-extern CORE_ADDR sigtramp_address, sigtramp_end;
-extern void fixup_sigtramp PARAMS ((void));
-
/* Exported procedure: Is PC in the signal trampoline code */
int
return (pc >= sigtramp_address && pc < sigtramp_end);
}
+/* Command to set FPU type. mips_fpu_string will have been set to the
+ user's argument. Set mips_fpu based on mips_fpu_string, and then
+ canonicalize mips_fpu_string. */
+
+/*ARGSUSED*/
+static void
+mips_set_fpu_command (args, from_tty, c)
+ char *args;
+ int from_tty;
+ struct cmd_list_element *c;
+{
+ char *err = NULL;
+
+ if (mips_fpu_string == NULL || *mips_fpu_string == '\0')
+ mips_fpu = MIPS_FPU_DOUBLE;
+ else if (strcasecmp (mips_fpu_string, "double") == 0
+ || strcasecmp (mips_fpu_string, "on") == 0
+ || strcasecmp (mips_fpu_string, "1") == 0
+ || strcasecmp (mips_fpu_string, "yes") == 0)
+ mips_fpu = MIPS_FPU_DOUBLE;
+ else if (strcasecmp (mips_fpu_string, "none") == 0
+ || strcasecmp (mips_fpu_string, "off") == 0
+ || strcasecmp (mips_fpu_string, "0") == 0
+ || strcasecmp (mips_fpu_string, "no") == 0)
+ mips_fpu = MIPS_FPU_NONE;
+ else if (strcasecmp (mips_fpu_string, "single") == 0)
+ mips_fpu = MIPS_FPU_SINGLE;
+ else
+ err = strsave (mips_fpu_string);
+
+ if (mips_fpu_string != NULL)
+ free (mips_fpu_string);
+
+ switch (mips_fpu)
+ {
+ case MIPS_FPU_DOUBLE:
+ mips_fpu_string = strsave ("double");
+ break;
+ case MIPS_FPU_SINGLE:
+ mips_fpu_string = strsave ("single");
+ break;
+ case MIPS_FPU_NONE:
+ mips_fpu_string = strsave ("none");
+ break;
+ }
+
+ if (err != NULL)
+ {
+ struct cleanup *cleanups = make_cleanup (free, err);
+ error ("Unknown FPU type `%s'. Use `double', `none', or `single'.",
+ err);
+ do_cleanups (cleanups);
+ }
+}
+
+static void
+mips_show_fpu_command (args, from_tty, c)
+ char *args;
+ int from_tty;
+ struct cmd_list_element *c;
+{
+}
+
/* Command to set the processor type. */
void
int i, j;
if (str == NULL)
- return;
+ return 0;
for (i = 0; mips_processor_type_table[i].name != NULL; ++i)
{
prid = read_register (PRID_REGNUM);
- if (prid & ~0xf == 0x700)
+ if ((prid & ~0xf) == 0x700)
return savestring ("r3041", strlen("r3041"));
return NULL;
reinit_frame_cache ();
}
+int
+gdb_print_insn_mips (memaddr, info)
+ bfd_vma memaddr;
+ disassemble_info *info;
+{
+ if (TARGET_BYTE_ORDER == BIG_ENDIAN)
+ return print_insn_big_mips (memaddr, info);
+ else
+ return print_insn_little_mips (memaddr, info);
+}
+
void
_initialize_mips_tdep ()
{
struct cmd_list_element *c;
+ tm_print_insn = gdb_print_insn_mips;
+
/* Let the user turn off floating point and set the fence post for
heuristic_proc_start. */
- add_show_from_set
- (add_set_cmd ("mipsfpu", class_support, var_boolean,
- (char *) &mips_fpu,
- "Set use of floating point coprocessor.\n\
-Turn off to avoid using floating point instructions when calling functions\n\
-or dealing with return values.", &setlist),
- &showlist);
+ c = add_set_cmd ("mipsfpu", class_support, var_string_noescape,
+ (char *) &mips_fpu_string,
+ "Set use of floating point coprocessor.\n\
+Set to `none' to avoid using floating point instructions when calling\n\
+functions or dealing with return values. Set to `single' to use only\n\
+single precision floating point as on the R4650. Set to `double' for\n\
+normal floating point support.",
+ &setlist);
+ c->function.sfunc = mips_set_fpu_command;
+ c = add_show_from_set (c, &showlist);
+ c->function.sfunc = mips_show_fpu_command;
+
+ mips_fpu = MIPS_FPU_DOUBLE;
+ mips_fpu_string = strsave ("double");
c = add_set_cmd ("processor", class_support, var_string_noescape,
(char *) &tmp_mips_processor_type,