aboutsummaryrefslogtreecommitdiff
path: root/gdb/mips-tdep.c
diff options
context:
space:
mode:
authorMark Alexander <marka@cygnus>1997-01-28 00:50:13 +0000
committerMark Alexander <marka@cygnus>1997-01-28 00:50:13 +0000
commitf9f8c14b1a642a7f463f0f0ce8175f232702b595 (patch)
tree69c6e423028b665c62d496e8508f9b28c5849381 /gdb/mips-tdep.c
parent0192b099bde982f3d5ef9a74cb8a5b0dae816f2a (diff)
downloadgdb-f9f8c14b1a642a7f463f0f0ce8175f232702b595.zip
gdb-f9f8c14b1a642a7f463f0f0ce8175f232702b595.tar.gz
gdb-f9f8c14b1a642a7f463f0f0ce8175f232702b595.tar.bz2
First set of changes for mips16:
* config/mips/tm-mips.h (MIPS16_BIG_BREAKPOINT, MIPS16_LITTLE_BREAKPOINT, BREAKPOINT_FROM_PC): Define. (ABOUT_TO_RETURN): Call new function mips_about_to_return. (mips_breakpoint_from_pc, mips_about_to_return): Declare. * mem-break.c (memory_breakpoint_from_pc): New function. (memory_insert_breakpoint, memory_remove_breakpoint): Use memory_breakpoint_from_pc to determine breakpoint contents and size. * target.h (memory_breakpoint_from_pc): Declare. * monitor.c (monitor_insert_breakpoint): Use memory_breakpoint_from_pc to determine size of breakpoint instruction. * mips-tdep.c (mips32_decode_reg_save, mips16_decode_reg_save): New helper functions for mips_find_saved_regs. (mips_find_saved_regs): Recognize mips16 prologues. (mips_addr_bits_remove): Strip off upper 32 bits of address when target CPU is 32 bits but CORE_ADDR is 64 bits. (mips_step_skips_delay): No branch delay slot on mips16. (gdb_print_insn_mips): Disassemble mips16 code. (mips_breakpoint_from_pc, mips_about_to_return): New functions.
Diffstat (limited to 'gdb/mips-tdep.c')
-rw-r--r--gdb/mips-tdep.c232
1 files changed, 185 insertions, 47 deletions
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index 6438cbd..72513c3 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -182,6 +182,9 @@ struct {
{ NULL, NULL }
};
+/* Table to translate MIPS16 register field to actual register number. */
+static int mips16_to_32_reg[8] = { 16, 17, 2, 3, 4, 5, 6, 7 };
+
/* Heuristic_proc_start may hunt through the text section for a long
time across a 2400 baud serial line. Allows the user to limit this
search. */
@@ -246,6 +249,62 @@ after_prologue (pc, proc_desc)
return 0;
}
+/* Decode a MIPS32 instruction that saves a register in the stack, and
+ set the appropriate bit in the general register mask or float register mask
+ to indicate which register is saved. This is a helper function
+ for mips_find_saved_regs. */
+
+static void
+mips32_decode_reg_save (inst, gen_mask, float_mask)
+ t_inst inst;
+ unsigned long *gen_mask;
+ unsigned long *float_mask;
+{
+ int reg;
+
+ if ((inst & 0xffe00000) == 0xafa00000 /* sw reg,n($sp) */
+ || (inst & 0xffe00000) == 0xafc00000 /* sw reg,n($r30) */
+ || (inst & 0xffe00000) == 0xffa00000) /* sd reg,n($sp) */
+ {
+ /* It might be possible to use the instruction to
+ find the offset, rather than the code below which
+ is based on things being in a certain order in the
+ frame, but figuring out what the instruction's offset
+ is relative to might be a little tricky. */
+ reg = (inst & 0x001f0000) >> 16;
+ *gen_mask |= (1 << reg);
+ }
+ else if ((inst & 0xffe00000) == 0xe7a00000 /* swc1 freg,n($sp) */
+ || (inst & 0xffe00000) == 0xe7c00000 /* swc1 freg,n($r30) */
+ || (inst & 0xffe00000) == 0xf7a00000)/* sdc1 freg,n($sp) */
+
+ {
+ reg = ((inst & 0x001f0000) >> 16);
+ *float_mask |= (1 << reg);
+ }
+}
+
+/* Decode a MIPS16 instruction that saves a register in the stack, and
+ set the appropriate bit in the general register or float register mask
+ to indicate which register is saved. This is a helper function
+ for mips_find_saved_regs. */
+
+static void
+mips16_decode_reg_save (inst, gen_mask)
+ t_inst inst;
+ unsigned long *gen_mask;
+{
+ if ((inst & 0xf800) == 0xd000 /* sw reg,n($sp) */
+ || (inst & 0xff00) == 0xf900) /* sd reg,n($sp) */
+ {
+ int reg = mips16_to_32_reg[(inst & 0xf00) >> 8];
+ *gen_mask |= (1 << reg);
+ }
+ else if ((inst & 0xff00) == 0x6200 /* sw $ra,n($sp) */
+ || (inst & 0xff00) == 0xfa00) /* sd $ra,n($sp) */
+ *gen_mask |= (1 << 31);
+}
+
/* Guaranteed to set fci->saved_regs to some values (it never leaves it
NULL). */
@@ -312,11 +371,11 @@ mips_find_saved_regs (fci)
gen_mask = kernel_trap ? 0xFFFFFFFF : PROC_REG_MASK(proc_desc);
float_mask = kernel_trap ? 0xFFFFFFFF : PROC_FREG_MASK(proc_desc);
- if (/* In any frame other than the innermost, we assume that all
- registers have been saved. This assumes that all register
- saves in a function happen before the first function
- call. */
- fci->next == NULL
+ if (/* In any frame other than the innermost or a frame interrupted by
+ a signal, we assume that all registers have been saved.
+ This assumes that all register saves in a function happen before
+ the first function call. */
+ (fci->next == NULL || fci->next->signal_handler_caller)
/* In a dummy frame we know exactly where things are saved. */
&& !PROC_DESC_IS_DUMMY (proc_desc)
@@ -339,50 +398,31 @@ mips_find_saved_regs (fci)
int status;
char buf[MIPS_INSTLEN];
t_inst inst;
+ int instlen;
/* Bitmasks; set if we have found a save for the register. */
unsigned long gen_save_found = 0;
unsigned long float_save_found = 0;
- for (addr = PROC_LOW_ADDR (proc_desc);
- addr < fci->pc /*&& (gen_mask != gen_save_found
- || float_mask != float_save_found)*/;
- addr += MIPS_INSTLEN)
+ if ((addr = PROC_LOW_ADDR (proc_desc)) & 1)
{
- status = read_memory_nobpt (addr, buf, MIPS_INSTLEN);
+ instlen = 2; /* MIPS16 */
+ addr &= ~1;
+ }
+ else
+ instlen = MIPS_INSTLEN; /* MIPS32 */
+
+ while (addr < fci->pc)
+ {
+ status = read_memory_nobpt (addr, buf, instlen);
if (status)
memory_error (status, addr);
- inst = extract_unsigned_integer (buf, MIPS_INSTLEN);
- if (/* sw reg,n($sp) */
- (inst & 0xffe00000) == 0xafa00000
-
- /* sw reg,n($r30) */
- || (inst & 0xffe00000) == 0xafc00000
-
- /* sd reg,n($sp) */
- || (inst & 0xffe00000) == 0xffa00000)
- {
- /* It might be possible to use the instruction to
- find the offset, rather than the code below which
- is based on things being in a certain order in the
- frame, but figuring out what the instruction's offset
- is relative to might be a little tricky. */
- int reg = (inst & 0x001f0000) >> 16;
- gen_save_found |= (1 << reg);
- }
- else if (/* swc1 freg,n($sp) */
- (inst & 0xffe00000) == 0xe7a00000
-
- /* swc1 freg,n($r30) */
- || (inst & 0xffe00000) == 0xe7c00000
-
- /* sdc1 freg,n($sp) */
- || (inst & 0xffe00000) == 0xf7a00000)
-
- {
- int reg = ((inst & 0x001f0000) >> 16);
- float_save_found |= (1 << reg);
- }
+ inst = extract_unsigned_integer (buf, instlen);
+ if (instlen == 2)
+ mips16_decode_reg_save (inst, &gen_save_found);
+ else
+ mips32_decode_reg_save (inst, &gen_save_found, &float_save_found);
+ addr += instlen;
}
gen_mask = gen_save_found;
float_mask = float_save_found;
@@ -469,6 +509,11 @@ mips_addr_bits_remove (addr)
addressing, and this masking will have to be disabled. */
addr &= (CORE_ADDR)0xffffffff;
}
+#else
+ /* Even when GDB is configured for some 32-bit targets (e.g. mips-elf),
+ BFD is configured to handle 64-bit targets, so CORE_ADDR is 64 bits.
+ So we still have to mask off useless bits from addresses. */
+ addr &= (CORE_ADDR)0xffffffff;
#endif
return addr;
@@ -1079,13 +1124,11 @@ mips_push_dummy_frame()
* Saved D18 (i.e. F19, F18)
* ...
* Saved D0 (i.e. F1, F0)
- * CALL_DUMMY (subroutine stub; see tm-mips.h)
- * Parameter build area (not yet implemented)
+ * Argument build area and stack arguments written via mips_push_arguments
* (low memory)
*/
/* Save special registers (PC, MMHI, MMLO, FPC_CSR) */
- write_register (PUSH_FP_REGNUM, sp);
PROC_FRAME_REG(proc_desc) = PUSH_FP_REGNUM;
PROC_FRAME_OFFSET(proc_desc) = 0;
mips_push_register (&sp, PC_REGNUM);
@@ -1109,9 +1152,10 @@ mips_push_dummy_frame()
if (PROC_FREG_MASK(proc_desc) & (1 << ireg))
mips_push_register (&sp, ireg + FP0_REGNUM);
- /* Update the stack pointer. Set the procedure's starting and ending
- addresses to point to the place on the stack where we'll be writing the
- dummy code (in mips_push_arguments). */
+ /* Update the frame pointer for the call dummy and the stack pointer.
+ Set the procedure's starting and ending addresses to point to the
+ call dummy address at the entry point. */
+ write_register (PUSH_FP_REGNUM, old_sp);
write_register (SP_REGNUM, sp);
PROC_LOW_ADDR(proc_desc) = CALL_DUMMY_ADDRESS();
PROC_HIGH_ADDR(proc_desc) = CALL_DUMMY_ADDRESS() + 4;
@@ -1308,6 +1352,10 @@ mips_step_skips_delay (pc)
{
char buf[MIPS_INSTLEN];
+ /* There is no branch delay slot on MIPS16. */
+ if (pc & 1)
+ return 0;
+
if (target_read_memory (pc, buf, MIPS_INSTLEN) != 0)
/* If error reading memory, guess that it is not a delayed branch. */
return 0;
@@ -1676,12 +1724,102 @@ gdb_print_insn_mips (memaddr, info)
bfd_vma memaddr;
disassemble_info *info;
{
+ mips_extra_func_info_t proc_desc;
+
+ /* Search for the function containing this address. Set the low bit
+ of the address when searching, in case we were given an even address
+ that is the start of a 16-bit function. If we didn't do this,
+ the search would fail because the symbol table says the function
+ starts at an odd address, i.e. 1 byte past the given address. */
+ proc_desc = find_proc_desc (memaddr | 1, NULL);
+
+ /* Make an attempt to determine if this is a 16-bit function. If
+ the procedure descriptor exists and the address therein is odd,
+ it's definitely a 16-bit function. Otherwise, we have to just
+ guess that if the address passed in is odd, it's 16-bits. */
+ if (proc_desc)
+ info->mach = PROC_LOW_ADDR (proc_desc) & 1 ? 16 : 0;
+ else
+ info->mach = memaddr & 1 ? 16 : 0;
+
+ /* Round down the instruction address to the appropriate boundary.
+ Save the amount rounded down and subtract it from the returned size of
+ the instruction so that the next time through the address won't
+ look bogus. */
+ memaddr &= (info->mach == 16 ? ~1 : ~3);
+
+ /* Call the appropriate disassembler based on the target endian-ness. */
if (TARGET_BYTE_ORDER == BIG_ENDIAN)
return print_insn_big_mips (memaddr, info);
else
return print_insn_little_mips (memaddr, info);
}
+/* This function implements the BREAKPOINT_FROM_PC macro. It uses the program
+ counter value to determine whether a 16- or 32-bit breakpoint should be
+ used. It returns a pointer to a string of bytes that encode a breakpoint
+ instruction, stores the length of the string to *lenptr, and adjusts pc
+ (if necessary) to point to the actual memory location where the
+ breakpoint should be inserted. */
+
+unsigned char *mips_breakpoint_from_pc (pcptr, lenptr)
+ CORE_ADDR *pcptr;
+ int *lenptr;
+{
+ if (TARGET_BYTE_ORDER == BIG_ENDIAN)
+ {
+ if (*pcptr & 1)
+ {
+ static char mips16_big_breakpoint[] = MIPS16_BIG_BREAKPOINT;
+ *pcptr &= ~1;
+ *lenptr = sizeof(mips16_big_breakpoint);
+ return mips16_big_breakpoint;
+ }
+ else
+ {
+ static char big_breakpoint[] = BIG_BREAKPOINT;
+ *lenptr = sizeof(big_breakpoint);
+ return big_breakpoint;
+ }
+ }
+ else
+ {
+ if (*pcptr & 1)
+ {
+ static char mips16_little_breakpoint[] = MIPS16_LITTLE_BREAKPOINT;
+ *pcptr &= ~1;
+ *lenptr = sizeof(mips16_little_breakpoint);
+ return mips16_little_breakpoint;
+ }
+ else
+ {
+ static char little_breakpoint[] = LITTLE_BREAKPOINT;
+ *lenptr = sizeof(little_breakpoint);
+ return little_breakpoint;
+ }
+ }
+}
+
+/* Test whether the PC points to the return instruction at the
+ end of a function. This implements the ABOUT_TO_RETURN macro. */
+
+int
+mips_about_to_return (pc)
+ CORE_ADDR pc;
+{
+ if (pc & 1)
+ /* 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 read_memory_integer (pc & ~1, 2) == 0xe820; /* jr $ra */
+ else
+ return read_memory_integer (pc, 4) == 0x3e00008; /* jr $ra */
+}
+
+
void
_initialize_mips_tdep ()
{