Added target function calls for SH, M32R and H8300.

Added some generic target-independant code for managing call-dummy frames.

Wed Nov 27 10:32:14 1996  Michael Snyder  <msnyder@cleaver.cygnus.com>

        * breakpoint.c: DELETE command will not delete CALL_DUMMY breakpoint.
        * blockframe.c: Add target-independant support for managing
                        CALL_DUMMY frames on the host side.
        * frame.h:      Declarations for generic CALL_DUMMY frame support.
        * h8300-tdep.c: Add target function calls using generic frame support.
        * config/h8300/tm-h8300.h: config for generic target function calls.
start-sanitize-m32r
        * m32r-tdep.c:  Add target function calls using generic frame support.
        * config/m32r/tm-m32r.h:   config for generic target function calls.
end-sanitize-m32r
        * sh-tdep.c:    Add target function calls using generic frame support.
        * config/sh/tm-sh.h:       config for generic target function calls.
start-sanitize-v850
        * v850-tdep.c:  Add target function calls using generic frame support.
        * config/v850/tm-v850.h:   config for generic target function calls.
end-sanitize-v850
        * valops.c:     ADD PUSH_RETURN_ADDRESS so that it doesn't have to be
                        done by PUSH_ARGUMENTS when there's no CALL_DUMMY.
This commit is contained in:
Michael Snyder
1996-11-27 19:10:07 +00:00
parent 3bb3fe44e0
commit dc1b349d39
11 changed files with 1453 additions and 772 deletions

View File

@ -1,6 +1,7 @@
/* Get info from stack frames;
convert between frames, blocks, functions and pc values.
Copyright 1986, 1987, 1988, 1989, 1991, 1994 Free Software Foundation, Inc.
Copyright 1986, 1987, 1988, 1989, 1991, 1994, 1995, 1996
Free Software Foundation, Inc.
This file is part of GDB.
@ -49,7 +50,8 @@ inside_entry_file (addr)
#if CALL_DUMMY_LOCATION == AT_ENTRY_POINT
/* Do not stop backtracing if the pc is in the call dummy
at the entry point. */
if (PC_IN_CALL_DUMMY (addr, 0, 0))
/* FIXME: Won't always work with zeros for the last two arguments */
if (PC_IN_CALL_DUMMY (addr, 0, 0))
return 0;
#endif
return (addr >= symfile_objfile -> ei.entry_file_lowpc &&
@ -68,22 +70,26 @@ int
inside_main_func (pc)
CORE_ADDR pc;
{
struct symbol *mainsym;
if (pc == 0)
return 1;
if (symfile_objfile == 0)
return 0;
if (symfile_objfile -> ei.main_func_lowpc == 0 &&
symfile_objfile -> ei.main_func_highpc == 0)
/* If the addr range is not set up at symbol reading time, set it up now.
This is for FRAME_CHAIN_VALID_ALTERNATE. I do this for coff, because
it is unable to set it up and symbol reading time. */
if (symfile_objfile -> ei.main_func_lowpc == INVALID_ENTRY_LOWPC &&
symfile_objfile -> ei.main_func_highpc == INVALID_ENTRY_HIGHPC)
{
struct symbol *mainsym;
mainsym = lookup_symbol ("main", NULL, VAR_NAMESPACE, NULL, NULL);
if (mainsym && SYMBOL_CLASS(mainsym) == LOC_BLOCK)
{
symfile_objfile->ei.main_func_lowpc = BLOCK_START (SYMBOL_BLOCK_VALUE (mainsym));
symfile_objfile->ei.main_func_highpc = BLOCK_END (SYMBOL_BLOCK_VALUE (mainsym));
}
}
return (symfile_objfile -> ei.main_func_lowpc <= pc &&
symfile_objfile -> ei.main_func_highpc > pc);
@ -108,6 +114,7 @@ CORE_ADDR pc;
#if CALL_DUMMY_LOCATION == AT_ENTRY_POINT
/* Do not stop backtracing if the pc is in the call dummy
at the entry point. */
/* FIXME: Won't always work with zeros for the last two arguments */
if (PC_IN_CALL_DUMMY (pc, 0, 0))
return 0;
#endif
@ -239,9 +246,10 @@ frameless_look_for_prologue (frame)
struct frame_info *frame;
{
CORE_ADDR func_start, after_prologue;
func_start = (get_pc_function_start (frame->pc) + FUNCTION_START_OFFSET);
func_start = get_pc_function_start (frame->pc);
if (func_start)
{
func_start += FUNCTION_START_OFFSET;
after_prologue = func_start;
#ifdef SKIP_PROLOGUE_FRAMELESS_P
/* This is faster, since only care whether there *is* a prologue,
@ -655,8 +663,8 @@ find_pc_partial_function (pc, name, address, endaddr)
#if defined SIGTRAMP_START
if (IN_SIGTRAMP (pc, (char *)NULL))
{
cache_pc_function_low = SIGTRAMP_START;
cache_pc_function_high = SIGTRAMP_END;
cache_pc_function_low = SIGTRAMP_START (pc);
cache_pc_function_high = SIGTRAMP_END (pc);
cache_pc_function_name = "<sigtramp>";
goto return_cached_value;
@ -738,25 +746,7 @@ find_pc_partial_function (pc, name, address, endaddr)
return 0;
}
/* See if we're in a transfer table for Sun shared libs.
Note the hack for Sun shared library transfer tables creates
problems for single stepping through the return path from a shared
library call if the return path includes trampoline code.
I don't really understand the reasoning behind the magic handling
for mst_trampoline symbols. */
#ifdef INHIBIT_SUNSOLIB_TRANSFER_TABLE_HACK
cache_pc_function_low = SYMBOL_VALUE_ADDRESS (msymbol);
#else
if (msymbol -> type == mst_text || msymbol -> type == mst_file_text)
cache_pc_function_low = SYMBOL_VALUE_ADDRESS (msymbol);
else
/* It is a transfer table for Sun shared libraries. */
cache_pc_function_low = pc - FUNCTION_START_OFFSET;
#endif
cache_pc_function_low = SYMBOL_VALUE_ADDRESS (msymbol);
cache_pc_function_name = SYMBOL_NAME (msymbol);
/* Use the lesser of the next minimal symbol, or the end of the section, as
@ -860,6 +850,283 @@ sigtramp_saved_pc (frame)
}
#endif /* SIGCONTEXT_PC_OFFSET */
/*
* DUMMY FRAMES
*
* The following code serves to maintain the dummy stack frames for
* inferior function calls (ie. when gdb calls into the inferior via
* call_function_by_hand). This code saves the machine state before
* the call in host memory, so it must maintain an independant stack
* and keep it consistant etc. I am attempting to make this code
* generic enough to be used by many targets.
*
* The cheapest and most generic way to do CALL_DUMMY on a new target
* is probably to define CALL_DUMMY to be empty, CALL_DUMMY_LENGTH to zero,
* and CALL_DUMMY_LOCATION to AT_ENTRY. Then you must remember to define
* PUSH_RETURN_ADDRESS, because there won't be a call instruction to do it.
*/
static struct dummy_frame *dummy_frame_stack = NULL;
/* Function: find_dummy_frame(pc, fp, sp)
Search the stack of dummy frames for one matching the given PC, FP and SP.
This is the work-horse for pc_in_call_dummy and read_register_dummy */
char *
generic_find_dummy_frame (pc, fp)
CORE_ADDR pc;
CORE_ADDR fp;
{
struct dummy_frame * dummyframe;
#ifdef NEED_TEXT_START_END
CORE_ADDR bkpt_address;
extern CORE_ADDR text_end;
#endif
#if CALL_DUMMY_LOCATION == AT_ENTRY_POINT
if (pc != entry_point_address ())
return 0;
#endif /* AT_ENTRY_POINT */
#if CALL_DUMMY_LOCATION == BEFORE_TEXT_END
bkpt_address = text_end - CALL_DUMMY_LENGTH + CALL_DUMMY_BREAKPOINT_OFFSET;
if (pc != bkpt_address)
return 0;
#endif /* BEFORE_TEXT_END */
#if CALL_DUMMY_LOCATION == AFTER_TEXT_END
bkpt_address = text_end + CALL_DUMMY_BREAKPOINT_OFFSET;
if (pc != bkpt_address)
return 0;
#endif /* AFTER_TEXT_END */
#if CALL_DUMMY_LOCATION == ON_STACK
/* compute the displacement from the CALL_DUMMY breakpoint
to the frame pointer */
if (1 INNER_THAN 2)
pc += CALL_DUMMY_LENGTH - CALL_DUMMY_BREAKPOINT_OFFSET;
else
pc += CALL_DUMMY_BREAKPOINT_OFFSET;
#endif /* ON_STACK */
for (dummyframe = dummy_frame_stack; dummyframe != NULL;
dummyframe = dummyframe->next)
if (fp == dummyframe->fp || fp == dummyframe->sp)
{
/* The frame in question lies between the saved fp and sp, inclusive */
#if CALL_DUMMY_LOCATION == ON_STACK
/* NOTE: a better way to do this might be simply to test whether
the pc lies between the saved (sp, fp) and CALL_DUMMY_LENGTH.
*/
if (pc == dummyframe->fp || pc == dummyframe->sp)
#endif /* ON_STACK */
return dummyframe->regs;
}
return 0;
}
/* Function: pc_in_call_dummy (pc, fp)
Return true if this is a dummy frame created by gdb for an inferior call */
int
generic_pc_in_call_dummy (pc, fp)
CORE_ADDR pc;
CORE_ADDR fp;
{
/* if find_dummy_frame succeeds, then PC is in a call dummy */
return (generic_find_dummy_frame (pc, fp) != 0);
}
/* Function: read_register_dummy
Find a saved register from before GDB calls a function in the inferior */
CORE_ADDR
generic_read_register_dummy (pc, fp, regno)
CORE_ADDR pc;
CORE_ADDR fp;
int regno;
{
char *dummy_regs = generic_find_dummy_frame (pc, fp);
if (dummy_regs)
return extract_address (&dummy_regs[REGISTER_BYTE (regno)],
REGISTER_RAW_SIZE(regno));
else
return 0;
}
/* Save all the registers on the dummy frame stack. Most ports save the
registers on the target stack. This results in lots of unnecessary memory
references, which are slow when debugging via a serial line. Instead, we
save all the registers internally, and never write them to the stack. The
registers get restored when the called function returns to the entry point,
where a breakpoint is laying in wait. */
void
generic_push_dummy_frame ()
{
struct dummy_frame *dummy_frame;
CORE_ADDR fp = (get_current_frame ())->frame;
/* check to see if there are stale dummy frames,
perhaps left over from when a longjump took us out of a
function that was called by the debugger */
dummy_frame = dummy_frame_stack;
while (dummy_frame)
if (dummy_frame->fp INNER_THAN fp) /* stale -- destroy! */
{
dummy_frame_stack = dummy_frame->next;
free (dummy_frame);
dummy_frame = dummy_frame_stack;
}
else
dummy_frame = dummy_frame->next;
dummy_frame = xmalloc (sizeof (struct dummy_frame));
dummy_frame->pc = read_register (PC_REGNUM);
dummy_frame->sp = read_register (SP_REGNUM);
dummy_frame->fp = fp;
read_register_bytes (0, dummy_frame->regs, REGISTER_BYTES);
dummy_frame->next = dummy_frame_stack;
dummy_frame_stack = dummy_frame;
}
/* Function: pop_dummy_frame
Restore the machine state from a saved dummy stack frame. */
void
generic_pop_dummy_frame ()
{
struct dummy_frame *dummy_frame = dummy_frame_stack;
/* FIXME: what if the first frame isn't the right one, eg..
because one call-by-hand function has done a longjmp into another one? */
if (!dummy_frame)
error ("Can't pop dummy frame!");
dummy_frame_stack = dummy_frame->next;
write_register_bytes (0, dummy_frame->regs, REGISTER_BYTES);
free (dummy_frame);
}
/* Function: frame_chain_valid
Returns true for a user frame or a call_function_by_hand dummy frame,
and false for the CRT0 start-up frame. Purpose is to terminate backtrace */
int
generic_frame_chain_valid (fp, fi)
CORE_ADDR fp;
struct frame_info *fi;
{
#if CALL_DUMMY_LOCATION == AT_ENTRY_POINT
if (PC_IN_CALL_DUMMY(FRAME_SAVED_PC(fi), fp, fp))
return 1; /* don't prune CALL_DUMMY frames */
else /* fall back to default algorithm (see frame.h) */
#endif
return (fp != 0 && !inside_entry_file (FRAME_SAVED_PC(fi)));
}
/* Function: get_saved_register
Find register number REGNUM relative to FRAME and put its (raw,
target format) contents in *RAW_BUFFER.
Set *OPTIMIZED if the variable was optimized out (and thus can't be
fetched). Note that this is never set to anything other than zero
in this implementation.
Set *LVAL to lval_memory, lval_register, or not_lval, depending on
whether the value was fetched from memory, from a register, or in a
strange and non-modifiable way (e.g. a frame pointer which was
calculated rather than fetched). We will use not_lval for values
fetched from generic dummy frames.
Set *ADDRP to the address, either in memory on as a REGISTER_BYTE
offset into the registers array. If the value is stored in a dummy
frame, set *ADDRP to zero.
To use this implementation, define a function called
"get_saved_register" in your target code, which simply passes all
of its arguments to this function.
The argument RAW_BUFFER must point to aligned memory. */
void
generic_get_saved_register (raw_buffer, optimized, addrp, frame, regnum, lval)
char *raw_buffer;
int *optimized;
CORE_ADDR *addrp;
struct frame_info *frame;
int regnum;
enum lval_type *lval;
{
CORE_ADDR addr;
struct frame_saved_regs fsr;
if (!target_has_registers)
error ("No registers.");
/* Normal systems don't optimize out things with register numbers. */
if (optimized != NULL)
*optimized = 0;
if (addrp) /* default assumption: not found in memory */
*addrp = 0;
/* Note: since the current frame's registers could only have been
saved by frames INTERIOR TO the current frame, we skip examining
the current frame itself: otherwise, we would be getting the
previous frame's registers which were saved by the current frame. */
while ((frame = frame->next) != NULL)
{
if (PC_IN_CALL_DUMMY (frame->pc, frame->frame, frame->frame))
{
if (lval) /* found it in a CALL_DUMMY frame */
*lval = not_lval;
if (raw_buffer)
memcpy (raw_buffer,
generic_find_dummy_frame (frame->pc, frame->frame) +
REGISTER_BYTE (regnum),
REGISTER_RAW_SIZE (regnum));
return;
}
FRAME_FIND_SAVED_REGS(frame, fsr);
if (fsr.regs[regnum] != 0)
{
if (lval) /* found it saved on the stack */
*lval = lval_memory;
if (regnum == SP_REGNUM)
{
if (raw_buffer) /* SP register treated specially */
store_address (raw_buffer, REGISTER_RAW_SIZE (regnum),
fsr.regs[regnum]);
}
else
{
if (addrp) /* any other register */
*addrp = fsr.regs[regnum];
if (raw_buffer)
read_memory (fsr.regs[regnum], raw_buffer,
REGISTER_RAW_SIZE (regnum));
}
return;
}
}
/* If we get thru the loop to this point, it means the register was
not saved in any frame. Return the actual live-register value. */
if (lval) /* found it in a live register */
*lval = lval_register;
if (addrp)
*addrp = REGISTER_BYTE (regnum);
if (raw_buffer)
read_register_gen (regnum, raw_buffer);
}
void
_initialize_blockframe ()
{