2002-11-08 Andrew Cagney <ac131313@redhat.com>
* blockframe.c: Include "dummy-frame.h". (struct dummy_frame, dummy_frame_stack) (generic_find_dummy_frame, deprecated_generic_find_dummy_frame) (generic_pc_in_call_dummy, deprecated_read_register_dummy) (generic_push_dummy_frame, generic_save_dummy_frame_tos) (generic_save_call_dummy_addr, generic_pop_current_frame) (generic_pop_dummy_frame, generic_fix_call_dummy) (generic_fix_call_dummy, generic_call_dummy_register_unwind): Move dummy frame code from here... * dummy-frame.c: ...to here. New file. * dummy-frame.h: New file. (generic_call_dummy_register_unwind): Declare. (generic_find_dummy_frame): Declare. * Makefile.in (SFILES): Add dummy-frame.c. (dummy-frame.o): Specify dependencies. (dummy_frame_h): Define. (COMMON_OBS): Add dummy-frame.o. (blockframe.o): Update dependencies.
This commit is contained in:
parent
208d818701
commit
9c1412c1a1
5 changed files with 404 additions and 306 deletions
304
gdb/blockframe.c
304
gdb/blockframe.c
|
@ -35,17 +35,10 @@
|
|||
#include "annotate.h"
|
||||
#include "regcache.h"
|
||||
#include "gdb_assert.h"
|
||||
#include "dummy-frame.h"
|
||||
|
||||
/* Prototypes for exported functions. */
|
||||
|
||||
static void generic_call_dummy_register_unwind (struct frame_info *frame,
|
||||
void **cache,
|
||||
int regnum,
|
||||
int *optimized,
|
||||
enum lval_type *lval,
|
||||
CORE_ADDR *addrp,
|
||||
int *realnum,
|
||||
void *raw_buffer);
|
||||
static void frame_saved_regs_register_unwind (struct frame_info *frame,
|
||||
void **cache,
|
||||
int regnum,
|
||||
|
@ -1107,244 +1100,6 @@ pc_in_call_dummy_at_entry_point (CORE_ADDR pc, CORE_ADDR sp,
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* GENERIC 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 we must maintain an independent 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 no call instruction will be
|
||||
* being executed by the target. Also FRAME_CHAIN_VALID as
|
||||
* generic_{file,func}_frame_chain_valid and FIX_CALL_DUMMY as
|
||||
* generic_fix_call_dummy. */
|
||||
|
||||
/* Dummy frame. This saves the processor state just prior to setting
|
||||
up the inferior function call. Older targets save the registers
|
||||
on the target stack (but that really slows down function calls). */
|
||||
|
||||
struct dummy_frame
|
||||
{
|
||||
struct dummy_frame *next;
|
||||
|
||||
CORE_ADDR pc;
|
||||
CORE_ADDR fp;
|
||||
CORE_ADDR sp;
|
||||
CORE_ADDR top;
|
||||
struct regcache *regcache;
|
||||
|
||||
/* Address range of the call dummy code. Look for PC in the range
|
||||
[LO..HI) (after allowing for DECR_PC_AFTER_BREAK). */
|
||||
CORE_ADDR call_lo;
|
||||
CORE_ADDR call_hi;
|
||||
};
|
||||
|
||||
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 and
|
||||
FP/SP. Unlike PC_IN_CALL_DUMMY, this function doesn't need to
|
||||
adjust for DECR_PC_AFTER_BREAK. This is because it is only legal
|
||||
to call this function after the PC has been adjusted. */
|
||||
|
||||
static struct regcache *
|
||||
generic_find_dummy_frame (CORE_ADDR pc, CORE_ADDR fp)
|
||||
{
|
||||
struct dummy_frame *dummyframe;
|
||||
|
||||
for (dummyframe = dummy_frame_stack; dummyframe != NULL;
|
||||
dummyframe = dummyframe->next)
|
||||
{
|
||||
/* Does the PC fall within the dummy frame's breakpoint
|
||||
instruction. If not, discard this one. */
|
||||
if (!(pc >= dummyframe->call_lo && pc < dummyframe->call_hi))
|
||||
continue;
|
||||
/* Does the FP match? */
|
||||
if (dummyframe->top != 0)
|
||||
{
|
||||
/* If the target architecture explicitly saved the
|
||||
top-of-stack before the inferior function call, assume
|
||||
that that same architecture will always pass in an FP
|
||||
(frame base) value that eactly matches that saved TOS.
|
||||
Don't check the saved SP and SP as they can lead to false
|
||||
hits. */
|
||||
if (fp != dummyframe->top)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* An older target that hasn't explicitly or implicitly
|
||||
saved the dummy frame's top-of-stack. Try matching the
|
||||
FP against the saved SP and FP. NOTE: If you're trying
|
||||
to fix a problem with GDB not correctly finding a dummy
|
||||
frame, check the comments that go with FRAME_ALIGN() and
|
||||
SAVE_DUMMY_FRAME_TOS(). */
|
||||
if (fp != dummyframe->fp && fp != dummyframe->sp)
|
||||
continue;
|
||||
}
|
||||
/* The FP matches this dummy frame. */
|
||||
return dummyframe->regcache;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *
|
||||
deprecated_generic_find_dummy_frame (CORE_ADDR pc, CORE_ADDR fp)
|
||||
{
|
||||
struct regcache *regcache = generic_find_dummy_frame (pc, fp);
|
||||
if (regcache == NULL)
|
||||
return NULL;
|
||||
return deprecated_grub_regcache_for_registers (regcache);
|
||||
}
|
||||
|
||||
/* Function: pc_in_call_dummy (pc, sp, fp)
|
||||
|
||||
Return true if the PC falls in a dummy frame created by gdb for an
|
||||
inferior call. The code below which allows DECR_PC_AFTER_BREAK is
|
||||
for infrun.c, which may give the function a PC without that
|
||||
subtracted out. */
|
||||
|
||||
int
|
||||
generic_pc_in_call_dummy (CORE_ADDR pc, CORE_ADDR sp, CORE_ADDR fp)
|
||||
{
|
||||
struct dummy_frame *dummyframe;
|
||||
for (dummyframe = dummy_frame_stack;
|
||||
dummyframe != NULL;
|
||||
dummyframe = dummyframe->next)
|
||||
{
|
||||
if ((pc >= dummyframe->call_lo)
|
||||
&& (pc < dummyframe->call_hi + DECR_PC_AFTER_BREAK))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Function: read_register_dummy
|
||||
Find a saved register from before GDB calls a function in the inferior */
|
||||
|
||||
CORE_ADDR
|
||||
deprecated_read_register_dummy (CORE_ADDR pc, CORE_ADDR fp, int regno)
|
||||
{
|
||||
struct regcache *dummy_regs = generic_find_dummy_frame (pc, fp);
|
||||
|
||||
if (dummy_regs)
|
||||
{
|
||||
/* NOTE: cagney/2002-08-12: Replaced a call to
|
||||
regcache_raw_read_as_address() with a call to
|
||||
regcache_cooked_read_unsigned(). The old, ...as_address
|
||||
function was eventually calling extract_unsigned_integer (via
|
||||
extract_address) to unpack the registers value. The below is
|
||||
doing an unsigned extract so that it is functionally
|
||||
equivalent. The read needs to be cooked as, otherwise, it
|
||||
will never correctly return the value of a register in the
|
||||
[NUM_REGS .. NUM_REGS+NUM_PSEUDO_REGS) range. */
|
||||
ULONGEST val;
|
||||
regcache_cooked_read_unsigned (dummy_regs, regno, &val);
|
||||
return val;
|
||||
}
|
||||
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 (void)
|
||||
{
|
||||
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 (INNER_THAN (dummy_frame->fp, fp)) /* stale -- destroy! */
|
||||
{
|
||||
dummy_frame_stack = dummy_frame->next;
|
||||
regcache_xfree (dummy_frame->regcache);
|
||||
xfree (dummy_frame);
|
||||
dummy_frame = dummy_frame_stack;
|
||||
}
|
||||
else
|
||||
dummy_frame = dummy_frame->next;
|
||||
|
||||
dummy_frame = xmalloc (sizeof (struct dummy_frame));
|
||||
dummy_frame->regcache = regcache_xmalloc (current_gdbarch);
|
||||
|
||||
dummy_frame->pc = read_pc ();
|
||||
dummy_frame->sp = read_sp ();
|
||||
dummy_frame->top = 0;
|
||||
dummy_frame->fp = fp;
|
||||
regcache_cpy (dummy_frame->regcache, current_regcache);
|
||||
dummy_frame->next = dummy_frame_stack;
|
||||
dummy_frame_stack = dummy_frame;
|
||||
}
|
||||
|
||||
void
|
||||
generic_save_dummy_frame_tos (CORE_ADDR sp)
|
||||
{
|
||||
dummy_frame_stack->top = sp;
|
||||
}
|
||||
|
||||
/* Record the upper/lower bounds on the address of the call dummy. */
|
||||
|
||||
void
|
||||
generic_save_call_dummy_addr (CORE_ADDR lo, CORE_ADDR hi)
|
||||
{
|
||||
dummy_frame_stack->call_lo = lo;
|
||||
dummy_frame_stack->call_hi = hi;
|
||||
}
|
||||
|
||||
/* Restore the machine state from either the saved dummy stack or a
|
||||
real stack frame. */
|
||||
|
||||
void
|
||||
generic_pop_current_frame (void (*popper) (struct frame_info * frame))
|
||||
{
|
||||
struct frame_info *frame = get_current_frame ();
|
||||
|
||||
if (PC_IN_CALL_DUMMY (frame->pc, frame->frame, frame->frame))
|
||||
generic_pop_dummy_frame ();
|
||||
else
|
||||
(*popper) (frame);
|
||||
}
|
||||
|
||||
/* Function: pop_dummy_frame
|
||||
Restore the machine state from a saved dummy stack frame. */
|
||||
|
||||
void
|
||||
generic_pop_dummy_frame (void)
|
||||
{
|
||||
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;
|
||||
regcache_cpy (current_regcache, dummy_frame->regcache);
|
||||
flush_cached_frames ();
|
||||
|
||||
regcache_xfree (dummy_frame->regcache);
|
||||
xfree (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 */
|
||||
|
@ -1373,63 +1128,6 @@ generic_func_frame_chain_valid (CORE_ADDR fp, struct frame_info *fi)
|
|||
&& !inside_entry_func ((fi)->pc));
|
||||
}
|
||||
|
||||
/* Function: fix_call_dummy
|
||||
Stub function. Generic dummy frames typically do not need to fix
|
||||
the frame being created */
|
||||
|
||||
void
|
||||
generic_fix_call_dummy (char *dummy, CORE_ADDR pc, CORE_ADDR fun, int nargs,
|
||||
struct value **args, struct type *type, int gcc_p)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Given a call-dummy dummy-frame, return the registers. Here the
|
||||
register value is taken from the local copy of the register buffer. */
|
||||
|
||||
static void
|
||||
generic_call_dummy_register_unwind (struct frame_info *frame, void **cache,
|
||||
int regnum, int *optimized,
|
||||
enum lval_type *lvalp, CORE_ADDR *addrp,
|
||||
int *realnum, void *bufferp)
|
||||
{
|
||||
gdb_assert (frame != NULL);
|
||||
gdb_assert (PC_IN_CALL_DUMMY (frame->pc, frame->frame, frame->frame));
|
||||
|
||||
/* Describe the register's location. Generic dummy frames always
|
||||
have the register value in an ``expression''. */
|
||||
*optimized = 0;
|
||||
*lvalp = not_lval;
|
||||
*addrp = 0;
|
||||
*realnum = -1;
|
||||
|
||||
/* If needed, find and return the value of the register. */
|
||||
if (bufferp != NULL)
|
||||
{
|
||||
struct regcache *registers;
|
||||
#if 1
|
||||
/* Get the address of the register buffer that contains all the
|
||||
saved registers for this dummy frame. Cache that address. */
|
||||
registers = (*cache);
|
||||
if (registers == NULL)
|
||||
{
|
||||
registers = generic_find_dummy_frame (frame->pc, frame->frame);
|
||||
(*cache) = registers;
|
||||
}
|
||||
#else
|
||||
/* Get the address of the register buffer that contains the
|
||||
saved registers and then extract the value from that. */
|
||||
registers = generic_find_dummy_frame (frame->pc, frame->frame);
|
||||
#endif
|
||||
gdb_assert (registers != NULL);
|
||||
/* Return the actual value. */
|
||||
/* Use the regcache_cooked_read() method so that it, on the fly,
|
||||
constructs either a raw or pseudo register from the raw
|
||||
register cache. */
|
||||
regcache_cooked_read (registers, regnum, bufferp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the register saved in the simplistic ``saved_regs'' cache.
|
||||
If the value isn't here AND a value is needed, try the next inner
|
||||
most frame. */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue