* [offbyone RFC] Merge i386newframe
@ 2003-03-12 22:06 Michal Ludvig
2003-03-12 22:42 ` Daniel Jacobowitz
` (2 more replies)
0 siblings, 3 replies; 14+ messages in thread
From: Michal Ludvig @ 2003-03-12 22:06 UTC (permalink / raw)
To: Andrew Cagney, GDB Patches; +Cc: Mark Kettenis
[-- Attachment #1: Type: text/plain, Size: 1160 bytes --]
Hi Andrew,
this patch is a merge of Mark's i386newframe-branch to your
offbyone-branch. It works quite well, unwinds through signal frames and
frameless functions, but still has some strange regressions that aren't
present in i386newframe branch.
For example this program:
int var;
void foo ()
{
var = 0x1234;
}
int main ()
{
foo ();
return 0;
}
Running it in the GDB:
(gdb) b main
Breakpoint 1 at 0x804832c: file tst.c, line 8.
(gdb) r
Starting program: tst
Breakpoint 1, main () at tst.c:8
8 foo ();
(gdb) n
Program exited normally.
(gdb)
Why it didn't stop on "return 0" but exitted immediately instead? Did
you observe this behaviour in offbyone branch too, or is it caused by
this patch? It seems to have something to do with symbols, linetables,
... but I can't see how it could be related to this patch. It doesn't
matter if I compile the program with stabs or dwarf2 debug info. Ideas?
Anyway comments on the code are welcome. As soon as i386newframe is
merged to offbyone I'll start porting x86-64 on top of it as well.
Michal Ludvig
--
* SuSE CR, s.r.o * mludvig@suse.cz
* (+420) 296.545.373 * http://www.suse.cz
[-- Attachment #2: merge-i386-1.diff --]
[-- Type: text/plain, Size: 26996 bytes --]
2003-03-12 Michal Ludvig <mludvig@suse.cz>
* frame.c (get_prev_frame): Link prev_frame to this_frame
as soon as possible.
* i386-tdep.c (i386_frameless_signal_p, i386_frame_chain)
(i386_sigtramp_saved_pc, i386_sigtramp_saved_sp)
(i386_frame_saved_pc, i386_frame_init_saved_regs): Removed.
(i386_frame_cache): New structure.
(i386_unwind_pc): New method.
(i386_frame_cache, i386_frame_pc_unwind, i386_frame_unwind)
(i386_frame_id_unwind, i386_frame_register_unwind): New
stuff for unwinding normal frames.
(i386_sigtramp_cache, i386_sigtramp_pc_unwind)
(i386_sigtramp_id_unwind, i386_sigtramp_register_unwind)
(i386_sigtramp_unwind): New stuff for unwinding sigtramp
frames.
(i386_frame_p): New.
(i386_unwind_dummy_id, i386_save_dummy_frame_tos): New.
(i386_pc_in_sigtramp): Doesn't need name anymore.
(i386_gdbarch_init): Updated to use new unwinding methods.
Index: frame.c
===================================================================
RCS file: /cvs/src/src/gdb/frame.c,v
retrieving revision 1.71.2.7
diff -u -p -r1.71.2.7 frame.c
--- frame.c 11 Mar 2003 23:00:26 -0000 1.71.2.7
+++ frame.c 12 Mar 2003 16:28:15 -0000
@@ -1282,7 +1282,6 @@ get_prev_frame (struct frame_info *this_
/* Only try to do the unwind once. */
if (this_frame->prev_p)
return this_frame->prev;
- this_frame->prev_p = 1;
/* If we're inside the entry file, it isn't valid. Don't apply this
test to a dummy frame - dummy frame PC's typically land in the
@@ -1351,6 +1350,11 @@ get_prev_frame (struct frame_info *this_
prev_frame = FRAME_OBSTACK_ZALLOC (struct frame_info);
prev_frame->level = this_frame->level + 1;
+ /* Link it in. */
+ this_frame->prev = prev_frame;
+ prev_frame->next = this_frame;
+ this_frame->prev_p = 1;
+
/* Try to unwind the PC. If that doesn't work, assume we've reached
the oldest frame and simply return. Is there a better sentinal
value? The unwound PC value is then used to initialize the new
@@ -1466,10 +1470,6 @@ get_prev_frame (struct frame_info *this_
(HP/UX) still reply on EXTRA_FRAME_INFO and, hence, still poke at
the "struct frame_info" object directly. */
prev_frame->frame = prev_frame->id.base;
-
- /* Link it in. */
- this_frame->prev = prev_frame;
- prev_frame->next = this_frame;
/* FIXME: cagney/2002-01-19: This call will go away. Instead of
initializing extra info, all frames will use the frame_cache
Index: i386-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/i386-tdep.c,v
retrieving revision 1.116
diff -u -p -r1.116 i386-tdep.c
--- i386-tdep.c 3 Mar 2003 20:50:18 -0000 1.116
+++ i386-tdep.c 12 Mar 2003 16:28:15 -0000
@@ -469,64 +476,8 @@ i386_get_frame_setup (CORE_ADDR pc)
return (-1);
}
-/* Signal trampolines don't have a meaningful frame. The frame
- pointer value we use is actually the frame pointer of the calling
- frame -- that is, the frame which was in progress when the signal
- trampoline was entered. GDB mostly treats this frame pointer value
- as a magic cookie. We detect the case of a signal trampoline by
- testing for get_frame_type() == SIGTRAMP_FRAME, which is set based
- on PC_IN_SIGTRAMP.
-
- When a signal trampoline is invoked from a frameless function, we
- essentially have two frameless functions in a row. In this case,
- we use the same magic cookie for three frames in a row. We detect
- this case by seeing whether the next frame is a SIGTRAMP_FRAME,
- and, if it does, checking whether the current frame is actually
- frameless. In this case, we need to get the PC by looking at the
- SP register value stored in the signal context.
-
- This should work in most cases except in horrible situations where
- a signal occurs just as we enter a function but before the frame
- has been set up. Incidentally, that's just what happens when we
- call a function from GDB with a signal pending (there's a test in
- the testsuite that makes this happen). Therefore we pretend that
- we have a frameless function if we're stopped at the start of a
- function. */
-
-/* Return non-zero if we're dealing with a frameless signal, that is,
- a signal trampoline invoked from a frameless function. */
-
-int
-i386_frameless_signal_p (struct frame_info *frame)
-{
- return (get_next_frame (frame)
- && get_frame_type (get_next_frame (frame)) == SIGTRAMP_FRAME
- && (frameless_look_for_prologue (frame)
- || get_frame_pc (frame) == get_pc_function_start (get_frame_pc (frame))));
-}
-
-/* Return the chain-pointer for FRAME. In the case of the i386, the
- frame's nominal address is the address of a 4-byte word containing
- the calling frame's address. */
-
-static CORE_ADDR
-i386_frame_chain (struct frame_info *frame)
-{
- if (pc_in_dummy_frame (get_frame_pc (frame)))
- return get_frame_base (frame);
-
- if (get_frame_type (frame) == SIGTRAMP_FRAME
- || i386_frameless_signal_p (frame))
- return get_frame_base (frame);
-
- if (! inside_entry_file (get_frame_pc (frame)))
- return read_memory_unsigned_integer (get_frame_base (frame), 4);
-
- return 0;
-}
-
/* Determine whether the function invocation represented by FRAME does
- not have a from on the stack associated with it. If it does not,
+ not have a frame on the stack associated with it. If it does not,
return non-zero, otherwise return zero. */
static int
@@ -538,66 +489,16 @@ i386_frameless_function_invocation (stru
return frameless_look_for_prologue (frame);
}
-/* Assuming FRAME is for a sigtramp routine, return the saved program
- counter. */
-
-static CORE_ADDR
-i386_sigtramp_saved_pc (struct frame_info *frame)
-{
- struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
- CORE_ADDR addr;
-
- addr = tdep->sigcontext_addr (frame);
- return read_memory_unsigned_integer (addr + tdep->sc_pc_offset, 4);
-}
-
-/* Assuming FRAME is for a sigtramp routine, return the saved stack
- pointer. */
-
-static CORE_ADDR
-i386_sigtramp_saved_sp (struct frame_info *frame)
-{
- struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
- CORE_ADDR addr;
-
- addr = tdep->sigcontext_addr (frame);
- return read_memory_unsigned_integer (addr + tdep->sc_sp_offset, 4);
-}
-
-/* Return the saved program counter for FRAME. */
-
-static CORE_ADDR
-i386_frame_saved_pc (struct frame_info *frame)
-{
- if (pc_in_dummy_frame (get_frame_pc (frame)))
- {
- ULONGEST pc;
-
- frame_unwind_unsigned_register (frame, PC_REGNUM, &pc);
- return pc;
- }
-
- if (get_frame_type (frame) == SIGTRAMP_FRAME)
- return i386_sigtramp_saved_pc (frame);
-
- if (i386_frameless_signal_p (frame))
- {
- CORE_ADDR sp = i386_sigtramp_saved_sp (get_next_frame (frame));
- return read_memory_unsigned_integer (sp, 4);
- }
-
- return read_memory_unsigned_integer (get_frame_base (frame) + 4, 4);
-}
-
/* Immediately after a function call, return the saved pc. */
static CORE_ADDR
i386_saved_pc_after_call (struct frame_info *frame)
{
- if (get_frame_type (frame) == SIGTRAMP_FRAME)
- return i386_sigtramp_saved_pc (frame);
+ char buf[4];
- return read_memory_unsigned_integer (read_register (SP_REGNUM), 4);
+ /* Our frame unwinder handles this just fine. */
+ frame_unwind_register (frame, PC_REGNUM, buf);
+ return extract_address (buf, 4);
}
/* Return number of args passed to a frame.
@@ -674,72 +575,6 @@ i386_frame_num_args (struct frame_info *
#endif
}
-/* Parse the first few instructions the function to see what registers
- were stored.
-
- We handle these cases:
-
- The startup sequence can be at the start of the function, or the
- function can start with a branch to startup code at the end.
-
- %ebp can be set up with either the 'enter' instruction, or "pushl
- %ebp, movl %esp, %ebp" (`enter' is too slow to be useful, but was
- once used in the System V compiler).
-
- Local space is allocated just below the saved %ebp by either the
- 'enter' instruction, or by "subl $<size>, %esp". 'enter' has a 16
- bit unsigned argument for space to allocate, and the 'addl'
- instruction could have either a signed byte, or 32 bit immediate.
-
- Next, the registers used by this function are pushed. With the
- System V compiler they will always be in the order: %edi, %esi,
- %ebx (and sometimes a harmless bug causes it to also save but not
- restore %eax); however, the code below is willing to see the pushes
- in any order, and will handle up to 8 of them.
-
- If the setup sequence is at the end of the function, then the next
- instruction will be a branch back to the start. */
-
-static void
-i386_frame_init_saved_regs (struct frame_info *fip)
-{
- long locals = -1;
- unsigned char op;
- CORE_ADDR addr;
- CORE_ADDR pc;
- int i;
-
- if (get_frame_saved_regs (fip))
- return;
-
- frame_saved_regs_zalloc (fip);
-
- pc = get_pc_function_start (get_frame_pc (fip));
- if (pc != 0)
- locals = i386_get_frame_setup (pc);
-
- if (locals >= 0)
- {
- addr = get_frame_base (fip) - 4 - locals;
- for (i = 0; i < 8; i++)
- {
- op = codestream_get ();
- if (op < 0x50 || op > 0x57)
- break;
-#ifdef I386_REGNO_TO_SYMMETRY
- /* Dynix uses different internal numbering. Ick. */
- get_frame_saved_regs (fip)[I386_REGNO_TO_SYMMETRY (op - 0x50)] = addr;
-#else
- get_frame_saved_regs (fip)[op - 0x50] = addr;
-#endif
- addr -= 4;
- }
- }
-
- get_frame_saved_regs (fip)[PC_REGNUM] = get_frame_base (fip) + 4;
- get_frame_saved_regs (fip)[FP_REGNUM] = get_frame_base (fip);
-}
-
/* Return PC of first real instruction. */
static CORE_ADDR
@@ -855,37 +690,423 @@ i386_push_return_address (CORE_ADDR pc,
write_memory (sp - 4, buf, 4);
return sp - 4;
}
+\f
+#include "frame-unwind.h"
+
+#define regcache_cooked_write_unsigned regcache_raw_write_unsigned
+
+#ifdef I386_REGNO_TO_SYMMETRY
+#error "The Sequent Symmetry is no longer supported."
+#endif
+
+/* According to the System V ABI, the registers %ebp, %ebx, %edi, %esi
+ and %esp "belong" to the calling function. */
+
+/* The maximum number of saved registers. This should include all
+ registers mentioned above, and %eip. */
+#define I386_NUM_SAVED_REGISTERS 9
+
+struct i386_frame_cache
+{
+ CORE_ADDR saved_regs[I386_NUM_SAVED_REGISTERS];
+ CORE_ADDR saved_sp;
+ CORE_ADDR return_pc;
+ CORE_ADDR base;
+ int frameless;
+};
+
+/* Parse the first few instructions the function to see what registers
+ were stored.
+
+ We handle these cases:
+
+ The startup sequence can be at the start of the function, or the
+ function can start with a branch to startup code at the end.
+
+ %ebp can be set up with either the 'enter' instruction, or "pushl
+ %ebp, movl %esp, %ebp" (`enter' is too slow to be useful, but was
+ once used in the System V compiler).
+
+ Local space is allocated just below the saved %ebp by either the
+ 'enter' instruction, or by "subl $<size>, %esp". 'enter' has a 16
+ bit unsigned argument for space to allocate, and the 'addl'
+ instruction could have either a signed byte, or 32 bit immediate.
+
+ Next, the registers used by this function are pushed. With the
+ System V compiler they will always be in the order: %edi, %esi,
+ %ebx (and sometimes a harmless bug causes it to also save but not
+ restore %eax); however, the code below is willing to see the pushes
+ in any order, and will handle up to 8 of them.
+
+ If the setup sequence is at the end of the function, then the next
+ instruction will be a branch back to the start. */
+
+static struct i386_frame_cache *
+i386_frame_cache (struct frame_info *frame, void **cachep)
+{
+ struct i386_frame_cache *cache;
+ CORE_ADDR pc, start_pc;
+
+ if (*cachep)
+ return *cachep;
+
+ cache = FRAME_OBSTACK_ZALLOC (struct i386_frame_cache);
+
+ pc = frame_pc_unwind (frame);
+ start_pc = get_pc_function_start (pc);
+
+ long locals = -1;
+
+ if (start_pc != 0)
+ locals = i386_get_frame_setup (start_pc);
+
+ if (locals >= 0 && pc > start_pc)
+ {
+ ULONGEST fp;
+ CORE_ADDR addr;
+ unsigned char op;
+ int i;
+
+ frame_unwind_unsigned_register (frame, FP_REGNUM, &fp);
+ cache->base = fp;
+
+ addr = fp - 4 - locals;
+ for (i = 0; i < I386_NUM_SAVED_REGISTERS; i++)
+ {
+ op = codestream_get ();
+ if (op < 0x50 || op > 0x57)
+ break;
+
+ cache->saved_regs[op - 0x50] = addr;
+ addr -= 4;
+ }
+
+ cache->saved_regs[PC_REGNUM] = fp + 4;
+ cache->saved_regs[FP_REGNUM] = fp;
+ cache->saved_sp = fp + 8;
+ }
+ else
+ {
+ ULONGEST sp;
+
+ /* This function is either frameless or we're at the start
+ of the function and this function's frame hasn't been
+ setup yet. In the latter case only %eip and %esp have
+ changed, and we can determine their previous values. We
+ pretend we can do the same in the former case. */
+ cache->frameless = 1;
+
+ frame_unwind_unsigned_register (frame, SP_REGNUM, &sp);
+ cache->saved_regs[PC_REGNUM] = sp;
+ cache->saved_sp = cache->saved_regs[PC_REGNUM] + 4;
+ cache->base = sp;
+ }
+
+ *cachep = cache;
+ return cache;
+}
static void
-i386_do_pop_frame (struct frame_info *frame)
+i386_frame_pop (struct frame_info *frame, void **cachep,
+ struct regcache *regcache)
{
- CORE_ADDR fp;
+ CORE_ADDR fp = get_frame_base (frame);
int regnum;
- char regbuf[I386_MAX_REGISTER_SIZE];
+ char buf[4];
+ ULONGEST val;
- fp = get_frame_base (frame);
- i386_frame_init_saved_regs (frame);
+ gdb_assert (get_frame_type (frame) != DUMMY_FRAME);
- for (regnum = 0; regnum < NUM_REGS; regnum++)
+ for (regnum = 0; regnum < I386_NUM_SAVED_REGISTERS; regnum++)
{
- CORE_ADDR addr;
- addr = get_frame_saved_regs (frame)[regnum];
- if (addr)
+ frame_unwind_register (frame, regnum, buf);
+ regcache_cooked_write (regcache, regnum, buf);
+ }
+
+ /* Reset the direction flag. */
+ regcache_cooked_read_unsigned (regcache, PS_REGNUM, &val);
+ val &= ~(1 << 10);
+ regcache_cooked_write_unsigned (regcache, PS_REGNUM, val);
+
+ /* The following sequence restores %ebp, %eip and %esp. */
+ read_memory (fp, buf, 4);
+ regcache_cooked_write (regcache, FP_REGNUM, buf);
+ read_memory (fp + 4, buf, 4);
+ regcache_cooked_write (regcache, PC_REGNUM, buf);
+ regcache_cooked_write_unsigned (regcache, SP_REGNUM, fp + 8);
+
+ flush_cached_frames ();
+}
+
+static CORE_ADDR
+i386_frame_pc_unwind (struct frame_info *frame, void **cachep)
+{
+ struct i386_frame_cache *cache = i386_frame_cache (frame, cachep);
+
+ gdb_assert (get_frame_type (frame) != DUMMY_FRAME);
+
+ if (cache->return_pc == 0)
+ {
+ char buf[4];
+
+ frame_unwind_register (frame, PC_REGNUM, buf);
+ cache->return_pc = extract_address (buf, 4);
+ }
+
+ return cache->return_pc;
+}
+
+static CORE_ADDR
+i386_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ ULONGEST pc;
+ frame_unwind_unsigned_register (next_frame, PC_REGNUM, &pc);
+ return (CORE_ADDR) pc;
+}
+
+static void
+i386_frame_id_unwind (struct frame_info *next_frame, void **cachep,
+ struct frame_id *id)
+{
+ struct i386_frame_cache *cache = i386_frame_cache (next_frame, cachep);
+
+ gdb_assert (cache);
+ gdb_assert (get_frame_type (next_frame) != DUMMY_FRAME);
+
+ /* Start with a NULL next_frame ID. */
+ *id = null_frame_id;
+
+ /* Unwind PC first */
+ id->pc = frame_pc_unwind (next_frame);
+ cache->return_pc = id->pc;
+
+ /* The next_frame's base is the address of a 4-byte word containing the
+ calling next_frame's address.
+
+ Signal trampolines don't have a meaningful next_frame. The frame
+ pointer value we use is actually the next_frame pointer of the calling
+ next_frame -- that is, the frame which was in progress when the signal
+ trampoline was entered. GDB mostly treats this next_frame pointer
+ value as a magic cookie. We detect the case of a signal
+ trampoline by testing for get_frame_type() == SIGTRAMP_FRAME,
+ which is set based on PC_IN_SIGTRAMP. */
+
+ if (get_frame_type (next_frame) == SIGTRAMP_FRAME || cache->frameless)
+ id->base = get_frame_base (next_frame);
+ else
+ if (!inside_entry_file (get_frame_pc (next_frame)))
+ id->base = cache->base;
+}
+
+static void
+i386_frame_register_unwind (struct frame_info *frame, void **cachep,
+ int regnum, int *optimizedp,
+ enum lval_type *lvalp, CORE_ADDR *addrp,
+ int *realnump, void *valuep)
+{
+ /* FIXME: kettenis/20030302: I don't understand why the cache isn't
+ already initialized. */
+ struct i386_frame_cache *cache = i386_frame_cache (frame, cachep);
+
+ gdb_assert (cache);
+ gdb_assert (get_frame_type (frame) != DUMMY_FRAME);
+ gdb_assert (regnum >= 0);
+
+ if (regnum == SP_REGNUM && cache->saved_sp)
+ {
+ *optimizedp = 0;
+ *lvalp = not_lval;
+ *addrp = 0;
+ *realnump = -1;
+ if (valuep)
{
- read_memory (addr, regbuf, REGISTER_RAW_SIZE (regnum));
- deprecated_write_register_gen (regnum, regbuf);
+ /* Store the value. */
+ store_address (valuep, 4, cache->saved_regs[SP_REGNUM]);
}
+ return;
}
- write_register (FP_REGNUM, read_memory_integer (fp, 4));
- write_register (PC_REGNUM, read_memory_integer (fp + 4, 4));
- write_register (SP_REGNUM, fp + 8);
- flush_cached_frames ();
+
+ if (regnum == PC_REGNUM)
+ {
+ CORE_ADDR pc;
+ *optimizedp = 0;
+ *lvalp = not_lval;
+ *addrp = 0;
+ *realnump = -1;
+ pc = i386_frame_pc_unwind (frame, cachep);
+ if (valuep)
+ store_address (valuep, 4, pc);
+ }
+
+ if (regnum < I386_NUM_SAVED_REGISTERS && cache->saved_regs[regnum] != 0)
+ {
+ *optimizedp = 0;
+ *lvalp = lval_memory;
+ *addrp = cache->saved_regs[regnum];
+ *realnump = -1;
+ if (valuep)
+ {
+ /* Read the value in from memory. */
+ read_memory (*addrp, valuep,
+ register_size (current_gdbarch, regnum));
+ }
+ return;
+ }
+
+ frame_register (frame, regnum, optimizedp, lvalp, addrp, realnump, valuep);
+}
+
+static struct i386_frame_cache *
+i386_sigtramp_cache (struct frame_info *frame, void **cachep)
+{
+ struct i386_frame_cache *cache;
+ CORE_ADDR pc, start_pc, addr;
+ struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+ int sc_offset, regnum;
+
+ if (*cachep)
+ return *cachep;
+
+ cache = FRAME_OBSTACK_ZALLOC (struct i386_frame_cache);
+
+ pc = frame_pc_unwind (frame);
+ start_pc = get_pc_function_start (pc);
+ addr = tdep->sigcontext_addr (frame->prev);
+
+ for (regnum=0; regnum<=PS_REGNUM; regnum++)
+ {
+ /* See <asm/sigcontext.h> for details on how registers are stored
+ in the sigcontext. */
+ if (regnum < PC_REGNUM)
+ sc_offset = (11 - regnum);
+ else if (regnum == PC_REGNUM)
+ sc_offset = 14;
+ else if (regnum == PS_REGNUM)
+ sc_offset = 16;
+ else
+ break;
+
+ sc_offset *= 4;
+
+ if (regnum < sizeof (cache->saved_regs))
+ cache->saved_regs[regnum] = addr + sc_offset;
+ }
+
+ cache->base = addr;
+
+ *cachep = cache;
+ return cache;
+}
+
+static void
+i386_sigtramp_register_unwind (struct frame_info *frame, void **cachep,
+ int regnum, int *optimizedp,
+ enum lval_type *lvalp, CORE_ADDR *addrp,
+ int *realnump, void *valuep)
+{
+ printf_debug ("## %s(%p, %p, %s(%d))\n", __func__, (void*)frame->frame,
+ (void*)frame->pc, i386_register_name(regnum), regnum);
+
+ struct i386_frame_cache *cache = i386_sigtramp_cache (frame, cachep);
+ char buf[4];
+ CORE_ADDR addr;
+
+ gdb_assert (cache);
+ gdb_assert (regnum >= 0);
+
+ if (regnum < I386_NUM_SAVED_REGISTERS && cache->saved_regs[regnum] != 0)
+ {
+ *optimizedp = 0;
+ *lvalp = lval_memory;
+ *addrp = cache->saved_regs[regnum];
+ *realnump = -1;
+ if (valuep)
+ read_memory (*addrp, valuep,
+ register_size (current_gdbarch, regnum));
+ return;
+ }
+
+ /* Fall back for non-saved registers. */
+ frame_register (frame, regnum, optimizedp, lvalp, addrp, realnump, valuep);
}
static void
-i386_pop_frame (void)
+i386_sigtramp_id_unwind (struct frame_info *next_frame, void **cachep,
+ struct frame_id *id)
+{
+ printf_debug ("## %s(%p, %p)\n", __func__, (void*)next_frame->frame,
+ (void*)next_frame->pc);
+ struct i386_frame_cache *cache = i386_sigtramp_cache (next_frame, cachep);
+
+ gdb_assert (cache);
+ gdb_assert (get_frame_type (next_frame) != DUMMY_FRAME);
+
+ /* Start with a NULL next_frame ID. */
+ *id = null_frame_id;
+
+ /* Unwind PC first */
+ id->pc = frame_pc_unwind (next_frame);
+ cache->return_pc = id->pc;
+
+ /* The next_frame's base is the address of a 4-byte word containing the
+ calling next_frame's address.
+
+ Signal trampolines don't have a meaningful next_frame. The frame
+ pointer value we use is actually the next_frame pointer of the calling
+ next_frame -- that is, the frame which was in progress when the signal
+ trampoline was entered. GDB mostly treats this next_frame pointer
+ value as a magic cookie. We detect the case of a signal
+ trampoline by testing for get_frame_type() == SIGTRAMP_FRAME,
+ which is set based on PC_IN_SIGTRAMP. */
+
+ if (get_frame_type (next_frame) == SIGTRAMP_FRAME || cache->frameless)
+ id->base = get_frame_base (next_frame);
+ else
+ if (!inside_entry_file (get_frame_pc (next_frame)))
+ id->base = cache->base;
+}
+
+static struct frame_unwind i386_sigtramp_unwind = {
+ i386_sigtramp_id_unwind,
+ i386_sigtramp_register_unwind
+};
+
+static struct frame_unwind i386_frame_unwind = {
+ i386_frame_id_unwind,
+ i386_frame_register_unwind
+};
+
+const struct frame_unwind *
+i386_frame_p (CORE_ADDR pc)
{
- generic_pop_current_frame (i386_do_pop_frame);
+ if (gdbarch_pc_in_sigtramp (current_gdbarch, pc, NULL))
+ return &i386_sigtramp_unwind;
+ else
+ return &i386_frame_unwind;
+}
+
+static struct frame_id
+i386_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ struct frame_id id;
+ ULONGEST base;
+
+ id.pc = frame_pc_unwind (next_frame);
+ frame_unwind_unsigned_register (next_frame, FP_REGNUM, &base);
+ id.base = base;
+ return id;
+}
+
+static void
+i386_save_dummy_frame_tos (CORE_ADDR sp)
+{
+ /* We can't use the saved top-of-stack to find the right dummy frame
+ when unwinding, since we can't reconstruct it properly if the
+ dummy frame is the innermost frame. To circumvent this, we fake
+ a frame pointer here. */
+
+ regcache_cooked_write_unsigned (current_regcache, FP_REGNUM, sp);
+ generic_save_dummy_frame_tos (sp);
}
\f
@@ -1312,7 +1533,13 @@ i386_pe_skip_trampoline_code (CORE_ADDR
static int
i386_pc_in_sigtramp (CORE_ADDR pc, char *name)
{
- return (name && strcmp ("_sigtramp", name) == 0);
+ char *pname;
+ if (name)
+ pname = name;
+ else
+ find_pc_partial_function (pc, &pname, NULL, NULL);
+
+ return (name && strcmp ("_sigtramp", pname) == 0);
}
\f
@@ -1506,10 +1733,6 @@ i386_gdbarch_init (struct gdbarch_info i
tdep = XMALLOC (struct gdbarch_tdep);
gdbarch = gdbarch_alloc (&info, tdep);
- /* NOTE: cagney/2002-12-06: This can be deleted when this arch is
- ready to unwind the PC first (see frame.c:get_prev_frame()). */
- set_gdbarch_deprecated_init_frame_pc (gdbarch, init_frame_pc_default);
-
/* The i386 default settings don't include the SSE registers.
FIXME: kettenis/20020614: They do include the FPU registers for
now, which probably is not quite right. */
@@ -1591,14 +1814,12 @@ i386_gdbarch_init (struct gdbarch_info i
set_gdbarch_extract_return_value (gdbarch, i386_extract_return_value);
set_gdbarch_push_arguments (gdbarch, i386_push_arguments);
set_gdbarch_push_return_address (gdbarch, i386_push_return_address);
- set_gdbarch_pop_frame (gdbarch, i386_pop_frame);
set_gdbarch_store_struct_return (gdbarch, i386_store_struct_return);
set_gdbarch_store_return_value (gdbarch, i386_store_return_value);
set_gdbarch_extract_struct_value_address (gdbarch,
i386_extract_struct_value_address);
set_gdbarch_use_struct_convention (gdbarch, i386_use_struct_convention);
- set_gdbarch_deprecated_frame_init_saved_regs (gdbarch, i386_frame_init_saved_regs);
set_gdbarch_skip_prologue (gdbarch, i386_skip_prologue);
/* Stack grows downward. */
@@ -1608,16 +1829,9 @@ i386_gdbarch_init (struct gdbarch_info i
set_gdbarch_decr_pc_after_break (gdbarch, 1);
set_gdbarch_function_start_offset (gdbarch, 0);
- /* The following redefines make backtracing through sigtramp work.
- They manufacture a fake sigtramp frame and obtain the saved pc in
- sigtramp from the sigcontext structure which is pushed by the
- kernel on the user stack, along with a pointer to it. */
-
set_gdbarch_frame_args_skip (gdbarch, 8);
set_gdbarch_frameless_function_invocation (gdbarch,
i386_frameless_function_invocation);
- set_gdbarch_frame_chain (gdbarch, i386_frame_chain);
- set_gdbarch_frame_saved_pc (gdbarch, i386_frame_saved_pc);
set_gdbarch_saved_pc_after_call (gdbarch, i386_saved_pc_after_call);
set_gdbarch_frame_num_args (gdbarch, i386_frame_num_args);
set_gdbarch_pc_in_sigtramp (gdbarch, i386_pc_in_sigtramp);
@@ -1629,12 +1843,19 @@ i386_gdbarch_init (struct gdbarch_info i
set_gdbarch_print_insn (gdbarch, i386_print_insn);
+ set_gdbarch_unwind_dummy_id (gdbarch, i386_unwind_dummy_id);
+ set_gdbarch_save_dummy_frame_tos (gdbarch, i386_save_dummy_frame_tos);
+
+ set_gdbarch_unwind_pc (gdbarch, i386_unwind_pc);
+
/* Add the i386 register groups. */
i386_add_reggroups (gdbarch);
set_gdbarch_register_reggroup_p (gdbarch, i386_register_reggroup_p);
/* Hook in ABI-specific overrides, if they have been registered. */
gdbarch_init_osabi (info, gdbarch);
+
+ frame_unwind_append_predicate (gdbarch, i386_frame_p);
return gdbarch;
}
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [offbyone RFC] Merge i386newframe
2003-03-12 22:06 [offbyone RFC] Merge i386newframe Michal Ludvig
@ 2003-03-12 22:42 ` Daniel Jacobowitz
2003-03-13 19:05 ` Andrew Cagney
2003-03-14 15:43 ` Michal Ludvig
2 siblings, 0 replies; 14+ messages in thread
From: Daniel Jacobowitz @ 2003-03-12 22:42 UTC (permalink / raw)
To: GDB Patches
On Wed, Mar 12, 2003 at 11:06:28PM +0100, Michal Ludvig wrote:
> Hi Andrew,
> this patch is a merge of Mark's i386newframe-branch to your
> offbyone-branch. It works quite well, unwinds through signal frames and
> frameless functions, but still has some strange regressions that aren't
> present in i386newframe branch.
>
> For example this program:
> int var;
> void foo ()
> {
> var = 0x1234;
> }
> int main ()
> {
> foo ();
> return 0;
> }
>
> Running it in the GDB:
> (gdb) b main
> Breakpoint 1 at 0x804832c: file tst.c, line 8.
> (gdb) r
> Starting program: tst
>
> Breakpoint 1, main () at tst.c:8
> 8 foo ();
> (gdb) n
>
> Program exited normally.
> (gdb)
>
> Why it didn't stop on "return 0" but exitted immediately instead? Did
> you observe this behaviour in offbyone branch too, or is it caused by
> this patch? It seems to have something to do with symbols, linetables,
> ... but I can't see how it could be related to this patch. It doesn't
> matter if I compile the program with stabs or dwarf2 debug info. Ideas?
Hmm, what usually causes this for me is that the frame ID has changed
between two stops in main (the one where the user types next and
the one returning from foo).
--
Daniel Jacobowitz
MontaVista Software Debian GNU/Linux Developer
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [offbyone RFC] Merge i386newframe
2003-03-12 22:06 [offbyone RFC] Merge i386newframe Michal Ludvig
2003-03-12 22:42 ` Daniel Jacobowitz
@ 2003-03-13 19:05 ` Andrew Cagney
2003-03-13 22:46 ` Mark Kettenis
2003-03-14 11:54 ` Michal Ludvig
2003-03-14 15:43 ` Michal Ludvig
2 siblings, 2 replies; 14+ messages in thread
From: Andrew Cagney @ 2003-03-13 19:05 UTC (permalink / raw)
To: Michal Ludvig, Mark Kettenis; +Cc: GDB Patches
> Hi Andrew,
> this patch is a merge of Mark's i386newframe-branch to your offbyone-branch. It works quite well, unwinds through signal frames and frameless functions, but still has some strange regressions that aren't present in i386newframe branch.
>
> For example this program:
> int var;
> void foo ()
> {
> var = 0x1234;
> }
> int main ()
> {
> foo ();
> return 0;
> }
>
> Running it in the GDB:
> (gdb) b main
> Breakpoint 1 at 0x804832c: file tst.c, line 8.
> (gdb) r
> Starting program: tst
>
> Breakpoint 1, main () at tst.c:8
> 8 foo ();
> (gdb) n
I suspect Daniel's answered this. The frame ID needs to be constant
through out the lifetime of the frame. Getting that right isn't
trivial. However, getting it right can receive a bonus: d10v now
passing mips_pro.exp.
> Program exited normally.
> (gdb)
>
> Why it didn't stop on "return 0" but exitted immediately instead? Did you observe this behaviour in offbyone branch too, or is it caused by this patch? It seems to have something to do with symbols, linetables, ... but I can't see how it could be related to this patch. It doesn't matter if I compile the program with stabs or dwarf2 debug info. Ideas?
>
> Anyway comments on the code are welcome. As soon as i386newframe is merged to offbyone I'll start porting x86-64 on top of it as well.
Definitly getting close!
> Index: frame.c
> ===================================================================
> RCS file: /cvs/src/src/gdb/frame.c,v
> retrieving revision 1.71.2.7
> diff -u -p -r1.71.2.7 frame.c
> --- frame.c 11 Mar 2003 23:00:26 -0000 1.71.2.7
> +++ frame.c 12 Mar 2003 16:28:15 -0000
> @@ -1282,7 +1282,6 @@ get_prev_frame (struct frame_info *this_
> /* Only try to do the unwind once. */
> if (this_frame->prev_p)
> return this_frame->prev;
> - this_frame->prev_p = 1;
>
> /* If we're inside the entry file, it isn't valid. Don't apply this
> test to a dummy frame - dummy frame PC's typically land in the
> @@ -1351,6 +1350,11 @@ get_prev_frame (struct frame_info *this_
> prev_frame = FRAME_OBSTACK_ZALLOC (struct frame_info);
> prev_frame->level = this_frame->level + 1;
>
> + /* Link it in. */
> + this_frame->prev = prev_frame;
> + prev_frame->next = this_frame;
> + this_frame->prev_p = 1;
> +
> /* Try to unwind the PC. If that doesn't work, assume we've reached
> the oldest frame and simply return. Is there a better sentinal
> value? The unwound PC value is then used to initialize the new
> @@ -1466,10 +1470,6 @@ get_prev_frame (struct frame_info *this_
> (HP/UX) still reply on EXTRA_FRAME_INFO and, hence, still poke at
> the "struct frame_info" object directly. */
> prev_frame->frame = prev_frame->id.base;
> -
> - /* Link it in. */
> - this_frame->prev = prev_frame;
> - prev_frame->next = this_frame;
>
> /* FIXME: cagney/2002-01-19: This call will go away. Instead of
> initializing extra info, all frames will use the frame_cache
The need for the above suggests code trying to walk up the frame chain
when it shouldn't need to. Do you have more details?
> static CORE_ADDR
> i386_saved_pc_after_call (struct frame_info *frame)
> {
> - if (get_frame_type (frame) == SIGTRAMP_FRAME)
> - return i386_sigtramp_saved_pc (frame);
> + char buf[4];
>
> - return read_memory_unsigned_integer (read_register (SP_REGNUM), 4);
> + /* Our frame unwinder handles this just fine. */
> + frame_unwind_register (frame, PC_REGNUM, buf);
> + return extract_address (buf, 4);
> }
Idea's for what to do with this architecture method welcome.
I believe the intent is for this method to have relatively low overhead
(when measured by the number of target interactions). Hence, it should
avoid doing prologue analysis (which frame_unwind_register() will trigger).
Perhaphs it should be superseeded by a method that takes a regcache
instead of a frame (making the non-analysis of the prologue clearer)?
Alternatively, it could be defaulted to gdbarch_unwind_pc() making its
implementation optional (by those that complain that GDB next's too slow
:-)?
Anyway, the call:
frame_unwind_unsigned_register (frame, PC_REGNUM, &ulongest);
would simplify the code a little.
> /* Return PC of first real instruction. */
>
> static CORE_ADDR
> @@ -855,37 +690,423 @@ i386_push_return_address (CORE_ADDR pc,
> write_memory (sp - 4, buf, 4);
> return sp - 4;
> }
> +\f
> +#include "frame-unwind.h"
Should eventually be moved to the start of the file (yes, that came with
the original patch).
> +#define regcache_cooked_write_unsigned regcache_raw_write_unsigned
> +
> +#ifdef I386_REGNO_TO_SYMMETRY
> +#error "The Sequent Symmetry is no longer supported."
> +#endif
> +
> +/* According to the System V ABI, the registers %ebp, %ebx, %edi, %esi
> + and %esp "belong" to the calling function. */
> +
> +/* The maximum number of saved registers. This should include all
> + registers mentioned above, and %eip. */
> +#define I386_NUM_SAVED_REGISTERS 9
> +
> +struct i386_frame_cache
> +{
> + CORE_ADDR saved_regs[I386_NUM_SAVED_REGISTERS];
> + CORE_ADDR saved_sp;
> + CORE_ADDR return_pc;
> + CORE_ADDR base;
> + int frameless;
> +};
> +
> +/* Parse the first few instructions the function to see what registers
> + were stored.
> +
> + We handle these cases:
> +
> + The startup sequence can be at the start of the function, or the
> + function can start with a branch to startup code at the end.
> +
> + %ebp can be set up with either the 'enter' instruction, or "pushl
> + %ebp, movl %esp, %ebp" (`enter' is too slow to be useful, but was
> + once used in the System V compiler).
> +
> + Local space is allocated just below the saved %ebp by either the
> + 'enter' instruction, or by "subl $<size>, %esp". 'enter' has a 16
> + bit unsigned argument for space to allocate, and the 'addl'
> + instruction could have either a signed byte, or 32 bit immediate.
> +
> + Next, the registers used by this function are pushed. With the
> + System V compiler they will always be in the order: %edi, %esi,
> + %ebx (and sometimes a harmless bug causes it to also save but not
> + restore %eax); however, the code below is willing to see the pushes
> + in any order, and will handle up to 8 of them.
> +
> + If the setup sequence is at the end of the function, then the next
> + instruction will be a branch back to the start. */
> +
> +static struct i386_frame_cache *
> +i386_frame_cache (struct frame_info *frame, void **cachep)
> +{
> + struct i386_frame_cache *cache;
> + CORE_ADDR pc, start_pc;
> +
> + if (*cachep)
> + return *cachep;
> +
> + cache = FRAME_OBSTACK_ZALLOC (struct i386_frame_cache);
> +
> + pc = frame_pc_unwind (frame);
> + start_pc = get_pc_function_start (pc);
> +
> + long locals = -1;
> +
> + if (start_pc != 0)
> + locals = i386_get_frame_setup (start_pc);
> +
> + if (locals >= 0 && pc > start_pc)
> + {
> + ULONGEST fp;
> + CORE_ADDR addr;
> + unsigned char op;
> + int i;
> +
> + frame_unwind_unsigned_register (frame, FP_REGNUM, &fp);
> + cache->base = fp;
> +
> + addr = fp - 4 - locals;
> + for (i = 0; i < I386_NUM_SAVED_REGISTERS; i++)
> + {
> + op = codestream_get ();
> + if (op < 0x50 || op > 0x57)
> + break;
> +
> + cache->saved_regs[op - 0x50] = addr;
> + addr -= 4;
> + }
> +
> + cache->saved_regs[PC_REGNUM] = fp + 4;
> + cache->saved_regs[FP_REGNUM] = fp;
> + cache->saved_sp = fp + 8;
> + }
> + else
> + {
> + ULONGEST sp;
> +
> + /* This function is either frameless or we're at the start
> + of the function and this function's frame hasn't been
> + setup yet. In the latter case only %eip and %esp have
> + changed, and we can determine their previous values. We
> + pretend we can do the same in the former case. */
> + cache->frameless = 1;
> +
> + frame_unwind_unsigned_register (frame, SP_REGNUM, &sp);
> + cache->saved_regs[PC_REGNUM] = sp;
> + cache->saved_sp = cache->saved_regs[PC_REGNUM] + 4;
> + cache->base = sp;
> + }
> +
> + *cachep = cache;
> + return cache;
> +}
>
> static void
> -i386_do_pop_frame (struct frame_info *frame)
> +i386_frame_pop (struct frame_info *frame, void **cachep,
> + struct regcache *regcache)
Hmm, I've deleted this function. Instead a frame pop relies 100% on
register unwind. Need to figure out the problem here.
> {
> - CORE_ADDR fp;
> + CORE_ADDR fp = get_frame_base (frame);
> int regnum;
> - char regbuf[I386_MAX_REGISTER_SIZE];
> + char buf[4];
> + ULONGEST val;
>
> - fp = get_frame_base (frame);
> - i386_frame_init_saved_regs (frame);
> + gdb_assert (get_frame_type (frame) != DUMMY_FRAME);
>
> - for (regnum = 0; regnum < NUM_REGS; regnum++)
> + for (regnum = 0; regnum < I386_NUM_SAVED_REGISTERS; regnum++)
> {
> - CORE_ADDR addr;
> - addr = get_frame_saved_regs (frame)[regnum];
> - if (addr)
> + frame_unwind_register (frame, regnum, buf);
> + regcache_cooked_write (regcache, regnum, buf);
> + }
> +
> + /* Reset the direction flag. */
> + regcache_cooked_read_unsigned (regcache, PS_REGNUM, &val);
> + val &= ~(1 << 10);
> + regcache_cooked_write_unsigned (regcache, PS_REGNUM, val);
Ah, ok. So what the heck is the direction flag and why would someone
want to clear it (Yes I've even looked in the ia32 spec :-)?
If the previous frame's direction flag should have been reset then the
register unwind code should have done that (wonder if dwarf2cfi is
powerful enough to specify this).
> +static CORE_ADDR
> +i386_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
> +{
> + ULONGEST pc;
> + frame_unwind_unsigned_register (next_frame, PC_REGNUM, &pc);
> + return (CORE_ADDR) pc;
> +}
Yes.
> +static void
> +i386_frame_id_unwind (struct frame_info *next_frame, void **cachep,
> + struct frame_id *id)
> +{
> + struct i386_frame_cache *cache = i386_frame_cache (next_frame, cachep);
> +
> + gdb_assert (cache);
> + gdb_assert (get_frame_type (next_frame) != DUMMY_FRAME);
> +
> + /* Start with a NULL next_frame ID. */
> + *id = null_frame_id;
> +
> + /* Unwind PC first */
> + id->pc = frame_pc_unwind (next_frame);
> + cache->return_pc = id->pc;
> + /* The next_frame's base is the address of a 4-byte word containing the
> + calling next_frame's address.
It's previous, not next.
> + Signal trampolines don't have a meaningful next_frame. The frame
> + pointer value we use is actually the next_frame pointer of the calling
> + next_frame -- that is, the frame which was in progress when the signal
> + trampoline was entered. GDB mostly treats this next_frame pointer
> + value as a magic cookie. We detect the case of a signal
> + trampoline by testing for get_frame_type() == SIGTRAMP_FRAME,
> + which is set based on PC_IN_SIGTRAMP. */
> + if (get_frame_type (next_frame) == SIGTRAMP_FRAME || cache->frameless)
> + id->base = get_frame_base (next_frame);
Is this SIGTRAMP_FRAME specific test needed? Can all the information
this code needs be obtained using frame_register_unwind(). If this and
the next frame's base pointer are identical, just unwind the next
frame's BP and trust the next frame to `do the right thing'.
> + else
> + if (!inside_entry_file (get_frame_pc (next_frame)))
> + id->base = cache->base;
> +}
> +
> +static void
> +i386_frame_register_unwind (struct frame_info *frame, void **cachep,
> + int regnum, int *optimizedp,
> + enum lval_type *lvalp, CORE_ADDR *addrp,
> + int *realnump, void *valuep)
> +{
> + /* FIXME: kettenis/20030302: I don't understand why the cache isn't
> + already initialized. */
> + struct i386_frame_cache *cache = i386_frame_cache (frame, cachep);
> +
> + gdb_assert (cache);
> + gdb_assert (get_frame_type (frame) != DUMMY_FRAME);
> + gdb_assert (regnum >= 0);
> +
> + if (regnum == SP_REGNUM && cache->saved_sp)
> + {
> + *optimizedp = 0;
> + *lvalp = not_lval;
> + *addrp = 0;
> + *realnump = -1;
> + if (valuep)
> {
> - read_memory (addr, regbuf, REGISTER_RAW_SIZE (regnum));
> - deprecated_write_register_gen (regnum, regbuf);
> + /* Store the value. */
> + store_address (valuep, 4, cache->saved_regs[SP_REGNUM]);
> }
> + return;
> }
> - write_register (FP_REGNUM, read_memory_integer (fp, 4));
> - write_register (PC_REGNUM, read_memory_integer (fp + 4, 4));
> - write_register (SP_REGNUM, fp + 8);
> - flush_cached_frames ();
> +
> + if (regnum == PC_REGNUM)
> + {
> + CORE_ADDR pc;
> + *optimizedp = 0;
> + *lvalp = not_lval;
> + *addrp = 0;
> + *realnump = -1;
> + pc = i386_frame_pc_unwind (frame, cachep);
> + if (valuep)
> + store_address (valuep, 4, pc);
> + }
> +
> + if (regnum < I386_NUM_SAVED_REGISTERS && cache->saved_regs[regnum] != 0)
> + {
> + *optimizedp = 0;
> + *lvalp = lval_memory;
> + *addrp = cache->saved_regs[regnum];
> + *realnump = -1;
> + if (valuep)
> + {
> + /* Read the value in from memory. */
> + read_memory (*addrp, valuep,
> + register_size (current_gdbarch, regnum));
> + }
> + return;
> + }
> +
> + frame_register (frame, regnum, optimizedp, lvalp, addrp, realnump, valuep);
> +}
> +
> +static struct i386_frame_cache *
> +i386_sigtramp_cache (struct frame_info *frame, void **cachep)
> +{
> + struct i386_frame_cache *cache;
> + CORE_ADDR pc, start_pc, addr;
> + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
> + int sc_offset, regnum;
> +
> + if (*cachep)
> + return *cachep;
> +
> + cache = FRAME_OBSTACK_ZALLOC (struct i386_frame_cache);
> +
> + pc = frame_pc_unwind (frame);
> + start_pc = get_pc_function_start (pc);
> + addr = tdep->sigcontext_addr (frame->prev);
> +
> + for (regnum=0; regnum<=PS_REGNUM; regnum++)
> + {
> + /* See <asm/sigcontext.h> for details on how registers are stored
> + in the sigcontext. */
> + if (regnum < PC_REGNUM)
> + sc_offset = (11 - regnum);
> + else if (regnum == PC_REGNUM)
> + sc_offset = 14;
> + else if (regnum == PS_REGNUM)
> + sc_offset = 16;
> + else
> + break;
> +
> + sc_offset *= 4;
> +
> + if (regnum < sizeof (cache->saved_regs))
> + cache->saved_regs[regnum] = addr + sc_offset;
> + }
> +
> + cache->base = addr;
> +
> + *cachep = cache;
> + return cache;
> +}
> +
> +static void
> +i386_sigtramp_register_unwind (struct frame_info *frame, void **cachep,
> + int regnum, int *optimizedp,
> + enum lval_type *lvalp, CORE_ADDR *addrp,
> + int *realnump, void *valuep)
> +{
> + printf_debug ("## %s(%p, %p, %s(%d))\n", __func__, (void*)frame->frame,
> + (void*)frame->pc, i386_register_name(regnum), regnum);
> +
> + struct i386_frame_cache *cache = i386_sigtramp_cache (frame, cachep);
> + char buf[4];
> + CORE_ADDR addr;
> +
> + gdb_assert (cache);
> + gdb_assert (regnum >= 0);
> +
> + if (regnum < I386_NUM_SAVED_REGISTERS && cache->saved_regs[regnum] != 0)
> + {
> + *optimizedp = 0;
> + *lvalp = lval_memory;
> + *addrp = cache->saved_regs[regnum];
> + *realnump = -1;
> + if (valuep)
> + read_memory (*addrp, valuep,
> + register_size (current_gdbarch, regnum));
> + return;
> + }
> +
> + /* Fall back for non-saved registers. */
> + frame_register (frame, regnum, optimizedp, lvalp, addrp, realnump, valuep);
> }
>
> static void
> -i386_pop_frame (void)
> +i386_sigtramp_id_unwind (struct frame_info *next_frame, void **cachep,
> + struct frame_id *id)
> +{
> + printf_debug ("## %s(%p, %p)\n", __func__, (void*)next_frame->frame,
> + (void*)next_frame->pc);
> + struct i386_frame_cache *cache = i386_sigtramp_cache (next_frame, cachep);
> +
> + gdb_assert (cache);
> + gdb_assert (get_frame_type (next_frame) != DUMMY_FRAME);
> +
> + /* Start with a NULL next_frame ID. */
> + *id = null_frame_id;
> +
> + /* Unwind PC first */
> + id->pc = frame_pc_unwind (next_frame);
> + cache->return_pc = id->pc;
> +
> + /* The next_frame's base is the address of a 4-byte word containing the
> + calling next_frame's address.
> +
> + Signal trampolines don't have a meaningful next_frame. The frame
> + pointer value we use is actually the next_frame pointer of the calling
> + next_frame -- that is, the frame which was in progress when the signal
> + trampoline was entered. GDB mostly treats this next_frame pointer
> + value as a magic cookie. We detect the case of a signal
> + trampoline by testing for get_frame_type() == SIGTRAMP_FRAME,
> + which is set based on PC_IN_SIGTRAMP. */
> +
> + if (get_frame_type (next_frame) == SIGTRAMP_FRAME || cache->frameless)
> + id->base = get_frame_base (next_frame);
Hmm, per the normal register unwind, is this needed?
> +static struct frame_unwind i386_sigtramp_unwind = {
> + i386_sigtramp_id_unwind,
> + i386_sigtramp_register_unwind
> +};
> +
> +static struct frame_unwind i386_frame_unwind = {
> + i386_frame_id_unwind,
> + i386_frame_register_unwind
> +};
> +
> +const struct frame_unwind *
> +i386_frame_p (CORE_ADDR pc)
> {
> - generic_pop_current_frame (i386_do_pop_frame);
> + if (gdbarch_pc_in_sigtramp (current_gdbarch, pc, NULL))
> + return &i386_sigtramp_unwind;
> + else
> + return &i386_frame_unwind;
> +}
Yes, or two separate predicate functions.
Andrew
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [offbyone RFC] Merge i386newframe
2003-03-13 19:05 ` Andrew Cagney
@ 2003-03-13 22:46 ` Mark Kettenis
2003-03-14 15:48 ` Andrew Cagney
2003-04-06 17:10 ` Andrew Cagney
2003-03-14 11:54 ` Michal Ludvig
1 sibling, 2 replies; 14+ messages in thread
From: Mark Kettenis @ 2003-03-13 22:46 UTC (permalink / raw)
To: ac131313; +Cc: mludvig, gdb-patches
Date: Thu, 13 Mar 2003 14:05:23 -0500
From: Andrew Cagney <ac131313@redhat.com>
> Breakpoint 1, main () at tst.c:8
> 8 foo ();
> (gdb) n
I suspect Daniel's answered this. The frame ID needs to be constant
through out the lifetime of the frame. Getting that right isn't
trivial. However, getting it right can receive a bonus: d10v now
passing mips_pro.exp.
There defenitely is a case where the frame ID isn't constant through
the lifetime of the frame in my current implementation. I think I can
fix that though...
The need for the above suggests code trying to walk up the frame chain
when it shouldn't need to. Do you have more details?
> static CORE_ADDR
> i386_saved_pc_after_call (struct frame_info *frame)
> {
> - if (get_frame_type (frame) == SIGTRAMP_FRAME)
> - return i386_sigtramp_saved_pc (frame);
> + char buf[4];
>
> - return read_memory_unsigned_integer (read_register (SP_REGNUM), 4);
> + /* Our frame unwinder handles this just fine. */
> + frame_unwind_register (frame, PC_REGNUM, buf);
> + return extract_address (buf, 4);
> }
Idea's for what to do with this architecture method welcome.
I believe the intent is for this method to have relatively low overhead
(when measured by the number of target interactions). Hence, it should
avoid doing prologue analysis (which frame_unwind_register() will trigger).
Hmm. I was under the impression that we have this function because on
some targets (the i386 is one of them) the frame hasn't been setup yet
when we've stopped on the first instruction of a function.
Perhaphs it should be superseeded by a method that takes a regcache
instead of a frame (making the non-analysis of the prologue clearer)?
I think that would be a good idea.
Alternatively, it could be defaulted to gdbarch_unwind_pc() making its
implementation optional (by those that complain that GDB next's too slow
:-)?
Anyway, the call:
frame_unwind_unsigned_register (frame, PC_REGNUM, &ulongest);
would simplify the code a little.
But I believe the current implementation is more expressive, since the
PC is a ultimately a memory address. I did consider your
implementation too, and did find it difficult too choose :-).
> /* Return PC of first real instruction. */
>
> static CORE_ADDR
> @@ -855,37 +690,423 @@ i386_push_return_address (CORE_ADDR pc,
> write_memory (sp - 4, buf, 4);
> return sp - 4;
> }
> +\f
> +#include "frame-unwind.h"
Should eventually be moved to the start of the file (yes, that came with
the original patch).
Yup!
> +#define regcache_cooked_write_unsigned regcache_raw_write_unsigned
And this is temporary too.
>
> static void
> -i386_do_pop_frame (struct frame_info *frame)
> +i386_frame_pop (struct frame_info *frame, void **cachep,
> + struct regcache *regcache)
Hmm, I've deleted this function. Instead a frame pop relies 100% on
register unwind. Need to figure out the problem here.
My implementation of this function suggests that, maybe, it isn't such
a good idea to delete it entirely.
> + /* Reset the direction flag. */
> + regcache_cooked_read_unsigned (regcache, PS_REGNUM, &val);
> + val &= ~(1 << 10);
> + regcache_cooked_write_unsigned (regcache, PS_REGNUM, val);
Ah, ok. So what the heck is the direction flag and why would someone
want to clear it (Yes I've even looked in the ia32 spec :-)?
Well, being CISC, the i386 has these instructions that, as a
side-effect, based on the direction flag, increase or decrease the
value of the %ecx register. My copy of the i386 processor supplement
of the system V ABI (Fourth edition) says on page 3-12 that "the
direction flag must be set to forward before entry and upon exit from
a function." This bit of code tries the implement the "upon exit" bit.
If the previous frame's direction flag should have been reset then the
register unwind code should have done that (wonder if dwarf2cfi is
powerful enough to specify this).
I felt that it is somehow different from a "saved" registers. But
your phrasing makes me believe it's more correct to reset from the
register unwind code.
> +static void
> +i386_frame_id_unwind (struct frame_info *next_frame, void **cachep,
> + struct frame_id *id)
> +{
> + struct i386_frame_cache *cache = i386_frame_cache (next_frame, cachep);
> +
> + gdb_assert (cache);
> + gdb_assert (get_frame_type (next_frame) != DUMMY_FRAME);
> +
> + /* Start with a NULL next_frame ID. */
> + *id = null_frame_id;
> +
> + /* Unwind PC first */
> + id->pc = frame_pc_unwind (next_frame);
> + cache->return_pc = id->pc;
> + /* The next_frame's base is the address of a 4-byte word containing the
> + calling next_frame's address.
It's previous, not next.
Ahh, Michael changed my comment here.
> + Signal trampolines don't have a meaningful next_frame. The frame
> + pointer value we use is actually the next_frame pointer of the calling
> + next_frame -- that is, the frame which was in progress when the signal
> + trampoline was entered. GDB mostly treats this next_frame pointer
> + value as a magic cookie. We detect the case of a signal
> + trampoline by testing for get_frame_type() == SIGTRAMP_FRAME,
> + which is set based on PC_IN_SIGTRAMP. */
> + if (get_frame_type (next_frame) == SIGTRAMP_FRAME || cache->frameless)
> + id->base = get_frame_base (next_frame);
Is this SIGTRAMP_FRAME specific test needed? Can all the information
this code needs be obtained using frame_register_unwind(). If this and
the next frame's base pointer are identical, just unwind the next
frame's BP and trust the next frame to `do the right thing'.
Might work. I'll have to think about that a bit more.
> +static struct i386_frame_cache *
> +i386_sigtramp_cache (struct frame_info *frame, void **cachep)
> +{
> + struct i386_frame_cache *cache;
> + CORE_ADDR pc, start_pc, addr;
> + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
> + int sc_offset, regnum;
> +
> + if (*cachep)
> + return *cachep;
> +
> + cache = FRAME_OBSTACK_ZALLOC (struct i386_frame_cache);
> +
> + pc = frame_pc_unwind (frame);
> + start_pc = get_pc_function_start (pc);
> + addr = tdep->sigcontext_addr (frame->prev);
> +
> + for (regnum=0; regnum<=PS_REGNUM; regnum++)
> + {
> + /* See <asm/sigcontext.h> for details on how registers are stored
> + in the sigcontext. */
> + if (regnum < PC_REGNUM)
> + sc_offset = (11 - regnum);
> + else if (regnum == PC_REGNUM)
> + sc_offset = 14;
> + else if (regnum == PS_REGNUM)
> + sc_offset = 16;
> + else
> + break;
> +
> + sc_offset *= 4;
> +
> + if (regnum < sizeof (cache->saved_regs))
> + cache->saved_regs[regnum] = addr + sc_offset;
> + }
> +
> + cache->base = addr;
> +
> + *cachep = cache;
> + return cache;
> +}
> +
> +static void
> +i386_sigtramp_register_unwind (struct frame_info *frame, void **cachep,
> + int regnum, int *optimizedp,
> + enum lval_type *lvalp, CORE_ADDR *addrp,
> + int *realnump, void *valuep)
> +{
> + printf_debug ("## %s(%p, %p, %s(%d))\n", __func__, (void*)frame->frame,
> + (void*)frame->pc, i386_register_name(regnum), regnum);
> +
> + struct i386_frame_cache *cache = i386_sigtramp_cache (frame, cachep);
> + char buf[4];
> + CORE_ADDR addr;
> +
> + gdb_assert (cache);
> + gdb_assert (regnum >= 0);
> +
> + if (regnum < I386_NUM_SAVED_REGISTERS && cache->saved_regs[regnum] != 0)
> + {
> + *optimizedp = 0;
> + *lvalp = lval_memory;
> + *addrp = cache->saved_regs[regnum];
> + *realnump = -1;
> + if (valuep)
> + read_memory (*addrp, valuep,
> + register_size (current_gdbarch, regnum));
> + return;
> + }
> +
> + /* Fall back for non-saved registers. */
> + frame_register (frame, regnum, optimizedp, lvalp, addrp, realnump, valuep);
> }
>
> static void
> -i386_pop_frame (void)
> +i386_sigtramp_id_unwind (struct frame_info *next_frame, void **cachep,
> + struct frame_id *id)
> +{
> + printf_debug ("## %s(%p, %p)\n", __func__, (void*)next_frame->frame,
> + (void*)next_frame->pc);
> + struct i386_frame_cache *cache = i386_sigtramp_cache (next_frame, cachep);
> +
> + gdb_assert (cache);
> + gdb_assert (get_frame_type (next_frame) != DUMMY_FRAME);
> +
> + /* Start with a NULL next_frame ID. */
> + *id = null_frame_id;
> +
> + /* Unwind PC first */
> + id->pc = frame_pc_unwind (next_frame);
> + cache->return_pc = id->pc;
> +
> + /* The next_frame's base is the address of a 4-byte word containing the
> + calling next_frame's address.
> +
> + Signal trampolines don't have a meaningful next_frame. The frame
> + pointer value we use is actually the next_frame pointer of the calling
> + next_frame -- that is, the frame which was in progress when the signal
> + trampoline was entered. GDB mostly treats this next_frame pointer
> + value as a magic cookie. We detect the case of a signal
> + trampoline by testing for get_frame_type() == SIGTRAMP_FRAME,
> + which is set based on PC_IN_SIGTRAMP. */
> +
> + if (get_frame_type (next_frame) == SIGTRAMP_FRAME || cache->frameless)
> + id->base = get_frame_base (next_frame);
Hmm, per the normal register unwind, is this needed?
I'm not sure whether we need/want these sigtramp-specific unwinders.
Read ahead.
> +static struct frame_unwind i386_sigtramp_unwind = {
> + i386_sigtramp_id_unwind,
> + i386_sigtramp_register_unwind
> +};
> +
> +static struct frame_unwind i386_frame_unwind = {
> + i386_frame_id_unwind,
> + i386_frame_register_unwind
> +};
> +
> +const struct frame_unwind *
> +i386_frame_p (CORE_ADDR pc)
> {
> - generic_pop_current_frame (i386_do_pop_frame);
> + if (gdbarch_pc_in_sigtramp (current_gdbarch, pc, NULL))
> + return &i386_sigtramp_unwind;
> + else
> + return &i386_frame_unwind;
> +}
Yes, or two separate predicate functions.
Hmm, the pc_in_sigtramp test isn't exactly cheap on some targets, so
if we can do without I think that would be preferable.
Mark
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [offbyone RFC] Merge i386newframe
2003-03-13 22:46 ` Mark Kettenis
@ 2003-03-14 15:48 ` Andrew Cagney
2003-03-14 16:19 ` Andrew Cagney
2003-03-14 18:20 ` Andrew Cagney
2003-04-06 17:10 ` Andrew Cagney
1 sibling, 2 replies; 14+ messages in thread
From: Andrew Cagney @ 2003-03-14 15:48 UTC (permalink / raw)
To: Mark Kettenis; +Cc: mludvig, gdb-patches
> I suspect Daniel's answered this. The frame ID needs to be constant
> through out the lifetime of the frame. Getting that right isn't
> trivial. However, getting it right can receive a bonus: d10v now
> passing mips_pro.exp.
>
> There defenitely is a case where the frame ID isn't constant through
> the lifetime of the frame in my current implementation. I think I can
> fix that though...
I don't think any existing implementations get the edge case right :-(
> Idea's for what to do with this architecture method welcome.
>
> I believe the intent is for this method to have relatively low overhead
> (when measured by the number of target interactions). Hence, it should
> avoid doing prologue analysis (which frame_unwind_register() will trigger).
>
> Hmm. I was under the impression that we have this function because on
> some targets (the i386 is one of them) the frame hasn't been setup yet
> when we've stopped on the first instruction of a function.
With CFI, frame or no frame, it is always possible to unwind the PC. A
more complex prologue analysier could also manage to unwind the PC
correctly in this case (but at the expense of doing prologue analysis).
The function is called when doing a next and has just stepped into a
function. To make the next faster (no prologue analysis), and the
prologue analyzer easier (avoid most common frameless case).
Thing is, it doesn't do anything for:
(gdb) stepi
Stepped into function foo()
0x10000 add 8 to sp
(gdb) stepi
0x10000 store link-register in [sp + 4]
which prologue analyzers should handle but don't `because it is to
hard'. A simple minded suggestion is to limit the prologue analyser to
the instruction range [func ... current-pc) so that, when still in the
prologue, it only records what really happened.
> Perhaphs it should be superseeded by a method that takes a regcache
> instead of a frame (making the non-analysis of the prologue clearer)?
>
> I think that would be a good idea.
Optional. It should fall back to using standard unwind_pc().
> Alternatively, it could be defaulted to gdbarch_unwind_pc() making its
> implementation optional (by those that complain that GDB next's too slow
> :-)?
>
> Anyway, the call:
> frame_unwind_unsigned_register (frame, PC_REGNUM, &ulongest);
> would simplify the code a little.
>
> But I believe the current implementation is more expressive, since the
> PC is a ultimately a memory address. I did consider your
> implementation too, and did find it difficult too choose :-).
I need to deprecate extract_address() people are ment to use extract
typed address. Which is even more descriptive :-)
> >
> > static void
> > -i386_do_pop_frame (struct frame_info *frame)
> > +i386_frame_pop (struct frame_info *frame, void **cachep,
> > + struct regcache *regcache)
>
> Hmm, I've deleted this function. Instead a frame pop relies 100% on
> register unwind. Need to figure out the problem here.
>
> My implementation of this function suggests that, maybe, it isn't such
> a good idea to delete it entirely.
>
> > + /* Reset the direction flag. */
> > + regcache_cooked_read_unsigned (regcache, PS_REGNUM, &val);
> > + val &= ~(1 << 10);
> > + regcache_cooked_write_unsigned (regcache, PS_REGNUM, val);
>
> Ah, ok. So what the heck is the direction flag and why would someone
> want to clear it (Yes I've even looked in the ia32 spec :-)?
>
> Well, being CISC, the i386 has these instructions that, as a
> side-effect, based on the direction flag, increase or decrease the
> value of the %ecx register. My copy of the i386 processor supplement
> of the system V ABI (Fourth edition) says on page 3-12 that "the
> direction flag must be set to forward before entry and upon exit from
> a function." This bit of code tries the implement the "upon exit" bit.
Ah, thats the info I needed.
> If the previous frame's direction flag should have been reset then the
> register unwind code should have done that (wonder if dwarf2cfi is
> powerful enough to specify this).
>
> I felt that it is somehow different from a "saved" registers. But
> your phrasing makes me believe it's more correct to reset from the
> register unwind code.
I don't think it is any different. For:
(gdb) up
Frame #1 foo()
(gdb) info register psw
to work correctly, the register unwind code will need to zap that bit.
Otherwize GDB will mis-represent the value of the PSW in the calling frame.
I think there is still going to be a problem in the CFI unwinder. The
CFI spec as the `architectural' register unwind rule as a loop-hole.
Something related to that may need to be added. Wonder if GCC even
thought to generate it. Hmm, the throw/catch code must have done
something ....
> Ahh, Michael changed my comment here.
>
> > + Signal trampolines don't have a meaningful next_frame. The frame
> > + pointer value we use is actually the next_frame pointer of the calling
> > + next_frame -- that is, the frame which was in progress when the signal
> > + trampoline was entered. GDB mostly treats this next_frame pointer
> > + value as a magic cookie. We detect the case of a signal
> > + trampoline by testing for get_frame_type() == SIGTRAMP_FRAME,
> > + which is set based on PC_IN_SIGTRAMP. */
>
> > + if (get_frame_type (next_frame) == SIGTRAMP_FRAME || cache->frameless)
> > + id->base = get_frame_base (next_frame);
>
> Is this SIGTRAMP_FRAME specific test needed? Can all the information
> this code needs be obtained using frame_register_unwind(). If this and
> the next frame's base pointer are identical, just unwind the next
> frame's BP and trust the next frame to `do the right thing'.
>
> Might work. I'll have to think about that a bit more.
It involves an act of faith. The code needs to trust that the next
frame's unwinder is correctly returning this frames register values.
Imagine a world where all next frames were as reliable as the sentinel
frame ...
> I'm not sure whether we need/want these sigtramp-specific unwinders.
> Read ahead.
>
> > +static struct frame_unwind i386_sigtramp_unwind = {
> > + i386_sigtramp_id_unwind,
> > + i386_sigtramp_register_unwind
> > +};
> > +
> > +static struct frame_unwind i386_frame_unwind = {
> > + i386_frame_id_unwind,
> > + i386_frame_register_unwind
> > +};
> > +
> > +const struct frame_unwind *
> > +i386_frame_p (CORE_ADDR pc)
> > {
> > - generic_pop_current_frame (i386_do_pop_frame);
> > + if (gdbarch_pc_in_sigtramp (current_gdbarch, pc, NULL))
> > + return &i386_sigtramp_unwind;
> > + else
> > + return &i386_frame_unwind;
> > +}
>
> Yes, or two separate predicate functions.
>
> Hmm, the pc_in_sigtramp test isn't exactly cheap on some targets, so
> if we can do without I think that would be preferable.
For the i386, are there any differences between how a sigtramp and a
normal frame are unwound? If there are, I think the i386 should
represent them with different frame objects. The above test will only
occure once per frame, and then, only if another frame object hasn't
already identified the frame.
I think here, the best thing is to implement it correctly, and then when
the numbers are in, implement it fast.
BTW, infrun.c and breakpoint.c both call pc_in_sigtramp. Perhaphs they
should call: get_frame_type (get_current_frame ()) == SIGTRAMP_FRAME. I
think, using the new frame code, it is even possible delay the
computation of the frame's ID until it is needed. This makes
get_current_frame() very cheap.
Andrew
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [offbyone RFC] Merge i386newframe
2003-03-14 15:48 ` Andrew Cagney
@ 2003-03-14 16:19 ` Andrew Cagney
2003-03-14 18:20 ` Andrew Cagney
1 sibling, 0 replies; 14+ messages in thread
From: Andrew Cagney @ 2003-03-14 16:19 UTC (permalink / raw)
To: Mark Kettenis, mludvig; +Cc: gdb-patches
> If the previous frame's direction flag should have been reset then the register unwind code should have done that (wonder if dwarf2cfi is powerful enough to specify this).
>
> I felt that it is somehow different from a "saved" registers. But
> your phrasing makes me believe it's more correct to reset from the
> register unwind code.
>
> I don't think it is any different. For:
>
> (gdb) up
> Frame #1 foo()
> (gdb) info register psw
>
> to work correctly, the register unwind code will need to zap that bit. Otherwize GDB will mis-represent the value of the PSW in the calling frame.
>
> I think there is still going to be a problem in the CFI unwinder. The CFI spec as the `architectural' register unwind rule as a loop-hole. Something related to that may need to be added. Wonder if GCC even thought to generate it. Hmm, the throw/catch code must have done something ....
Thinking about this some more, a generic pop function won't work.
Consider the frames:
- sentinel
- normal
- sigtramp
- dummy
It's only when unwinding a normal frame that that PSW bit should be
zapped. For all the others the PSW value of the callee was hopefully
completly saved by the caller.
Andrew
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [offbyone RFC] Merge i386newframe
2003-03-14 15:48 ` Andrew Cagney
2003-03-14 16:19 ` Andrew Cagney
@ 2003-03-14 18:20 ` Andrew Cagney
1 sibling, 0 replies; 14+ messages in thread
From: Andrew Cagney @ 2003-03-14 18:20 UTC (permalink / raw)
To: Andrew Cagney; +Cc: Mark Kettenis, mludvig, gdb-patches
> Hmm. I was under the impression that we have this function because on
> some targets (the i386 is one of them) the frame hasn't been setup yet
> when we've stopped on the first instruction of a function.
>
> With CFI, frame or no frame, it is always possible to unwind the PC. A more complex prologue analysier could also manage to unwind the PC correctly in this case (but at the expense of doing prologue analysis).
>
> The function is called when doing a next and has just stepped into a function. To make the next faster (no prologue analysis), and the prologue analyzer easier (avoid most common frameless case).
>
> Thing is, it doesn't do anything for:
>
> (gdb) stepi
> Stepped into function foo()
> 0x10000 add 8 to sp
> (gdb) stepi
> 0x10000 store link-register in [sp + 4]
>
> which prologue analyzers should handle but don't `because it is to hard'. A simple minded suggestion is to limit the prologue analyser to the instruction range [func ... current-pc) so that, when still in the prologue, it only records what really happened.
>
> Perhaphs it should be superseeded by a method that takes a regcache instead of a frame (making the non-analysis of the prologue clearer)?
>
> I think that would be a good idea.
>
> Optional. It should fall back to using standard unwind_pc().
Hmm, why not get dogmatic? The prologue analyzer shall efficiently, and
correctly, handle the case of a pc in the prologue.
If that is a given, the pc == function edge case can be handled with:
if (pc == function)
// don't bother with prologue analysis
// all registers (except PC/LR) come from next frame
return;
or
for (iaddr = function; iaddr < pc; iaddr += insn length)
... examine instruction ...
either way it will efficiently avoid doing prologue analysis when on the
first instruction, and in the second case will result in a better analyzer.
Andrew
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [offbyone RFC] Merge i386newframe
2003-03-13 22:46 ` Mark Kettenis
2003-03-14 15:48 ` Andrew Cagney
@ 2003-04-06 17:10 ` Andrew Cagney
2003-04-07 18:53 ` Mark Kettenis
1 sibling, 1 reply; 14+ messages in thread
From: Andrew Cagney @ 2003-04-06 17:10 UTC (permalink / raw)
To: Mark Kettenis, mludvig; +Cc: gdb-patches
[picking up old thread]
> The need for the above suggests code trying to walk up the frame chain
> when it shouldn't need to. Do you have more details?
>
> > static CORE_ADDR
> > i386_saved_pc_after_call (struct frame_info *frame)
> > {
> > - if (get_frame_type (frame) == SIGTRAMP_FRAME)
> > - return i386_sigtramp_saved_pc (frame);
> > + char buf[4];
> >
> > - return read_memory_unsigned_integer (read_register (SP_REGNUM), 4);
> > + /* Our frame unwinder handles this just fine. */
> > + frame_unwind_register (frame, PC_REGNUM, buf);
> > + return extract_address (buf, 4);
> > }
>
> Idea's for what to do with this architecture method welcome.
>
> I believe the intent is for this method to have relatively low overhead
> (when measured by the number of target interactions). Hence, it should
> avoid doing prologue analysis (which frame_unwind_register() will trigger).
If that was the intent, then it no longer applies. The call site looks
like:
sr_sal.pc = ADDR_BITS_REMOVE (SAVED_PC_AFTER_CALL (get_current_frame
()));
sr_sal.section = find_pc_overlay (sr_sal.pc);
check_for_old_step_resume_breakpoint ();
step_resume_breakpoint =
set_momentary_breakpoint (sr_sal,
get_frame_id (get_current_frame ()),
bp_step_resume);
Not five lines after the SAVED_PC_AFTER_CALL call is a call to
get_frame_id() and that is going to trigger the prologue analyser. Kind
of makes avoiding prologue analysis futile.
I suspect that, originally, there was a read_fp() call here (that was
cheap) but that was later changed to get_frame_base() / get_frame_id()
when it was realised that read_fp() was not going to be sufficient.
> Hmm. I was under the impression that we have this function because on
> some targets (the i386 is one of them) the frame hasn't been setup yet
> when we've stopped on the first instruction of a function.
I think the prologue analyzer needs to handle this case regardless. It
is just an edge case of the more general problem of determing the frame
ID when still part way through the prologue. The d10v handles this by
bailing out of the prologue analysis when it reaches the current
instruction.
> Perhaphs it should be superseeded by a method that takes a regcache
> instead of a frame (making the non-analysis of the prologue clearer)?
>
> I think that would be a good idea.
On second thoughts, I'm back to thinking that deprecating it is the
right thing to do. Architectures need to fix their prologue analyzer.
Andrew
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [offbyone RFC] Merge i386newframe
2003-04-06 17:10 ` Andrew Cagney
@ 2003-04-07 18:53 ` Mark Kettenis
0 siblings, 0 replies; 14+ messages in thread
From: Mark Kettenis @ 2003-04-07 18:53 UTC (permalink / raw)
To: ac131313; +Cc: mludvig, gdb-patches
Date: Sun, 06 Apr 2003 13:10:40 -0400
From: Andrew Cagney <ac131313@redhat.com>
[picking up old thread]
> The need for the above suggests code trying to walk up the frame chain
> when it shouldn't need to. Do you have more details?
>
> > static CORE_ADDR
> > i386_saved_pc_after_call (struct frame_info *frame)
> > {
> > - if (get_frame_type (frame) == SIGTRAMP_FRAME)
> > - return i386_sigtramp_saved_pc (frame);
> > + char buf[4];
> >
> > - return read_memory_unsigned_integer (read_register (SP_REGNUM), 4);
> > + /* Our frame unwinder handles this just fine. */
> > + frame_unwind_register (frame, PC_REGNUM, buf);
> > + return extract_address (buf, 4);
> > }
>
> Idea's for what to do with this architecture method welcome.
>
> I believe the intent is for this method to have relatively low overhead
> (when measured by the number of target interactions). Hence, it should
> avoid doing prologue analysis (which frame_unwind_register() will trigger).
If that was the intent, then it no longer applies. The call site looks
like:
sr_sal.pc = ADDR_BITS_REMOVE (SAVED_PC_AFTER_CALL (get_current_frame
()));
sr_sal.section = find_pc_overlay (sr_sal.pc);
check_for_old_step_resume_breakpoint ();
step_resume_breakpoint =
set_momentary_breakpoint (sr_sal,
get_frame_id (get_current_frame ()),
bp_step_resume);
Not five lines after the SAVED_PC_AFTER_CALL call is a call to
get_frame_id() and that is going to trigger the prologue analyser. Kind
of makes avoiding prologue analysis futile.
Indeed.
> Hmm. I was under the impression that we have this function because on
> some targets (the i386 is one of them) the frame hasn't been setup yet
> when we've stopped on the first instruction of a function.
I think the prologue analyzer needs to handle this case regardless. It
is just an edge case of the more general problem of determing the frame
ID when still part way through the prologue. The d10v handles this by
bailing out of the prologue analysis when it reaches the current
instruction.
I totally agree with you here.
> Perhaphs it should be superseeded by a method that takes a regcache
> instead of a frame (making the non-analysis of the prologue clearer)?
>
> I think that would be a good idea.
On second thoughts, I'm back to thinking that deprecating it is the
right thing to do. Architectures need to fix their prologue analyzer.
Please do so. Make things as simple as possible now, and let's
optimize *after* the new frame code has stabilized, if the need
arises. If SAVED_PC_AFTER_CALL was an optimization, chances are it
isn't anymore with the new code.
Mark
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [offbyone RFC] Merge i386newframe
2003-03-13 19:05 ` Andrew Cagney
2003-03-13 22:46 ` Mark Kettenis
@ 2003-03-14 11:54 ` Michal Ludvig
2003-03-14 15:59 ` Andrew Cagney
1 sibling, 1 reply; 14+ messages in thread
From: Michal Ludvig @ 2003-03-14 11:54 UTC (permalink / raw)
To: Andrew Cagney; +Cc: Mark Kettenis, GDB Patches
Andrew Cagney wrote:
>> Hi Andrew,
>> this patch is a merge of Mark's i386newframe-branch to your
>> offbyone-branch. It works quite well, unwinds through signal frames
>> and frameless functions, but still has some strange regressions that
>> aren't present in i386newframe branch.
>>
>> For example this program:
>> int var;
>> void foo ()
>> {
>> var = 0x1234;
>> }
>> int main ()
>> {
>> foo ();
>> return 0;
>> }
>>
>> Running it in the GDB:
>> (gdb) b main
>> Breakpoint 1 at 0x804832c: file tst.c, line 8.
>> (gdb) r
>> Starting program: tst
>>
>> Breakpoint 1, main () at tst.c:8
>> 8 foo ();
>> (gdb) n
>
>
> I suspect Daniel's answered this. The frame ID needs to be constant
> through out the lifetime of the frame. Getting that right isn't
> trivial. However, getting it right can receive a bonus: d10v now
> passing mips_pro.exp.
You mean the whole ID should be constant or just id.base? If id.pc must
be constant as well, it couldn't represent the PC register anymore...
I'm confused.
Michal Ludvig
--
* SuSE CR, s.r.o * mludvig@suse.cz
* (+420) 296.545.373 * http://www.suse.cz
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [offbyone RFC] Merge i386newframe
2003-03-14 11:54 ` Michal Ludvig
@ 2003-03-14 15:59 ` Andrew Cagney
0 siblings, 0 replies; 14+ messages in thread
From: Andrew Cagney @ 2003-03-14 15:59 UTC (permalink / raw)
To: Michal Ludvig; +Cc: Mark Kettenis, GDB Patches
>
> I suspect Daniel's answered this. The frame ID needs to be constant through out the lifetime of the frame. Getting that right isn't trivial. However, getting it right can receive a bonus: d10v now passing mips_pro.exp.
>
> You mean the whole ID should be constant or just id.base? If id.pc must be constant as well, it couldn't represent the PC register anymore...
> I'm confused.
At present, just the base needs to be constant.
However, to differentiate between two frameless functions, and to
re-find a frameless function, both the frame base and the frame
function's start address need to be compared. GDB doesn't yet do this
and when it does, it may need to refine what that PC value is.
"frame.h" mentions the problem.
Andrew
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [offbyone RFC] Merge i386newframe
2003-03-12 22:06 [offbyone RFC] Merge i386newframe Michal Ludvig
2003-03-12 22:42 ` Daniel Jacobowitz
2003-03-13 19:05 ` Andrew Cagney
@ 2003-03-14 15:43 ` Michal Ludvig
2003-03-16 12:48 ` Mark Kettenis
2 siblings, 1 reply; 14+ messages in thread
From: Michal Ludvig @ 2003-03-14 15:43 UTC (permalink / raw)
To: GDB Patches; +Cc: Andrew Cagney, Mark Kettenis
[-- Attachment #1: Type: text/plain, Size: 485 bytes --]
Hi all,
I've reworked the merge a little to avoid the need to reorder
frame.c:get_prev_frame() and fixed the 'next' and 'return' bugs that
were in the prevoius one. Now it passes the testsuite even better than
the i386newframe code did :-)
I'm sorry I'm short of time to write more now.
Andrew and Mark, please look around the patch and tell me your opinions.
Have a nice weekend,
Michal Ludvig
--
* SuSE CR, s.r.o * mludvig@suse.cz
* (+420) 296.545.373 * http://www.suse.cz
[-- Attachment #2: merge-i386-3.diff --]
[-- Type: text/plain, Size: 20994 bytes --]
Index: i386-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/i386-tdep.c,v
retrieving revision 1.117
diff -u -p -r1.117 i386-tdep.c
--- i386-tdep.c 12 Mar 2003 16:50:44 -0000 1.117
+++ i386-tdep.c 14 Mar 2003 15:37:22 -0000
@@ -40,6 +40,7 @@
#include "reggroups.h"
#include "dummy-frame.h"
#include "osabi.h"
+#include "frame-unwind.h"
#include "i386-tdep.h"
#include "i387-tdep.h"
@@ -469,64 +470,8 @@ i386_get_frame_setup (CORE_ADDR pc)
return (-1);
}
-/* Signal trampolines don't have a meaningful frame. The frame
- pointer value we use is actually the frame pointer of the calling
- frame -- that is, the frame which was in progress when the signal
- trampoline was entered. GDB mostly treats this frame pointer value
- as a magic cookie. We detect the case of a signal trampoline by
- testing for get_frame_type() == SIGTRAMP_FRAME, which is set based
- on PC_IN_SIGTRAMP.
-
- When a signal trampoline is invoked from a frameless function, we
- essentially have two frameless functions in a row. In this case,
- we use the same magic cookie for three frames in a row. We detect
- this case by seeing whether the next frame is a SIGTRAMP_FRAME,
- and, if it does, checking whether the current frame is actually
- frameless. In this case, we need to get the PC by looking at the
- SP register value stored in the signal context.
-
- This should work in most cases except in horrible situations where
- a signal occurs just as we enter a function but before the frame
- has been set up. Incidentally, that's just what happens when we
- call a function from GDB with a signal pending (there's a test in
- the testsuite that makes this happen). Therefore we pretend that
- we have a frameless function if we're stopped at the start of a
- function. */
-
-/* Return non-zero if we're dealing with a frameless signal, that is,
- a signal trampoline invoked from a frameless function. */
-
-int
-i386_frameless_signal_p (struct frame_info *frame)
-{
- return (get_next_frame (frame)
- && get_frame_type (get_next_frame (frame)) == SIGTRAMP_FRAME
- && (frameless_look_for_prologue (frame)
- || get_frame_pc (frame) == get_pc_function_start (get_frame_pc (frame))));
-}
-
-/* Return the chain-pointer for FRAME. In the case of the i386, the
- frame's nominal address is the address of a 4-byte word containing
- the calling frame's address. */
-
-static CORE_ADDR
-i386_frame_chain (struct frame_info *frame)
-{
- if (pc_in_dummy_frame (get_frame_pc (frame)))
- return get_frame_base (frame);
-
- if (get_frame_type (frame) == SIGTRAMP_FRAME
- || i386_frameless_signal_p (frame))
- return get_frame_base (frame);
-
- if (! inside_entry_file (get_frame_pc (frame)))
- return read_memory_unsigned_integer (get_frame_base (frame), 4);
-
- return 0;
-}
-
/* Determine whether the function invocation represented by FRAME does
- not have a from on the stack associated with it. If it does not,
+ not have a frame on the stack associated with it. If it does not,
return non-zero, otherwise return zero. */
static int
@@ -538,66 +483,16 @@ i386_frameless_function_invocation (stru
return frameless_look_for_prologue (frame);
}
-/* Assuming FRAME is for a sigtramp routine, return the saved program
- counter. */
-
-static CORE_ADDR
-i386_sigtramp_saved_pc (struct frame_info *frame)
-{
- struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
- CORE_ADDR addr;
-
- addr = tdep->sigcontext_addr (frame);
- return read_memory_unsigned_integer (addr + tdep->sc_pc_offset, 4);
-}
-
-/* Assuming FRAME is for a sigtramp routine, return the saved stack
- pointer. */
-
-static CORE_ADDR
-i386_sigtramp_saved_sp (struct frame_info *frame)
-{
- struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
- CORE_ADDR addr;
-
- addr = tdep->sigcontext_addr (frame);
- return read_memory_unsigned_integer (addr + tdep->sc_sp_offset, 4);
-}
-
-/* Return the saved program counter for FRAME. */
-
-static CORE_ADDR
-i386_frame_saved_pc (struct frame_info *frame)
-{
- if (pc_in_dummy_frame (get_frame_pc (frame)))
- {
- ULONGEST pc;
-
- frame_unwind_unsigned_register (frame, PC_REGNUM, &pc);
- return pc;
- }
-
- if (get_frame_type (frame) == SIGTRAMP_FRAME)
- return i386_sigtramp_saved_pc (frame);
-
- if (i386_frameless_signal_p (frame))
- {
- CORE_ADDR sp = i386_sigtramp_saved_sp (get_next_frame (frame));
- return read_memory_unsigned_integer (sp, 4);
- }
-
- return read_memory_unsigned_integer (get_frame_base (frame) + 4, 4);
-}
-
/* Immediately after a function call, return the saved pc. */
static CORE_ADDR
i386_saved_pc_after_call (struct frame_info *frame)
{
- if (get_frame_type (frame) == SIGTRAMP_FRAME)
- return i386_sigtramp_saved_pc (frame);
+ char buf[4];
- return read_memory_unsigned_integer (read_register (SP_REGNUM), 4);
+ /* Our frame unwinder handles this just fine. */
+ frame_unwind_register (frame, PC_REGNUM, buf);
+ return extract_address (buf, 4);
}
/* Return number of args passed to a frame.
@@ -674,72 +569,6 @@ i386_frame_num_args (struct frame_info *
#endif
}
-/* Parse the first few instructions the function to see what registers
- were stored.
-
- We handle these cases:
-
- The startup sequence can be at the start of the function, or the
- function can start with a branch to startup code at the end.
-
- %ebp can be set up with either the 'enter' instruction, or "pushl
- %ebp, movl %esp, %ebp" (`enter' is too slow to be useful, but was
- once used in the System V compiler).
-
- Local space is allocated just below the saved %ebp by either the
- 'enter' instruction, or by "subl $<size>, %esp". 'enter' has a 16
- bit unsigned argument for space to allocate, and the 'addl'
- instruction could have either a signed byte, or 32 bit immediate.
-
- Next, the registers used by this function are pushed. With the
- System V compiler they will always be in the order: %edi, %esi,
- %ebx (and sometimes a harmless bug causes it to also save but not
- restore %eax); however, the code below is willing to see the pushes
- in any order, and will handle up to 8 of them.
-
- If the setup sequence is at the end of the function, then the next
- instruction will be a branch back to the start. */
-
-static void
-i386_frame_init_saved_regs (struct frame_info *fip)
-{
- long locals = -1;
- unsigned char op;
- CORE_ADDR addr;
- CORE_ADDR pc;
- int i;
-
- if (get_frame_saved_regs (fip))
- return;
-
- frame_saved_regs_zalloc (fip);
-
- pc = get_pc_function_start (get_frame_pc (fip));
- if (pc != 0)
- locals = i386_get_frame_setup (pc);
-
- if (locals >= 0)
- {
- addr = get_frame_base (fip) - 4 - locals;
- for (i = 0; i < 8; i++)
- {
- op = codestream_get ();
- if (op < 0x50 || op > 0x57)
- break;
-#ifdef I386_REGNO_TO_SYMMETRY
- /* Dynix uses different internal numbering. Ick. */
- get_frame_saved_regs (fip)[I386_REGNO_TO_SYMMETRY (op - 0x50)] = addr;
-#else
- get_frame_saved_regs (fip)[op - 0x50] = addr;
-#endif
- addr -= 4;
- }
- }
-
- get_frame_saved_regs (fip)[PC_REGNUM] = get_frame_base (fip) + 4;
- get_frame_saved_regs (fip)[FP_REGNUM] = get_frame_base (fip);
-}
-
/* Return PC of first real instruction. */
static CORE_ADDR
@@ -855,37 +684,323 @@ i386_push_return_address (CORE_ADDR pc,
write_memory (sp - 4, buf, 4);
return sp - 4;
}
+\f
+#define regcache_cooked_write_unsigned regcache_raw_write_unsigned
-static void
-i386_do_pop_frame (struct frame_info *frame)
+#ifdef I386_REGNO_TO_SYMMETRY
+#error "The Sequent Symmetry is no longer supported."
+#endif
+
+/* According to the System V ABI, the registers %ebp, %ebx, %edi, %esi
+ and %esp "belong" to the calling function. */
+
+/* The maximum number of saved registers. This should include all
+ registers mentioned above, and %eip. */
+#define I386_NUM_SAVED_REGISTERS 9
+
+struct i386_frame_cache
+{
+ CORE_ADDR saved_regs[I386_NUM_SAVED_REGISTERS];
+ CORE_ADDR saved_sp;
+ CORE_ADDR return_pc;
+ CORE_ADDR base;
+ int frameless;
+};
+
+/* Parse the first few instructions the function to see what registers
+ were stored.
+
+ We handle these cases:
+
+ The startup sequence can be at the start of the function, or the
+ function can start with a branch to startup code at the end.
+
+ %ebp can be set up with either the 'enter' instruction, or "pushl
+ %ebp, movl %esp, %ebp" (`enter' is too slow to be useful, but was
+ once used in the System V compiler).
+
+ Local space is allocated just below the saved %ebp by either the
+ 'enter' instruction, or by "subl $<size>, %esp". 'enter' has a 16
+ bit unsigned argument for space to allocate, and the 'addl'
+ instruction could have either a signed byte, or 32 bit immediate.
+
+ Next, the registers used by this function are pushed. With the
+ System V compiler they will always be in the order: %edi, %esi,
+ %ebx (and sometimes a harmless bug causes it to also save but not
+ restore %eax); however, the code below is willing to see the pushes
+ in any order, and will handle up to 8 of them.
+
+ If the setup sequence is at the end of the function, then the next
+ instruction will be a branch back to the start. */
+
+static struct i386_frame_cache *
+i386_frame_cache (struct frame_info *frame, void **cachep)
{
- CORE_ADDR fp;
- int regnum;
- char regbuf[I386_MAX_REGISTER_SIZE];
+ struct i386_frame_cache *cache;
+ CORE_ADDR pc, start_pc;
+
+ if (*cachep)
+ return *cachep;
+
+ cache = FRAME_OBSTACK_ZALLOC (struct i386_frame_cache);
+
+ pc = frame_pc_unwind (frame);
+ start_pc = get_pc_function_start (pc);
- fp = get_frame_base (frame);
- i386_frame_init_saved_regs (frame);
+ long locals = -1;
+
+ if (start_pc != 0)
+ locals = i386_get_frame_setup (start_pc);
- for (regnum = 0; regnum < NUM_REGS; regnum++)
+ if (locals >= 0 && pc > start_pc)
{
+ ULONGEST fp;
CORE_ADDR addr;
- addr = get_frame_saved_regs (frame)[regnum];
- if (addr)
+ unsigned char op;
+ int i;
+
+ frame_unwind_unsigned_register (frame, FP_REGNUM, &fp);
+ cache->base = fp;
+
+ addr = fp - 4 - locals;
+ for (i = 0; i < I386_NUM_SAVED_REGISTERS; i++)
+ {
+ op = codestream_get ();
+ if (op < 0x50 || op > 0x57)
+ break;
+
+ cache->saved_regs[op - 0x50] = addr;
+ addr -= 4;
+ }
+
+ cache->saved_regs[PC_REGNUM] = fp + 4;
+ cache->saved_regs[FP_REGNUM] = fp;
+ cache->saved_sp = fp + 8;
+ }
+ else
+ {
+ ULONGEST sp;
+
+ /* This function is either frameless or we're at the start
+ of the function and this function's frame hasn't been
+ setup yet. In the latter case only %eip and %esp have
+ changed, and we can determine their previous values. We
+ pretend we can do the same in the former case. */
+ cache->frameless = 1;
+
+ frame_unwind_unsigned_register (frame, SP_REGNUM, &sp);
+ cache->saved_regs[PC_REGNUM] = sp;
+ cache->saved_sp = cache->saved_regs[PC_REGNUM] + 4;
+ cache->base = sp;
+ }
+
+ *cachep = cache;
+ return cache;
+}
+
+static CORE_ADDR
+i386_frame_pc_unwind (struct frame_info *frame, void **cachep)
+{
+ struct i386_frame_cache *cache = i386_frame_cache (frame, cachep);
+
+ if (cache->return_pc == 0)
+ {
+ char buf[4];
+
+ frame_unwind_register (frame, PC_REGNUM, buf);
+ cache->return_pc = extract_address (buf, 4);
+ }
+
+ return cache->return_pc;
+}
+
+static CORE_ADDR
+i386_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ ULONGEST pc;
+ frame_unwind_unsigned_register (next_frame, PC_REGNUM, &pc);
+ return (CORE_ADDR) pc;
+}
+
+static void
+i386_frame_this_id (struct frame_info *next_frame,
+ void **cachep,
+ struct frame_id *this_id)
+{
+ CORE_ADDR pc;
+ ULONGEST base;
+
+ gdb_assert (this_id != NULL);
+
+ *this_id = null_frame_id;
+
+ pc = frame_pc_unwind (next_frame);
+
+ frame_unwind_unsigned_register (next_frame, FP_REGNUM, &base);
+
+ 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;
+
+ this_id->pc = pc;
+ this_id->base = base;
+}
+
+static void
+i386_frame_register_unwind (struct frame_info *frame, void **cachep,
+ int regnum, int *optimizedp,
+ enum lval_type *lvalp, CORE_ADDR *addrp,
+ int *realnump, void *valuep)
+{
+ /* FIXME: kettenis/20030302: I don't understand why the cache isn't
+ already initialized. */
+ struct i386_frame_cache *cache = i386_frame_cache (frame, cachep);
+
+ gdb_assert (cache);
+ gdb_assert (regnum >= 0);
+
+ if (regnum == PC_REGNUM)
+ {
+ CORE_ADDR pc;
+ *optimizedp = 0;
+ *lvalp = not_lval;
+ *addrp = 0;
+ *realnump = -1;
+ pc = i386_frame_pc_unwind (frame, cachep);
+ if (valuep)
+ store_address (valuep, 4, pc);
+ }
+
+ if (regnum < I386_NUM_SAVED_REGISTERS && cache->saved_regs[regnum] != 0)
+ {
+ *optimizedp = 0;
+ *lvalp = lval_memory;
+ *addrp = cache->saved_regs[regnum];
+ *realnump = -1;
+ if (valuep)
{
- read_memory (addr, regbuf, REGISTER_RAW_SIZE (regnum));
- deprecated_write_register_gen (regnum, regbuf);
+ /* Read the value in from memory. */
+ read_memory (*addrp, valuep,
+ register_size (current_gdbarch, regnum));
}
+ return;
}
- write_register (FP_REGNUM, read_memory_integer (fp, 4));
- write_register (PC_REGNUM, read_memory_integer (fp + 4, 4));
- write_register (SP_REGNUM, fp + 8);
- flush_cached_frames ();
+
+ frame_register (frame, regnum, optimizedp, lvalp, addrp, realnump, valuep);
+}
+
+static struct i386_frame_cache *
+i386_sigtramp_cache (struct frame_info *frame, void **cachep)
+{
+ struct i386_frame_cache *cache;
+ CORE_ADDR pc, start_pc, addr;
+ struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+ int sc_offset, regnum;
+
+ if (*cachep)
+ return *cachep;
+
+ cache = FRAME_OBSTACK_ZALLOC (struct i386_frame_cache);
+
+ pc = frame_pc_unwind (frame);
+ start_pc = get_pc_function_start (pc);
+ addr = tdep->sigcontext_addr (frame->prev);
+
+ for (regnum=0; regnum<=PS_REGNUM; regnum++)
+ {
+ /* See <asm/sigcontext.h> for details on how registers are stored
+ in the sigcontext. */
+ if (regnum < PC_REGNUM)
+ sc_offset = (11 - regnum);
+ else if (regnum == PC_REGNUM)
+ sc_offset = 14;
+ else if (regnum == PS_REGNUM)
+ sc_offset = 16;
+ else
+ break;
+
+ sc_offset *= 4;
+
+ if (regnum < sizeof (cache->saved_regs))
+ cache->saved_regs[regnum] = addr + sc_offset;
+ }
+
+ cache->base = addr;
+
+ *cachep = cache;
+ return cache;
}
static void
-i386_pop_frame (void)
+i386_sigtramp_register_unwind (struct frame_info *frame, void **cachep,
+ int regnum, int *optimizedp,
+ enum lval_type *lvalp, CORE_ADDR *addrp,
+ int *realnump, void *valuep)
+{
+ struct i386_frame_cache *cache = i386_sigtramp_cache (frame, cachep);
+
+ gdb_assert (cache);
+ gdb_assert (regnum >= 0);
+
+ if (regnum < I386_NUM_SAVED_REGISTERS && cache->saved_regs[regnum] != 0)
+ {
+ *optimizedp = 0;
+ *lvalp = lval_memory;
+ *addrp = cache->saved_regs[regnum];
+ *realnump = -1;
+ if (valuep)
+ read_memory (*addrp, valuep,
+ register_size (current_gdbarch, regnum));
+ return;
+ }
+
+ /* Fall back for non-saved registers. */
+ frame_register (frame, regnum, optimizedp, lvalp, addrp, realnump, valuep);
+}
+
+static struct frame_unwind i386_sigtramp_unwind = {
+ i386_frame_this_id,
+ i386_sigtramp_register_unwind
+};
+
+static struct frame_unwind i386_frame_unwind = {
+ i386_frame_this_id,
+ i386_frame_register_unwind
+};
+
+const struct frame_unwind *
+i386_frame_p (CORE_ADDR pc)
+{
+ if (gdbarch_pc_in_sigtramp (current_gdbarch, pc, NULL))
+ return &i386_sigtramp_unwind;
+ else
+ return &i386_frame_unwind;
+}
+
+static struct frame_id
+i386_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ struct frame_id id;
+ ULONGEST base;
+
+ id.pc = frame_pc_unwind (next_frame);
+ frame_unwind_unsigned_register (next_frame, FP_REGNUM, &base);
+ id.base = base;
+ return id;
+}
+
+static void
+i386_save_dummy_frame_tos (CORE_ADDR sp)
{
- generic_pop_current_frame (i386_do_pop_frame);
+ /* We can't use the saved top-of-stack to find the right dummy frame
+ when unwinding, since we can't reconstruct it properly if the
+ dummy frame is the innermost frame. To circumvent this, we fake
+ a frame pointer here. */
+
+ regcache_cooked_write_unsigned (current_regcache, FP_REGNUM, sp);
+ generic_save_dummy_frame_tos (sp);
}
\f
@@ -1312,7 +1427,13 @@ i386_pe_skip_trampoline_code (CORE_ADDR
static int
i386_pc_in_sigtramp (CORE_ADDR pc, char *name)
{
- return (name && strcmp ("_sigtramp", name) == 0);
+ char *pname;
+ if (name)
+ pname = name;
+ else
+ find_pc_partial_function (pc, &pname, NULL, NULL);
+
+ return (name && strcmp ("_sigtramp", pname) == 0);
}
\f
@@ -1506,10 +1627,6 @@ i386_gdbarch_init (struct gdbarch_info i
tdep = XMALLOC (struct gdbarch_tdep);
gdbarch = gdbarch_alloc (&info, tdep);
- /* NOTE: cagney/2002-12-06: This can be deleted when this arch is
- ready to unwind the PC first (see frame.c:get_prev_frame()). */
- set_gdbarch_deprecated_init_frame_pc (gdbarch, init_frame_pc_default);
-
/* The i386 default settings don't include the SSE registers.
FIXME: kettenis/20020614: They do include the FPU registers for
now, which probably is not quite right. */
@@ -1591,14 +1708,12 @@ i386_gdbarch_init (struct gdbarch_info i
set_gdbarch_extract_return_value (gdbarch, i386_extract_return_value);
set_gdbarch_push_arguments (gdbarch, i386_push_arguments);
set_gdbarch_push_return_address (gdbarch, i386_push_return_address);
- set_gdbarch_pop_frame (gdbarch, i386_pop_frame);
set_gdbarch_store_struct_return (gdbarch, i386_store_struct_return);
set_gdbarch_store_return_value (gdbarch, i386_store_return_value);
set_gdbarch_extract_struct_value_address (gdbarch,
i386_extract_struct_value_address);
set_gdbarch_use_struct_convention (gdbarch, i386_use_struct_convention);
- set_gdbarch_deprecated_frame_init_saved_regs (gdbarch, i386_frame_init_saved_regs);
set_gdbarch_skip_prologue (gdbarch, i386_skip_prologue);
/* Stack grows downward. */
@@ -1608,16 +1723,9 @@ i386_gdbarch_init (struct gdbarch_info i
set_gdbarch_decr_pc_after_break (gdbarch, 1);
set_gdbarch_function_start_offset (gdbarch, 0);
- /* The following redefines make backtracing through sigtramp work.
- They manufacture a fake sigtramp frame and obtain the saved pc in
- sigtramp from the sigcontext structure which is pushed by the
- kernel on the user stack, along with a pointer to it. */
-
set_gdbarch_frame_args_skip (gdbarch, 8);
set_gdbarch_frameless_function_invocation (gdbarch,
i386_frameless_function_invocation);
- set_gdbarch_frame_chain (gdbarch, i386_frame_chain);
- set_gdbarch_deprecated_frame_saved_pc (gdbarch, i386_frame_saved_pc);
set_gdbarch_saved_pc_after_call (gdbarch, i386_saved_pc_after_call);
set_gdbarch_frame_num_args (gdbarch, i386_frame_num_args);
set_gdbarch_pc_in_sigtramp (gdbarch, i386_pc_in_sigtramp);
@@ -1629,12 +1737,19 @@ i386_gdbarch_init (struct gdbarch_info i
set_gdbarch_print_insn (gdbarch, i386_print_insn);
+ set_gdbarch_unwind_dummy_id (gdbarch, i386_unwind_dummy_id);
+ set_gdbarch_save_dummy_frame_tos (gdbarch, i386_save_dummy_frame_tos);
+
+ set_gdbarch_unwind_pc (gdbarch, i386_unwind_pc);
+
/* Add the i386 register groups. */
i386_add_reggroups (gdbarch);
set_gdbarch_register_reggroup_p (gdbarch, i386_register_reggroup_p);
/* Hook in ABI-specific overrides, if they have been registered. */
gdbarch_init_osabi (info, gdbarch);
+
+ frame_unwind_append_predicate (gdbarch, i386_frame_p);
return gdbarch;
}
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [offbyone RFC] Merge i386newframe
2003-03-14 15:43 ` Michal Ludvig
@ 2003-03-16 12:48 ` Mark Kettenis
2003-03-17 7:52 ` Michal Ludvig
0 siblings, 1 reply; 14+ messages in thread
From: Mark Kettenis @ 2003-03-16 12:48 UTC (permalink / raw)
To: mludvig; +Cc: gdb-patches, ac131313
Date: Fri, 14 Mar 2003 16:43:12 +0100
From: Michal Ludvig <mludvig@suse.cz>
Hi all,
I've reworked the merge a little to avoid the need to reorder
frame.c:get_prev_frame() and fixed the 'next' and 'return' bugs that
were in the prevoius one. Now it passes the testsuite even better than
the i386newframe code did :-)
I'm sorry I'm short of time to write more now.
Andrew and Mark, please look around the patch and tell me your opinions.
Michal,
I appreciate your efforts here, but your patch introduces
Linux-specific code into i386-tdep.c. I'm sorry, but we can't have
that. I'll try to keep my i386newframe branch synced with HEAD. I'm
afraid I don't have the time now to keep track of the offbyone branch
right now, but as Andrew merges things from there, I'll pick them up
in i386newframe. I'll certainly take a look at your patch, and
incorporate the good bits.
Mark
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [offbyone RFC] Merge i386newframe
2003-03-16 12:48 ` Mark Kettenis
@ 2003-03-17 7:52 ` Michal Ludvig
0 siblings, 0 replies; 14+ messages in thread
From: Michal Ludvig @ 2003-03-17 7:52 UTC (permalink / raw)
To: Mark Kettenis; +Cc: gdb-patches, Andrew Cagney
Mark Kettenis wrote:
> Date: Fri, 14 Mar 2003 16:43:12 +0100
> From: Michal Ludvig <mludvig@suse.cz>
>
> Hi all,
> I've reworked the merge a little to avoid the need to reorder
> frame.c:get_prev_frame() and fixed the 'next' and 'return' bugs that
> were in the prevoius one. Now it passes the testsuite even better than
> the i386newframe code did :-)
> I'm sorry I'm short of time to write more now.
>
> Andrew and Mark, please look around the patch and tell me your opinions.
>
> Michal,
>
> I appreciate your efforts here, but your patch introduces
> Linux-specific code into i386-tdep.c. I'm sorry, but we can't have
> that.
Hmm, yes. Might be, because I don't use anything else but linux and
don't have an idea what's different on non-linux targets. Is it only the
i386_sigtramp_cache() that is different? Would it help if I moved
sigtramp unwinders (or maybe only i386_sigtramp_cache()) into
i386-linux-tdep.c?
Anyway could you or Andrew tell me if I did at least the linux bits
right? I'd like to know if I could build x86-64 support in the same way
or even better on top of i386?
> I'll try to keep my i386newframe branch synced with HEAD. I'm
> afraid I don't have the time now to keep track of the offbyone branch
> right now, but as Andrew merges things from there, I'll pick them up
> in i386newframe. I'll certainly take a look at your patch, and
> incorporate the good bits.
The problem is that Andrew told me o use offbyone branch but I'd like to
be in sync with you on i386newframe as well... But these two branches
aren't compatible anymore.
Michal Ludvig
--
* SuSE CR, s.r.o * mludvig@suse.cz
* (+420) 296.545.373 * http://www.suse.cz
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2003-04-07 18:53 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2003-03-12 22:06 [offbyone RFC] Merge i386newframe Michal Ludvig
2003-03-12 22:42 ` Daniel Jacobowitz
2003-03-13 19:05 ` Andrew Cagney
2003-03-13 22:46 ` Mark Kettenis
2003-03-14 15:48 ` Andrew Cagney
2003-03-14 16:19 ` Andrew Cagney
2003-03-14 18:20 ` Andrew Cagney
2003-04-06 17:10 ` Andrew Cagney
2003-04-07 18:53 ` Mark Kettenis
2003-03-14 11:54 ` Michal Ludvig
2003-03-14 15:59 ` Andrew Cagney
2003-03-14 15:43 ` Michal Ludvig
2003-03-16 12:48 ` Mark Kettenis
2003-03-17 7:52 ` Michal Ludvig
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox