/* Get info from stack frames;
convert between frames, blocks, functions and pc values.
- Copyright 1986, 1987, 1988, 1989, 1991 Free Software Foundation, Inc.
+ Copyright 1986, 1987, 1988, 1989, 1991, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
This file is part of GDB.
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 "symtab.h"
#include "value.h" /* for read_register */
#include "target.h" /* for target_has_stack */
#include "inferior.h" /* for read_pc */
+#include "annotate.h"
/* Is ADDR inside the startup file? Note that if your machine
has a way to detect the bottom of the stack, there is no need
#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 &&
return 1;
if (symfile_objfile == 0)
return 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);
}
#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
symfile_objfile -> ei.entry_func_highpc > pc);
}
-/* Address of innermost stack frame (contents of FP register) */
+/* Info about the innermost stack frame (contents of FP register) */
-static FRAME current_frame;
+static struct frame_info *current_frame;
+
+/* Cache for frame addresses already read by gdb. Valid only while
+ inferior is stopped. Control variables for the frame cache should
+ be local to this module. */
-/*
- * Cache for frame addresses already read by gdb. Valid only while
- * inferior is stopped. Control variables for the frame cache should
- * be local to this module.
- */
struct obstack frame_cache_obstack;
/* Return the innermost (currently executing) stack frame. */
-FRAME
+struct frame_info *
get_current_frame ()
{
- /* We assume its address is kept in a general register;
- param.h says which register. */
-
+ if (current_frame == NULL)
+ {
+ if (target_has_stack)
+ current_frame = create_new_frame (read_fp (), read_pc ());
+ else
+ error ("No stack.");
+ }
return current_frame;
}
void
set_current_frame (frame)
- FRAME frame;
+ struct frame_info *frame;
{
current_frame = frame;
}
-FRAME
+/* Create an arbitrary (i.e. address specified by user) or innermost frame.
+ Always returns a non-NULL value. */
+
+struct frame_info *
create_new_frame (addr, pc)
- FRAME_ADDR addr;
+ CORE_ADDR addr;
CORE_ADDR pc;
{
- struct frame_info *fci; /* Same type as FRAME */
+ struct frame_info *fi;
char *name;
- fci = (struct frame_info *)
+ fi = (struct frame_info *)
obstack_alloc (&frame_cache_obstack,
sizeof (struct frame_info));
/* Arbitrary frame */
- fci->next = (struct frame_info *) 0;
- fci->prev = (struct frame_info *) 0;
- fci->frame = addr;
- fci->pc = pc;
+ fi->next = NULL;
+ fi->prev = NULL;
+ fi->frame = addr;
+ fi->pc = pc;
find_pc_partial_function (pc, &name, (CORE_ADDR *)NULL,(CORE_ADDR *)NULL);
- fci->signal_handler_caller = IN_SIGTRAMP (fci->pc, name);
+ fi->signal_handler_caller = IN_SIGTRAMP (fi->pc, name);
#ifdef INIT_EXTRA_FRAME_INFO
- INIT_EXTRA_FRAME_INFO (0, fci);
+ INIT_EXTRA_FRAME_INFO (0, fi);
#endif
- return fci;
+ return fi;
}
-/* Return the frame that called FRAME.
- If FRAME is the original frame (it has no caller), return 0. */
+/* Return the frame that called FI.
+ If FI is the original frame (it has no caller), return 0. */
-FRAME
+struct frame_info *
get_prev_frame (frame)
- FRAME frame;
+ struct frame_info *frame;
{
- /* We're allowed to know that FRAME and "struct frame_info *" are
- the same */
return get_prev_frame_info (frame);
}
-/* Return the frame that FRAME calls (0 if FRAME is the innermost
+/* Return the frame that FRAME calls (NULL if FRAME is the innermost
frame). */
-FRAME
+struct frame_info *
get_next_frame (frame)
- FRAME frame;
+ struct frame_info *frame;
{
- /* We're allowed to know that FRAME and "struct frame_info *" are
- the same */
return frame->next;
}
-/*
- * Flush the entire frame cache.
- */
+/* Flush the entire frame cache. */
+
void
flush_cached_frames ()
{
obstack_free (&frame_cache_obstack, 0);
obstack_init (&frame_cache_obstack);
- current_frame = (struct frame_info *) 0; /* Invalidate cache */
+ current_frame = NULL; /* Invalidate cache */
+ select_frame (NULL, -1);
+ annotate_frames_invalid ();
}
/* Flush the frame cache, and start a new one if necessary. */
+
void
reinit_frame_cache ()
{
flush_cached_frames ();
- if (target_has_stack)
+
+ /* FIXME: The inferior_pid test is wrong if there is a corefile. */
+ if (inferior_pid != 0)
{
- set_current_frame (create_new_frame (read_fp (), read_pc ()));
select_frame (get_current_frame (), 0);
}
- else
- {
- set_current_frame (0);
- select_frame ((FRAME) 0, -1);
- }
-}
-
-/* Return a structure containing various interesting information
- about a specified stack frame. */
-/* How do I justify including this function? Well, the FRAME
- identifier format has gone through several changes recently, and
- it's not completely inconceivable that it could happen again. If
- it does, have this routine around will help */
-
-struct frame_info *
-get_frame_info (frame)
- FRAME frame;
-{
- return frame;
}
/* If a machine allows frameless functions, it should define a macro
int
frameless_look_for_prologue (frame)
- FRAME 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,
#endif
return after_prologue == func_start;
}
+ else if (frame->pc == 0)
+ /* A frame with a zero PC is usually created by dereferencing a NULL
+ function pointer, normally causing an immediate core dump of the
+ inferior. Mark function as frameless, as the inferior has no chance
+ of setting up a stack frame. */
+ return 1;
else
/* If we can't find the start of the function, we don't really
know whether the function is frameless, but we should be able
struct frame_info *
get_prev_frame_info (next_frame)
- FRAME next_frame;
+ struct frame_info *next_frame;
{
- FRAME_ADDR address = 0;
+ CORE_ADDR address = 0;
struct frame_info *prev;
int fromleaf = 0;
char *name;
if (!next_frame)
{
+#if 0
+ /* This screws value_of_variable, which just wants a nice clean
+ NULL return from block_innermost_frame if there are no frames.
+ I don't think I've ever seen this message happen otherwise.
+ And returning NULL here is a perfectly legitimate thing to do. */
if (!current_frame)
{
error ("You haven't set up a process's stack to examine.");
}
+#endif
return current_frame;
}
{
FRAMELESS_FUNCTION_INVOCATION (next_frame, fromleaf);
if (fromleaf)
- address = next_frame->frame;
+ address = FRAME_FP (next_frame);
}
#endif
We shouldn't need INIT_FRAME_PC_FIRST to add more complication to
an already overcomplicated part of GDB. gnu@cygnus.com, 15Sep92.
- To answer the question, yes the sparc needs INIT_FRAME_PC after
- INIT_EXTRA_FRAME_INFO. Suggested scheme:
+ Assuming that some machines need INIT_FRAME_PC after
+ INIT_EXTRA_FRAME_INFO, one possible scheme:
SETUP_INNERMOST_FRAME()
Default version is just create_new_frame (read_fp ()),
Only change here is that create_new_frame would no longer init extra
frame info; SETUP_ARBITRARY_FRAME would have to do that.
INIT_PREV_FRAME(fromleaf, prev)
- Replace INIT_EXTRA_FRAME_INFO and INIT_FRAME_PC.
+ Replace INIT_EXTRA_FRAME_INFO and INIT_FRAME_PC. This should
+ also return a flag saying whether to keep the new frame, or
+ whether to discard it, because on some machines (e.g. mips) it
+ is really awkward to have FRAME_CHAIN_VALID called *before*
+ INIT_EXTRA_FRAME_INFO (there is no good way to get information
+ deduced in FRAME_CHAIN_VALID into the extra fields of the new frame).
std_frame_pc(fromleaf, prev)
This is the default setting for INIT_PREV_FRAME. It just does what
the default INIT_FRAME_PC does. Some machines will call it from
INIT_PREV_FRAME (either at the beginning, the end, or in the middle).
Some machines won't use it.
- kingdon@cygnus.com, 13Apr93. */
+ kingdon@cygnus.com, 13Apr93, 31Jan94, 14Dec94. */
#ifdef INIT_FRAME_PC_FIRST
INIT_FRAME_PC_FIRST (fromleaf, prev);
#endif
/* This entry is in the frame queue now, which is good since
- FRAME_SAVED_PC may use that queue to figure out it's value
+ FRAME_SAVED_PC may use that queue to figure out its value
(see tm-sparc.h). We want the pc saved in the inferior frame. */
INIT_FRAME_PC(fromleaf, prev);
+ /* If ->frame and ->pc are unchanged, we are in the process of getting
+ ourselves into an infinite backtrace. Some architectures check this
+ in FRAME_CHAIN or thereabouts, but it seems like there is no reason
+ this can't be an architecture-independent check. */
+ if (next_frame != NULL)
+ {
+ if (prev->frame == next_frame->frame
+ && prev->pc == next_frame->pc)
+ {
+ next_frame->prev = NULL;
+ obstack_free (&frame_cache_obstack, prev);
+ return NULL;
+ }
+ }
+
find_pc_partial_function (prev->pc, &name,
(CORE_ADDR *)NULL,(CORE_ADDR *)NULL);
if (IN_SIGTRAMP (prev->pc, name))
CORE_ADDR
get_frame_pc (frame)
- FRAME frame;
+ struct frame_info *frame;
{
- struct frame_info *fi;
- fi = get_frame_info (frame);
- return fi->pc;
+ return frame->pc;
}
#if defined (FRAME_FIND_SAVED_REGS)
/* Find the addresses in which registers are saved in FRAME. */
void
-get_frame_saved_regs (frame_info_addr, saved_regs_addr)
- struct frame_info *frame_info_addr;
+get_frame_saved_regs (frame, saved_regs_addr)
+ struct frame_info *frame;
struct frame_saved_regs *saved_regs_addr;
{
- FRAME_FIND_SAVED_REGS (frame_info_addr, *saved_regs_addr);
+ FRAME_FIND_SAVED_REGS (frame, *saved_regs_addr);
}
#endif
struct block *
get_frame_block (frame)
- FRAME frame;
+ struct frame_info *frame;
{
- struct frame_info *fi;
CORE_ADDR pc;
- fi = get_frame_info (frame);
-
- pc = fi->pc;
- if (fi->next != 0)
- /* We are not in the innermost frame. We need to subtract one to
- get the correct block, in case the call instruction was the
- last instruction of the block. If there are any machines on
- which the saved pc does not point to after the call insn, we
- probably want to make fi->pc point after the call insn anyway. */
+ pc = frame->pc;
+ if (frame->next != 0 && frame->next->signal_handler_caller == 0)
+ /* We are not in the innermost frame and we were not interrupted
+ by a signal. We need to subtract one to get the correct block,
+ in case the call instruction was the last instruction of the block.
+ If there are any machines on which the saved pc does not point to
+ after the call insn, we probably want to make frame->pc point after
+ the call insn anyway. */
--pc;
return block_for_pc (pc);
}
struct symbol *
get_frame_function (frame)
- FRAME frame;
+ struct frame_info *frame;
{
register struct block *bl = get_frame_block (frame);
if (bl == 0)
struct symbol *f;
struct minimal_symbol *msymbol;
struct partial_symbol *psb;
+ struct obj_section *sec;
if (pc >= cache_pc_function_low && pc < cache_pc_function_high)
goto return_cached_value;
#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;
{
/* Need to read the symbols to get a good value for the end address. */
if (endaddr != NULL && !pst->readin)
- PSYMTAB_TO_SYMTAB (pst);
+ {
+ /* Need to get the terminal in case symbol-reading produces
+ output. */
+ target_terminal_ours_for_output ();
+ PSYMTAB_TO_SYMTAB (pst);
+ }
if (pst->readin)
{
}
}
+ /* Not in the normal symbol tables, see if the pc is in a known section.
+ If it's not, then give up. This ensures that anything beyond the end
+ of the text seg doesn't appear to be part of the last function in the
+ text segment. */
+
+ sec = find_pc_section (pc);
+
+ if (!sec)
+ msymbol = NULL;
+
/* Must be in the minimal symbol table. */
if (msymbol == NULL)
{
return 0;
}
- /* I believe the purpose of this check is to make sure that anything
- beyond the end of the text segment does not appear as part of the
- last function of the text segment. It assumes that there is something
- other than a mst_text symbol after the text segment. It is broken in
- various cases, so anything relying on this behavior (there might be
- some places) should be using find_pc_section or some such instead. */
- if (msymbol -> type == mst_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;
+ cache_pc_function_low = SYMBOL_VALUE_ADDRESS (msymbol);
cache_pc_function_name = SYMBOL_NAME (msymbol);
- if (SYMBOL_NAME (msymbol + 1) != NULL)
- /* This might be part of a different segment, which might be a bad
- idea. Perhaps we should be using the smaller of this address or the
- endaddr from find_pc_section. */
+ /* Use the lesser of the next minimal symbol, or the end of the section, as
+ the end of the function. */
+
+ if (SYMBOL_NAME (msymbol + 1) != NULL
+ && SYMBOL_VALUE_ADDRESS (msymbol + 1) < sec->endaddr)
cache_pc_function_high = SYMBOL_VALUE_ADDRESS (msymbol + 1);
else
- {
- /* We got the start address from the last msymbol in the objfile.
- So the end address is the end of the section. */
- struct obj_section *sec;
-
- sec = find_pc_section (pc);
- if (sec == NULL)
- {
- /* Don't know if this can happen but if it does, then just say
- that the function is 1 byte long. */
- cache_pc_function_high = cache_pc_function_low + 1;
- }
- else
- cache_pc_function_high = sec->endaddr;
- }
+ /* We got the start address from the last msymbol in the objfile.
+ So the end address is the end of the section. */
+ cache_pc_function_high = sec->endaddr;
return_cached_value:
if (address)
/* Return the innermost stack frame executing inside of BLOCK,
or NULL if there is no such frame. If BLOCK is NULL, just return NULL. */
-FRAME
+struct frame_info *
block_innermost_frame (block)
struct block *block;
{
- struct frame_info *fi;
- register FRAME frame;
+ struct frame_info *frame;
register CORE_ADDR start;
register CORE_ADDR end;
start = BLOCK_START (block);
end = BLOCK_END (block);
- frame = 0;
+ frame = NULL;
while (1)
{
frame = get_prev_frame (frame);
- if (frame == 0)
- return 0;
- fi = get_frame_info (frame);
- if (fi->pc >= start && fi->pc < end)
+ if (frame == NULL)
+ return NULL;
+ if (frame->pc >= start && frame->pc < end)
+ return frame;
+ }
+}
+
+/* Return the full FRAME which corresponds to the given CORE_ADDR
+ or NULL if no FRAME on the chain corresponds to CORE_ADDR. */
+
+struct frame_info *
+find_frame_addr_in_frame_chain (frame_addr)
+ CORE_ADDR frame_addr;
+{
+ struct frame_info *frame = NULL;
+
+ if (frame_addr == (CORE_ADDR)0)
+ return NULL;
+
+ while (1)
+ {
+ frame = get_prev_frame (frame);
+ if (frame == NULL)
+ return NULL;
+ if (FRAME_FP (frame) == frame_addr)
return frame;
}
}
CORE_ADDR
sigtramp_saved_pc (frame)
- FRAME frame;
+ struct frame_info *frame;
{
CORE_ADDR sigcontext_addr;
char buf[TARGET_PTR_BIT / TARGET_CHAR_BIT];
/* Get sigcontext address, it is the third parameter on the stack. */
if (frame->next)
sigcontext_addr = read_memory_integer (FRAME_ARGS_ADDRESS (frame->next)
- + FRAME_ARGS_SKIP + sigcontext_offs,
+ + FRAME_ARGS_SKIP
+ + sigcontext_offs,
ptrbytes);
else
sigcontext_addr = read_memory_integer (read_register (SP_REGNUM)
}
#endif /* SIGCONTEXT_PC_OFFSET */
+#ifdef USE_GENERIC_DUMMY_FRAMES
+
+/*
+ * 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 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 no call instruction will be being
+ * executed by the target.
+ */
+
+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;
+
+ if (pc != entry_point_address ())
+ return 0;
+
+ 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 */
+ 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 (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) */
+ return (fp != 0
+ && fi->frame INNER_THAN fp
+ && !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 = 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);
+}
+#endif /* USE_GENERIC_DUMMY_FRAMES */
+
void
_initialize_blockframe ()
{