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.
This commit is contained in:
Mark Alexander 1997-01-28 00:50:13 +00:00
parent 0192b099bd
commit f9f8c14b1a
3 changed files with 224 additions and 48 deletions

View File

@ -1,3 +1,25 @@
Mon Jan 27 14:31:52 1997 Mark Alexander <marka@cygnus.com>
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.
Mon Jan 27 10:34:03 1997 Jeffrey A Law (law@cygnus.com)
* tm-mn10200.h (NUM_REGS): Decrease to 12.

View File

@ -114,8 +114,23 @@ extern int in_sigtramp PARAMS ((CORE_ADDR, char *));
#define INNER_THAN <
#define BIG_ENDIAN 4321
/* Old-style breakpoint macros. */
#define BIG_BREAKPOINT {0, 0x5, 0, 0xd}
#define LITTLE_BREAKPOINT {0xd, 0, 0x5, 0}
#define MIPS16_BIG_BREAKPOINT {0xe8, 0xa5}
#define MIPS16_LITTLE_BREAKPOINT {0xa5, 0xe8}
/* BREAKPOINT_FROM_PC 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 the pc (if necessary) to
point to the actual memory location where the breakpoint should be
inserted. */
unsigned char *mips_breakpoint_from_pc PARAMS ((CORE_ADDR *pcptr, int *lenptr));
#define BREAKPOINT_FROM_PC(pcptr, lenptr) mips_breakpoint_from_pc(pcptr, lenptr)
/* Amount PC must be decremented by after a breakpoint.
This is often the number of bytes in BREAKPOINT
@ -125,7 +140,8 @@ extern int in_sigtramp PARAMS ((CORE_ADDR, char *));
/* Nonzero if instruction at PC is a return instruction. "j ra" on mips. */
#define ABOUT_TO_RETURN(pc) (read_memory_integer (pc, 4) == 0x3e00008)
int mips_about_to_return PARAMS ((CORE_ADDR pc));
#define ABOUT_TO_RETURN(pc) mips_about_to_return (pc)
/* Say how long (ordinary) registers are. This is a piece of bogosity
used in push_word and a few other places; REGISTER_RAW_SIZE is the

View File

@ -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 ()
{