From 6dc42492b62e85e892cb3a0ec4732d4b81f79b87 Mon Sep 17 00:00:00 2001 From: Andrew Cagney Date: Mon, 17 Mar 2003 14:23:50 +0000 Subject: [PATCH] 2003-03-17 Andrew Cagney Fix frame off-by-one bug. * frame-unwind.h (frame_this_id_ftype): Replace frame_unwind_id_ftype. (frame_prev_register_ftype): Replace frame_unwind_reg_ftype. (struct frame_unwind): Replace "id" with "this_id". Replace "reg" with "prev_register". * frame-unwind.c (frame_unwind_find_by_pc): Return legacy_saved_regs_unwind instead of trad_frame_unwind. Update comment. * dummy-frame.c (cached_find_dummy_frame): Delete function. (dummy_frame_this_id): Replace dummy_frame_id_unwind. (dummy_frame_prev_register): Replace dummy_frame_register_unwind. (dummy_frame_unwind): Update. * sentinel-frame.c (sentinel_frame_prev_register): Replace sentinel_frame_register_unwind. (sentinel_frame_this_id): Replace sentinel_frame_id_unwind. (sentinel_frame_unwinder): Update. * frame.h (legacy_saved_regs_unwind): Replace trad_frame_unwind. (struct frame_info): Rename "unwind_cache" to "prologue_cache". * frame.c (create_sentinel_frame): Update. Initialize "prologue_cache" instead of "unwind_cache". (frame_register_unwind): Call this frame's prev_register with the next frame and this frame's prologue cache. (get_prev_frame): Simplify. Always call prev frame's this_id with this frame and prev frame's prologue cache. Document that this call is shifted one to the left when compared to the frame_register_unwind call. (legacy_saved_regs_prev_register): Replace frame_saved_regs_register_unwind. (legacy_saved_regs_this_id): Replace frame_saved_regs_id_unwind. (legacy_saved_regs_unwinder): Replace trad_frame_unwinder. (legacy_saved_regs_unwind): Replace trad_frame_unwind. * d10v-tdep.c (d10v_frame_this_id): Replace d10v_frame_id_unwind. (d10v_frame_unwind): Update. (d10v_frame_prev_register): Replace d10v_frame_register_unwind. (d10v_frame_unwind_cache): Replace this "fi" with "next_frame". (saved_regs_unwinder): Replace this "frame" with "next_frame", and "saved_regs" with "this_saved_regs". --- gdb/ChangeLog | 41 ++++++++++ gdb/d10v-tdep.c | 120 +++++++++++++++-------------- gdb/dummy-frame.c | 109 ++++++++++++++++++++------- gdb/frame-unwind.c | 9 ++- gdb/frame-unwind.h | 110 +++++++++++++++++++-------- gdb/frame.c | 176 ++++++++++++++++++++++--------------------- gdb/frame.h | 11 +-- gdb/sentinel-frame.c | 32 ++++---- 8 files changed, 381 insertions(+), 227 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 72733b4a158..bec734b8c2a 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,44 @@ +2003-03-17 Andrew Cagney + + Fix frame off-by-one bug. + * frame-unwind.h (frame_this_id_ftype): Replace + frame_unwind_id_ftype. + (frame_prev_register_ftype): Replace frame_unwind_reg_ftype. + (struct frame_unwind): Replace "id" with "this_id". Replace "reg" + with "prev_register". + * frame-unwind.c (frame_unwind_find_by_pc): Return + legacy_saved_regs_unwind instead of trad_frame_unwind. Update + comment. + * dummy-frame.c (cached_find_dummy_frame): Delete function. + (dummy_frame_this_id): Replace dummy_frame_id_unwind. + (dummy_frame_prev_register): Replace dummy_frame_register_unwind. + (dummy_frame_unwind): Update. + * sentinel-frame.c (sentinel_frame_prev_register): Replace + sentinel_frame_register_unwind. + (sentinel_frame_this_id): Replace sentinel_frame_id_unwind. + (sentinel_frame_unwinder): Update. + * frame.h (legacy_saved_regs_unwind): Replace trad_frame_unwind. + (struct frame_info): Rename "unwind_cache" to "prologue_cache". + * frame.c (create_sentinel_frame): Update. Initialize + "prologue_cache" instead of "unwind_cache". + (frame_register_unwind): Call this frame's prev_register with the + next frame and this frame's prologue cache. + (get_prev_frame): Simplify. Always call prev frame's this_id with + this frame and prev frame's prologue cache. Document that this + call is shifted one to the left when compared to the + frame_register_unwind call. + (legacy_saved_regs_prev_register): Replace + frame_saved_regs_register_unwind. + (legacy_saved_regs_this_id): Replace frame_saved_regs_id_unwind. + (legacy_saved_regs_unwinder): Replace trad_frame_unwinder. + (legacy_saved_regs_unwind): Replace trad_frame_unwind. + * d10v-tdep.c (d10v_frame_this_id): Replace d10v_frame_id_unwind. + (d10v_frame_unwind): Update. + (d10v_frame_prev_register): Replace d10v_frame_register_unwind. + (d10v_frame_unwind_cache): Replace this "fi" with "next_frame". + (saved_regs_unwinder): Replace this "frame" with "next_frame", and + "saved_regs" with "this_saved_regs". + 2003-03-16 Andrew Cagney * frame.c (frame_pop): Don't call target_store_registers. Fix diff --git a/gdb/d10v-tdep.c b/gdb/d10v-tdep.c index b60ce7fad3a..f443ceff8dd 100644 --- a/gdb/d10v-tdep.c +++ b/gdb/d10v-tdep.c @@ -705,8 +705,8 @@ prologue_find_regs (struct d10v_unwind_cache *info, unsigned short op, for it IS the sp for the next frame. */ struct d10v_unwind_cache * -d10v_frame_unwind_cache (struct frame_info *fi, - void **cache) +d10v_frame_unwind_cache (struct frame_info *next_frame, + void **this_prologue_cache) { CORE_ADDR pc; ULONGEST prev_sp; @@ -716,18 +716,18 @@ d10v_frame_unwind_cache (struct frame_info *fi, int i; struct d10v_unwind_cache *info; - if ((*cache)) - return (*cache); + if ((*this_prologue_cache)) + return (*this_prologue_cache); info = FRAME_OBSTACK_ZALLOC (struct d10v_unwind_cache); - (*cache) = info; + (*this_prologue_cache) = info; info->saved_regs = frame_obstack_zalloc (SIZEOF_FRAME_SAVED_REGS); info->size = 0; info->return_pc = 0; info->sp_offset = 0; - pc = get_pc_function_start (get_frame_pc (fi)); + pc = get_pc_function_start (frame_pc_unwind (next_frame)); info->uses_frame = 0; while (1) @@ -788,7 +788,7 @@ d10v_frame_unwind_cache (struct frame_info *fi, /* The SP was moved to the FP. This indicates that a new frame was created. Get THIS frame's FP value by unwinding it from the next frame. */ - frame_read_unsigned_register (fi, FP_REGNUM, &this_base); + frame_unwind_unsigned_register (next_frame, FP_REGNUM, &this_base); /* The FP points at the last saved register. Adjust the FP back to before the first saved register giving the SP. */ prev_sp = this_base + info->size; @@ -806,7 +806,7 @@ d10v_frame_unwind_cache (struct frame_info *fi, { /* Assume that the FP is this frame's SP but with that pushed stack space added back. */ - frame_read_unsigned_register (fi, SP_REGNUM, &this_base); + frame_unwind_unsigned_register (next_frame, SP_REGNUM, &this_base); prev_sp = this_base + info->size; } @@ -831,7 +831,7 @@ d10v_frame_unwind_cache (struct frame_info *fi, else { ULONGEST return_pc; - frame_read_unsigned_register (fi, LR_REGNUM, &return_pc); + frame_unwind_unsigned_register (next_frame, LR_REGNUM, &return_pc); info->return_pc = d10v_make_iaddr (return_pc); } @@ -1457,58 +1457,55 @@ d10v_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame) frame. This will be used to create a new GDB frame struct. */ static void -d10v_frame_id_unwind (struct frame_info *frame, - void **cache, - struct frame_id *id) +d10v_frame_this_id (struct frame_info *next_frame, + void **this_prologue_cache, + struct frame_id *this_id) { - struct d10v_unwind_cache *info = d10v_frame_unwind_cache (frame, cache); - CORE_ADDR addr; + struct d10v_unwind_cache *info + = d10v_frame_unwind_cache (next_frame, this_prologue_cache); + CORE_ADDR base; + CORE_ADDR pc; /* Start with a NULL frame ID. */ - (*id) = null_frame_id; + (*this_id) = null_frame_id; - if (info->return_pc == IMEM_START - || info->return_pc <= IMEM_START - || inside_entry_file (info->return_pc)) - { - /* This is meant to halt the backtrace at "_start". - Make sure we don't halt it at a generic dummy frame. */ - return; - } + /* The PC is easy. */ + pc = frame_pc_unwind (next_frame); - if (!info->saved_regs[FP_REGNUM]) - { - if (!info->saved_regs[SP_REGNUM] - || info->saved_regs[SP_REGNUM] == STACK_START) - return; + /* This is meant to halt the backtrace at "_start". Make sure we + don't halt it at a generic dummy frame. */ + if (pc == IMEM_START || pc <= IMEM_START || inside_entry_file (pc)) + return; - id->base = info->saved_regs[SP_REGNUM]; - id->pc = info->return_pc; - } + /* Hopefully the prologue analysis either correctly determined the + frame's base (which is the SP from the previous frame), or set + that base to "NULL". */ + base = info->base; + if (base == STACK_START || base == 0) + return; - addr = read_memory_unsigned_integer (info->saved_regs[FP_REGNUM], - register_size (current_gdbarch, FP_REGNUM)); - if (addr == 0) + /* Check that we're not going round in circles with the same frame + ID (but avoid applying the test to sentinel frames which do go + round in circles). Can't use frame_id_eq() as that doesn't yet + compare the frame's PC value. */ + if (frame_relative_level (next_frame) >= 0 + && get_frame_type (next_frame) != DUMMY_FRAME + && get_frame_id (next_frame).pc == pc + && get_frame_id (next_frame).base == base) return; - id->base = d10v_make_daddr (addr); - id->pc = info->return_pc; + this_id->base = base; + this_id->pc = pc; } static void -saved_regs_unwinder (struct frame_info *frame, - CORE_ADDR *saved_regs, +saved_regs_unwinder (struct frame_info *next_frame, + CORE_ADDR *this_saved_regs, int regnum, int *optimizedp, enum lval_type *lvalp, CORE_ADDR *addrp, int *realnump, void *bufferp) { - /* If we're using generic dummy frames, we'd better not be in a call - dummy. (generic_call_dummy_register_unwind ought to have been called - instead.) */ - gdb_assert (!(DEPRECATED_USE_GENERIC_DUMMY_FRAMES - && (get_frame_type (frame) == DUMMY_FRAME))); - - if (saved_regs[regnum] != 0) + if (this_saved_regs[regnum] != 0) { if (regnum == SP_REGNUM) { @@ -1519,7 +1516,7 @@ saved_regs_unwinder (struct frame_info *frame, *realnump = -1; if (bufferp != NULL) store_address (bufferp, register_size (current_gdbarch, regnum), - saved_regs[regnum]); + this_saved_regs[regnum]); } else { @@ -1527,12 +1524,12 @@ saved_regs_unwinder (struct frame_info *frame, a local copy of its value. */ *optimizedp = 0; *lvalp = lval_memory; - *addrp = saved_regs[regnum]; + *addrp = this_saved_regs[regnum]; *realnump = -1; if (bufferp != NULL) { /* Read the value in from memory. */ - read_memory (saved_regs[regnum], bufferp, + read_memory (this_saved_regs[regnum], bufferp, register_size (current_gdbarch, regnum)); } } @@ -1543,38 +1540,39 @@ saved_regs_unwinder (struct frame_info *frame, value. If a value is needed, pass the request on down the chain; otherwise just return an indication that the value is in the same register as the next frame. */ - frame_register (frame, regnum, optimizedp, lvalp, addrp, - realnump, bufferp); + frame_register_unwind (next_frame, regnum, optimizedp, lvalp, addrp, + realnump, bufferp); } static void -d10v_frame_register_unwind (struct frame_info *frame, - void **cache, - int regnum, int *optimizedp, - enum lval_type *lvalp, CORE_ADDR *addrp, - int *realnump, void *bufferp) -{ - struct d10v_unwind_cache *info = d10v_frame_unwind_cache (frame, cache); +d10v_frame_prev_register (struct frame_info *next_frame, + void **this_prologue_cache, + int regnum, int *optimizedp, + enum lval_type *lvalp, CORE_ADDR *addrp, + int *realnump, void *bufferp) +{ + struct d10v_unwind_cache *info + = d10v_frame_unwind_cache (next_frame, this_prologue_cache); if (regnum == PC_REGNUM) { /* The call instruction saves the caller's PC in LR. The function prologue of the callee may then save the LR on the stack. Find that possibly saved LR value and return it. */ - saved_regs_unwinder (frame, info->saved_regs, LR_REGNUM, optimizedp, + saved_regs_unwinder (next_frame, info->saved_regs, LR_REGNUM, optimizedp, lvalp, addrp, realnump, bufferp); } else { - saved_regs_unwinder (frame, info->saved_regs, regnum, optimizedp, + saved_regs_unwinder (next_frame, info->saved_regs, regnum, optimizedp, lvalp, addrp, realnump, bufferp); } } static struct frame_unwind d10v_frame_unwind = { - d10v_frame_id_unwind, - d10v_frame_register_unwind + d10v_frame_this_id, + d10v_frame_prev_register }; const struct frame_unwind * diff --git a/gdb/dummy-frame.c b/gdb/dummy-frame.c index bf4a31c1ef4..3f7746f235d 100644 --- a/gdb/dummy-frame.c +++ b/gdb/dummy-frame.c @@ -30,6 +30,10 @@ #include "gdb_assert.h" #include "frame-unwind.h" +static void dummy_frame_this_id (struct frame_info *next_frame, + void **this_prologue_cache, + struct frame_id *this_id); + /* 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). */ @@ -104,14 +108,6 @@ find_dummy_frame (CORE_ADDR pc, CORE_ADDR fp) return NULL; } -struct dummy_frame * -cached_find_dummy_frame (struct frame_info *frame, void **cache) -{ - if ((*cache) == NULL) - (*cache) = find_dummy_frame (get_frame_pc (frame), get_frame_base (frame)); - return (*cache); -} - struct regcache * generic_find_dummy_frame (CORE_ADDR pc, CORE_ADDR fp) { @@ -313,12 +309,19 @@ generic_fix_call_dummy (char *dummy, CORE_ADDR pc, CORE_ADDR fun, int nargs, register value is taken from the local copy of the register buffer. */ static void -dummy_frame_register_unwind (struct frame_info *frame, void **cache, - int regnum, int *optimized, - enum lval_type *lvalp, CORE_ADDR *addrp, - int *realnum, void *bufferp) +dummy_frame_prev_register (struct frame_info *next_frame, + void **this_prologue_cache, + int regnum, int *optimized, + enum lval_type *lvalp, CORE_ADDR *addrp, + int *realnum, void *bufferp) { - struct dummy_frame *dummy = cached_find_dummy_frame (frame, cache); + struct dummy_frame *dummy; + struct frame_id id; + + /* Call the ID method which, if at all possible, will set the + prologue cache. */ + dummy_frame_this_id (next_frame, this_prologue_cache, &id); + dummy = (*this_prologue_cache); gdb_assert (dummy != NULL); /* Describe the register's location. Generic dummy frames always @@ -339,28 +342,80 @@ dummy_frame_register_unwind (struct frame_info *frame, void **cache, } } -/* Assuming that FRAME is a dummy, return the ID of the calling frame - (the frame that the dummy has the saved state of). */ +/* Assuming that THIS frame is a dummy (remember, the NEXT and not + THIS frame is passed in), return the ID of THIS frame. That ID is + determined by examining the NEXT frame's unwound registers using + the method unwind_dummy_id(). As a side effect, THIS dummy frame's + dummy cache is located and and saved in THIS_PROLOGUE_CACHE. */ static void -dummy_frame_id_unwind (struct frame_info *frame, - void **cache, - struct frame_id *id) +dummy_frame_this_id (struct frame_info *next_frame, + void **this_prologue_cache, + struct frame_id *this_id) { - struct dummy_frame *dummy = cached_find_dummy_frame (frame, cache); - /* Oops! In a dummy-frame but can't find the stack dummy. Pretend - that the frame doesn't unwind. Should this function instead - return a has-no-caller indication? */ - if (dummy == NULL) - (*id) = null_frame_id; + struct dummy_frame *dummy = (*this_prologue_cache); + if (dummy != NULL) + { + (*this_id) = dummy->id; + return; + } + /* When unwinding a normal frame, the stack structure is determined + by analyzing the frame's function's code (be it using brute force + prologue analysis, or the dwarf2 CFI). In the case of a dummy + frame, that simply isn't possible. The The PC is either the + program entry point, or some random address on the stack. Trying + to use that PC to apply standard frame ID unwind techniques is + just asking for trouble. */ + if (gdbarch_unwind_dummy_id_p (current_gdbarch)) + { + /* Assume hand_function_call(), via SAVE_DUMMY_FRAME_TOS, + previously saved the dummy frame's ID. Things only work if + the two return the same value. */ + gdb_assert (SAVE_DUMMY_FRAME_TOS_P ()); + /* Use an architecture specific method to extract the prev's + dummy ID from the next frame. Note that this method uses + frame_register_unwind to obtain the register values needed to + determine the dummy frame's ID. */ + (*this_id) = gdbarch_unwind_dummy_id (current_gdbarch, next_frame); + } + else if (frame_relative_level (next_frame) < 0) + { + /* We're unwinding a sentinel frame, the PC of which is pointing + at a stack dummy. Fake up the dummy frame's ID using the + same sequence as is found a traditional unwinder. Once all + architectures supply the unwind_dummy_id method, this code + can go away. */ + (*this_id).base = read_fp (); + (*this_id).pc = read_pc (); + } + else if (legacy_frame_p (current_gdbarch) + && get_prev_frame (next_frame)) + { + /* Things are looking seriously grim! Assume that the legacy + get_prev_frame code has already created THIS frame and linked + it in to the frame chain (a pretty bold assumption), extract + the ID from THIS base / pc. */ + (*this_id).base = get_frame_base (get_prev_frame (next_frame)); + (*this_id).pc = get_frame_pc (get_prev_frame (next_frame)); + } else - (*id) = dummy->id; + { + /* Outch! We're not trying to find the innermost frame's ID yet + we're trying to unwind to a dummy. The architecture must + provide the unwind_dummy_id() method. Abandon the unwind + process but only after first warning the user. */ + internal_warning (__FILE__, __LINE__, + "Missing unwind_dummy_id architecture method"); + (*this_id) = null_frame_id; + return; + } + (*this_prologue_cache) = find_dummy_frame ((*this_id).pc, (*this_id).base); } static struct frame_unwind dummy_frame_unwind = { - dummy_frame_id_unwind, - dummy_frame_register_unwind + dummy_frame_this_id, + dummy_frame_prev_register }; const struct frame_unwind * diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c index a889f955d84..c72d24a0b23 100644 --- a/gdb/frame-unwind.c +++ b/gdb/frame-unwind.c @@ -83,16 +83,19 @@ frame_unwind_find_by_pc (struct gdbarch *gdbarch, CORE_ADDR pc) int i; struct frame_unwind_table *table = gdbarch_data (gdbarch, frame_unwind_data); - /* Seriously old code. Don't even try to use this new mechanism. */ if (!DEPRECATED_USE_GENERIC_DUMMY_FRAMES) - return trad_frame_unwind; + /* Seriously old code. Don't even try to use this new mechanism. + (Note: The variable USE_GENERIC_DUMMY_FRAMES is deprecated, not + the dummy frame mechanism. All architectures should be using + generic dummy frames). */ + return legacy_saved_regs_unwind; for (i = 0; i < table->nr; i++) { const struct frame_unwind *desc = table->p[i] (pc); if (desc != NULL) return desc; } - return trad_frame_unwind; + return legacy_saved_regs_unwind; } void diff --git a/gdb/frame-unwind.h b/gdb/frame-unwind.h index 5d85efe9c09..099f9dee420 100644 --- a/gdb/frame-unwind.h +++ b/gdb/frame-unwind.h @@ -47,42 +47,92 @@ extern const struct frame_unwind *frame_unwind_find_by_pc (struct gdbarch *gdbarch, CORE_ADDR pc); -/* Return the location (and possibly value) of REGNUM for the previous - (older, up) frame. All parameters except VALUEP can be assumed to - be non NULL. When VALUEP is NULL, just the location of the - register should be returned. - - UNWIND_CACHE is provided as mechanism for implementing a per-frame - local cache. It's initial value being NULL. Memory for that cache - should be allocated using frame_obstack_zalloc(). - - Register window architectures (eg SPARC) should note that REGNUM - identifies the register for the previous frame. For instance, a - request for the value of "o1" for the previous frame would be found - in the register "i1" in this FRAME. */ - -typedef void (frame_unwind_reg_ftype) (struct frame_info * frame, - void **unwind_cache, - int regnum, - int *optimized, - enum lval_type * lvalp, - CORE_ADDR *addrp, - int *realnump, void *valuep); - -/* Same as for registers above, but return the ID of the frame that - called this one. */ - -typedef void (frame_unwind_id_ftype) (struct frame_info * frame, - void **unwind_cache, - struct frame_id * id); +/* The following unwind functions assume a chain of frames forming the + sequence: (outer) prev <-> this <-> next (inner). All the + functions are called with called with the next frame's `struct + frame_info' and and this frame's prologue cache. + + THIS frame's register values can be obtained by unwinding NEXT + frame's registers (a recursive operation). + + THIS frame's prologue cache can be used to cache information such + as where this frame's prologue stores the previous frame's + registers. */ + +/* Assuming the frame chain: (outer) prev <-> this <-> next (inner); + use the NEXT frame, and its register unwind method, to determine + the frame ID of THIS frame. + + A frame ID provides an invariant that can be used to re-identify an + instance of a frame. It is a combination of the frame's `base' and + the frame's function's code address. + + Traditionally, THIS frame's ID was determined by examining THIS + frame's function's prologue, and identifying the register/offset + used as THIS frame's base. + + Example: An examination of THIS frame's prologue reveals that, on + entry, it saves the PC(+12), SP(+8), and R1(+4) registers + (decrementing the SP by 12). Consequently, the frame ID's base can + be determined by adding 12 to the THIS frame's stack-pointer, and + the value of THIS frame's SP can be obtained by unwinding the NEXT + frame's SP. + + THIS_PROLOGUE_CACHE can be used to share any prolog analysis data + with the other unwind methods. Memory for that cache should be + allocated using frame_obstack_zalloc(). */ + +typedef void (frame_this_id_ftype) (struct frame_info *next_frame, + void **this_prologue_cache, + struct frame_id *this_id); + +/* Assuming the frame chain: (outer) prev <-> this <-> next (inner); + use the NEXT frame, and its register unwind method, to unwind THIS + frame's registers (returning the value of the specified register + REGNUM in the previous frame). + + Traditionally, THIS frame's registers were unwound by examining + THIS frame's function's prologue and identifying which registers + that prolog code saved on the stack. + + Example: An examination of THIS frame's prologue reveals that, on + entry, it saves the PC(+12), SP(+8), and R1(+4) registers + (decrementing the SP by 12). Consequently, the value of the PC + register in the previous frame is found in memory at SP+12, and + THIS frame's SP can be obtained by unwinding the NEXT frame's SP. + + Why not pass in THIS_FRAME? By passing in NEXT frame and THIS + cache, the supplied parameters are consistent with the sibling + function THIS_ID. + + Can the code call ``frame_register (get_prev_frame (NEXT_FRAME))''? + Won't the call frame_register (THIS_FRAME) be faster? Well, + ignoring the possability that the previous frame does not yet + exist, the ``frame_register (FRAME)'' function is expanded to + ``frame_register_unwind (get_next_frame (FRAME)'' and hence that + call will expand to ``frame_register_unwind (get_next_frame + (get_prev_frame (NEXT_FRAME)))''. Might as well call + ``frame_register_unwind (NEXT_FRAME)'' directly. + + THIS_PROLOGUE_CACHE can be used to share any prolog analysis data + with the other unwind methods. Memory for that cache should be + allocated using frame_obstack_zalloc(). */ + +typedef void (frame_prev_register_ftype) (struct frame_info *next_frame, + void **this_prologue_cache, + int prev_regnum, + int *optimized, + enum lval_type * lvalp, + CORE_ADDR *addrp, + int *realnump, void *valuep); struct frame_unwind { /* Should the frame's type go here? */ /* Should an attribute indicating the frame's address-in-block go here? */ - frame_unwind_id_ftype *id; - frame_unwind_reg_ftype *reg; + frame_this_id_ftype *this_id; + frame_prev_register_ftype *prev_register; }; #endif diff --git a/gdb/frame.c b/gdb/frame.c index 178dfdfe5d9..900b51518c5 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -254,9 +254,12 @@ frame_register_unwind (struct frame_info *frame, int regnum, detected the problem before calling here. */ gdb_assert (frame != NULL); - /* Ask this frame to unwind its register. */ - frame->unwind->reg (frame, &frame->unwind_cache, regnum, - optimizedp, lvalp, addrp, realnump, bufferp); + /* Ask this frame to unwind its register. See comment in + "frame-unwind.h" for why NEXT frame and this unwind cace are + passed in. */ + frame->unwind->prev_register (frame->next, &frame->prologue_cache, regnum, + optimizedp, lvalp, addrp, realnump, bufferp); + } void @@ -490,7 +493,7 @@ create_sentinel_frame (struct regcache *regcache) /* Explicitly initialize the sentinel frame's cache. Provide it with the underlying regcache. In the future additional information, such as the frame's thread will be added. */ - frame->unwind_cache = sentinel_frame_cache (regcache); + frame->prologue_cache = sentinel_frame_cache (regcache); /* For the moment there is only one sentinel frame implementation. */ frame->unwind = sentinel_frame_unwind; /* Link this frame back to itself. The frame is self referential @@ -647,19 +650,20 @@ select_frame (struct frame_info *fi) most frame. */ static void -frame_saved_regs_register_unwind (struct frame_info *frame, void **cache, - int regnum, int *optimizedp, - enum lval_type *lvalp, CORE_ADDR *addrp, - int *realnump, void *bufferp) +legacy_saved_regs_prev_register (struct frame_info *next_frame, + void **this_prologue_cache, + int regnum, int *optimizedp, + enum lval_type *lvalp, CORE_ADDR *addrp, + int *realnump, void *bufferp) { - /* There is always a frame at this point. And THIS is the frame - we're interested in. */ + /* HACK: New code is passed the next frame and this cache. + Unfortunatly, old code expects this frame. Since this is a + backward compatibility hack, cheat by walking one level along the + prologue chain to the frame the old code expects. + + Do not try this at home. Professional driver, closed course. */ + struct frame_info *frame = next_frame->prev; gdb_assert (frame != NULL); - /* If we're using generic dummy frames, we'd better not be in a call - dummy. (generic_call_dummy_register_unwind ought to have been called - instead.) */ - gdb_assert (!(DEPRECATED_USE_GENERIC_DUMMY_FRAMES - && (get_frame_type (frame) == DUMMY_FRAME))); /* Only (older) architectures that implement the DEPRECATED_FRAME_INIT_SAVED_REGS method should be using this @@ -697,13 +701,13 @@ frame_saved_regs_register_unwind (struct frame_info *frame, void **cache, #if 1 /* Save each register value, as it is read in, in a frame based cache. */ - void **regs = (*cache); + void **regs = (*this_prologue_cache); if (regs == NULL) { int sizeof_cache = ((NUM_REGS + NUM_PSEUDO_REGS) * sizeof (void *)); regs = frame_obstack_zalloc (sizeof_cache); - (*cache) = regs; + (*this_prologue_cache) = regs; } if (regs[regnum] == NULL) { @@ -723,22 +727,33 @@ frame_saved_regs_register_unwind (struct frame_info *frame, void **cache, return; } - /* No luck, assume this and the next frame have the same register - value. Pass the request down the frame chain to the next frame. - Hopefully that will find the register's location, either in a - register or in memory. */ - frame_register (frame, regnum, optimizedp, lvalp, addrp, realnump, - bufferp); + /* No luck. Assume this and the next frame have the same register + value. Pass the unwind request down the frame chain to the next + frame. Hopefully that frame will find the register's location. */ + frame_register_unwind (next_frame, regnum, optimizedp, lvalp, addrp, + realnump, bufferp); } static void -frame_saved_regs_id_unwind (struct frame_info *next_frame, void **cache, - struct frame_id *id) +legacy_saved_regs_this_id (struct frame_info *next_frame, + void **this_prologue_cache, + struct frame_id *id) { int fromleaf; CORE_ADDR base; CORE_ADDR pc; + if (frame_relative_level (next_frame) < 0) + { + /* FIXME: cagney/2003-03-14: We've got the extra special case of + unwinding a sentinel frame, the PC of which is pointing at a + stack dummy. Fake up the dummy frame's ID using the same + sequence as is found a traditional unwinder. */ + (*id).base = read_fp (); + (*id).pc = read_pc (); + return; + } + /* Start out by assuming it's NULL. */ (*id) = null_frame_id; @@ -792,15 +807,14 @@ frame_saved_regs_id_unwind (struct frame_info *next_frame, void **cache, id->base = base; } -const struct frame_unwind trad_frame_unwinder = { - frame_saved_regs_id_unwind, - frame_saved_regs_register_unwind +const struct frame_unwind legacy_saved_regs_unwinder = { + legacy_saved_regs_this_id, + legacy_saved_regs_prev_register }; -const struct frame_unwind *trad_frame_unwind = &trad_frame_unwinder; +const struct frame_unwind *legacy_saved_regs_unwind = &legacy_saved_regs_unwinder; /* Function: deprecated_generic_get_saved_register - Find register number REGNUM relative to FRAME and put its (raw, target format) contents in *RAW_BUFFER. @@ -1496,63 +1510,57 @@ get_prev_frame (struct frame_info *this_frame) prev_frame->pc); /* Find the prev's frame's ID. */ - switch (prev_frame->type) + + /* The callee expects to be invoked with: + + this->unwind->this_id (this->next, &this->cache, &this->id); + + The below is carefully shifted one frame `to the left' so that + both the unwind->this_id and unwind->prev_register methods are + consistently invoked with NEXT_FRAME and THIS_PROLOGUE_CACHE. + + Also note that, while the PC for this new previous frame was + unwound first (see above), the below is the first call that + [potentially] requires analysis of the new previous frame's + prologue. Consequently, it is this call, that typically ends up + initializing the previous frame's prologue cache. */ + prev_frame->unwind->this_id (this_frame, + &prev_frame->prologue_cache, + &prev_frame->id); + + /* Check that the unwound ID is valid. */ + if (!frame_id_p (prev_frame->id)) { - case DUMMY_FRAME: - /* When unwinding a normal frame, the stack structure is - determined by analyzing the frame's function's code (be it - using brute force prologue analysis, or the dwarf2 CFI). In - the case of a dummy frame, that simply isn't possible. The - The PC is either the program entry point, or some random - address on the stack. Trying to use that PC to apply - standard frame ID unwind techniques is just asking for - trouble. */ - gdb_assert (gdbarch_unwind_dummy_id_p (current_gdbarch)); - /* Assume hand_function_call(), via SAVE_DUMMY_FRAME_TOS, - previously saved the dummy frame's ID. Things only work if - the two return the same value. */ - gdb_assert (SAVE_DUMMY_FRAME_TOS_P ()); - /* Use an architecture specific method to extract the prev's - dummy ID from the next frame. Note that this method uses - frame_register_unwind to obtain the register values needed to - determine the dummy frame's ID. */ - prev_frame->id = gdbarch_unwind_dummy_id (current_gdbarch, this_frame); - break; - case NORMAL_FRAME: - case SIGTRAMP_FRAME: - /* FIXME: cagney/2003-03-04: The below call isn't right. It - should instead be doing something like "prev_frame -> unwind - -> id (this_frame, & prev_frame -> unwind_cache, & prev_frame - -> id)" but that requires more extensive (pending) changes. */ - this_frame->unwind->id (this_frame, &this_frame->unwind_cache, - &prev_frame->id); - /* Check that the unwound ID is valid. */ - if (!frame_id_p (prev_frame->id)) - { - if (frame_debug) - fprintf_unfiltered (gdb_stdlog, - "Outermost frame - unwound frame ID invalid\n"); - return NULL; - } - /* Check that the new frame isn't inner to (younger, below, - next) the old frame. If that happens the frame unwind is - going backwards. */ - /* FIXME: cagney/2003-02-25: Ignore the sentinel frame since - that doesn't have a valid frame ID. Should instead set the - sentinel frame's frame ID to a `sentinel'. Leave it until - after the switch to storing the frame ID, instead of the - frame base, in the frame object. */ - if (this_frame->level >= 0 - && frame_id_inner (prev_frame->id, get_frame_id (this_frame))) - error ("Unwound frame inner-to selected frame (corrupt stack?)"); - /* Note that, due to frameless functions, the stronger test of - the new frame being outer to the old frame can't be used - - frameless functions differ by only their PC value. */ - break; - default: - internal_error (__FILE__, __LINE__, "bad switch"); + if (frame_debug) + fprintf_unfiltered (gdb_stdlog, + "Outermost frame - unwound frame ID invalid\n"); + return NULL; } + /* Check that the new frame isn't inner to (younger, below, next) + the old frame. If that happens the frame unwind is going + backwards. */ + /* FIXME: cagney/2003-02-25: Ignore the sentinel frame since that + doesn't have a valid frame ID. Should instead set the sentinel + frame's frame ID to a true `sentinel'. Leave it until after the + switch to storing the frame ID, instead of the frame base, in the + frame object. */ + if (this_frame->level >= 0 + && frame_id_inner (prev_frame->id, get_frame_id (this_frame))) + error ("Unwound frame inner-to selected frame (corrupt stack?)"); + + /* FIXME: cagney/2003-03-14: Should check that this and next frame's + IDs are different (i.e., !frame_id_eq()). Can't yet do that as + the EQ function doesn't yet compare PC values. */ + + /* FIXME: cagney/2003-03-14: Should delay the evaluation of the + frame ID until when it is needed. That way the inner most frame + can be created without needing to do prologue analysis. */ + + /* Note that, due to frameless functions, the stronger test of the + new frame being outer to the old frame can't be used - frameless + functions differ by only their PC value. */ + /* FIXME: cagney/2002-12-18: Instead of this hack, should only store the frame ID in PREV_FRAME. Unfortunatly, some architectures (HP/UX) still reply on EXTRA_FRAME_INFO and, hence, still poke at diff --git a/gdb/frame.h b/gdb/frame.h index c4905dd2e13..ee0fc7e49b1 100644 --- a/gdb/frame.h +++ b/gdb/frame.h @@ -27,8 +27,9 @@ struct symtab_and_line; struct frame_unwind; struct block; -/* The traditional frame unwinder. */ -extern const struct frame_unwind *trad_frame_unwind; +/* A legacy unwinder to prop up architectures using the old style + saved regs array. */ +extern const struct frame_unwind *legacy_saved_regs_unwind; /* The frame object. */ @@ -397,9 +398,9 @@ struct frame_info related unwind data. */ struct context *context; - /* Unwind cache shared between the unwind functions - they had - better all agree as to the contents. */ - void *unwind_cache; + /* Prologue cache shared between the unwind functions. See + "frame-unwind.h" for more information. */ + void *prologue_cache; /* The frame's unwinder. */ const struct frame_unwind *unwind; diff --git a/gdb/sentinel-frame.c b/gdb/sentinel-frame.c index 961e0300ea6..e4060d27767 100644 --- a/gdb/sentinel-frame.c +++ b/gdb/sentinel-frame.c @@ -45,13 +45,13 @@ sentinel_frame_cache (struct regcache *regcache) /* Here the register value is taken direct from the register cache. */ void -sentinel_frame_register_unwind (struct frame_info *frame, - void **unwind_cache, - int regnum, int *optimized, - enum lval_type *lvalp, CORE_ADDR *addrp, - int *realnum, void *bufferp) +sentinel_frame_prev_register (struct frame_info *next_frame, + void **this_prologue_cache, + int regnum, int *optimized, + enum lval_type *lvalp, CORE_ADDR *addrp, + int *realnum, void *bufferp) { - struct frame_unwind_cache *cache = *unwind_cache; + struct frame_unwind_cache *cache = *this_prologue_cache; /* Describe the register's location. A reg-frame maps all registers onto the corresponding hardware register. */ *optimized = 0; @@ -71,22 +71,20 @@ sentinel_frame_register_unwind (struct frame_info *frame, } void -sentinel_frame_id_unwind (struct frame_info *frame, - void **cache, - struct frame_id *id) +sentinel_frame_this_id (struct frame_info *next_frame, + void **this_prologue_cache, + struct frame_id *this_id) { - /* FIXME: cagney/2003-01-08: This should be using a per-architecture - method that doesn't suffer from DECR_PC_AFTER_BREAK problems. - Such a method would take unwind_cache, regcache and stop reason - parameters. */ - id->base = read_fp (); - id->pc = read_pc (); + /* The sentinel frame is used as a starting point for creating the + previous (inner most) frame. That frame's THIS_ID method will be + called to determine the inner most frame's ID. Not this one. */ + internal_error (__FILE__, __LINE__, "sentinel_frame_this_id called"); } const struct frame_unwind sentinel_frame_unwinder = { - sentinel_frame_id_unwind, - sentinel_frame_register_unwind + sentinel_frame_this_id, + sentinel_frame_prev_register }; const struct frame_unwind *const sentinel_frame_unwind = &sentinel_frame_unwinder; -- 2.30.2