From: Jason Molenda <jmolenda@apple.com>
To: gdb-patches@sources.redhat.com
Cc: Mark Kettenis <kettenis@jive.nl>
Subject: The gdb x86 function prologue parser
Date: Wed, 08 Jun 2005 05:51:00 -0000 [thread overview]
Message-ID: <85C775AE-3B05-431E-96D2-49EA9D1413E6@apple.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 6281 bytes --]
Hello, I'm Jason Molenda and I work on gdb for Apple. :-)
As was announced yesterday, we'll be transitioning from PPC to x86
over the next couple of years. Over the last few months I had a
delightful little introduction to the x86 architecture (read: read
the IA32 PDFs until I couldn't see straight any more) and worked to
get our x86 gdb support up to snuff for this software release.
One of the biggest problems we found was the x86 function prologue
parser is remarkably weak. We have a very mature and featureful
prologue parser on our ppc side and an amazing number of bugs were
directed my way as we had people pounding on the x86 side. We aren't
using DWARF yet, so CFI can't save our bacon -- the prologue parser
has to work or our gdb fails.
There are a couple classes of changes I made, and I spent today
trying to massage them into some kind of presentable form. This is
not perfect -- well, to be honest, this is just a first sketch -- but
this is a HUGE improvement over the existing facilities. I wrote the
code while under immense deadline pressure so I'm not particularly
interested in how I implemented any of it. But changes akin to these
are necessary if you want to debug programs with optimized code on
the stack and without CFI. I'll guarantee it.
Roughly speaking, here are the class of changes included in this patch.
1. i386_match_insn() bug fixes. It wouldn't work for an instruction
pattern of 1 byte, and it would never check anything beyond the 2nd
byte (notice where the final "return insn" is located). I've added
patterns that can match prologue instructions, so exceptions had to
be added for the big two (push %ebp, mov %esp, %ebp) and the
equivalent ones used in the first frame (_start()).
2. i386_frame_setup_skip_insns table expansion. Because you can't
skip over an unknown instruction on x86 without knowing its length,
this was of paramount importance. Initially I waited for users to
tell me of prologues that gdb was failing on, but this was taking too
long and there were too many instructions scheduled into prologues
for me to hear of them in time. So I wrote a little maintenance
command (not included in the patch to keep things simple) which would
tell you if gdb could parse through the prologue of a given
function. Then with a couple of shell scripts, I could have gdb try
to analyze the prologues of every function in every library on my
MacOS X system and show me the ones it failed on. I'd add them to
this list. I also made a little testsuite generator where the input
looks like
# SOURCE: RedHat FedoraCore2 /lib/ld-2.3.3.so _dl_reloc_bad_type
# PROLOGUE
push %ebp
shl $0x5, %ecx # [ 0xc1 0xe1 0x05 ]
mov %esp, %ebp
sub $0x8, %esp
# EPILOGUE
add $0x8, %esp
pop %ebp
ret
and a script that transforms the patterns into a test program and a
Dejagnu expect script. So you can ensure that you don't regress the
prologue parser. This was the lesson we learned in writing our PPC
parser -- we have this wonderfully ornate parser with lots of
exceptions and known tricks, but no testsuite for it. So whenever we
change it we're cringing because the gdb testsuite has nothing useful
in it. (you need optimized, no debug info test cases to be sure it's
still working right). The testsuite stuff isn't included in this
patch, but I'll put that together soon and send it along if anyone's
interested.
3. relatively minor changes to i386_analyze_frame_setup(). It had to
have the push %ebp as the very first instruction or it would give
up. That's really bad -- the compiler can (and does) schedule all
sorts of stuff before that instruction.
4. new function, i386_find_esp_adjustments(). This is used in a
frameless leaf function where the compiler may create space on the
stack for local variables and stuff, but doesn't call anything so it
doesn't save the caller's frame pointer. And it allows -fomit-leaf-
frame-pointer codegen to be debugged. -fomit-frame-pointer is a
whole lot more complicated, but this wasn't so bad. (we didn't end
up enabling -fomit-leaf-frame-pointer in this release because of the
schedule time constraints, but that's why I wrote it)
5. Huge i386_frame_cache() changes. There's no way around it, this
function is just not right. It doesn't handle frameless functions
correctly at all. It's written without a clear understanding of the
different classes of functions it needs to handle and works primarily
by luck. And for goodness sakes, if we can't figure out anything
about a function that's not at the top of the stack, don't you think
it'd be reasonable to assume that the function has set up a stack
frame and saved the caller's EBP? Sure seems like a reasonable
assumption to me. Why can't this function do something even that
basic? This function really cheesed my mellow.
Mark, I want to say that I'm not directing any of these criticisms
towards you -- I've been looking over the changes you've made and
they're definite improvements over the existing code. The existing
code bites, though. I can't even begin to imagine how annoyed
developers using the FSF gdb on x86 must be. The changes I'm sending
here are not a panacea/beautiful/perfect, but *functionally* they're
a huge improvement. Now that our release is out there I'll be more
than happy to revisit the decisions/implementation that I came up
with on little sleep. :-)
Oh, and I ran my "find all prologues gdb can't parse" on a FedoraCore
2 system I have handy here at the office today and added the patterns
of the biggest offenders. There are still a few patterns I need to
add to get 100% parsing success but I want to go home :-) so that'll
be for another day.
We're in the middle of an all-week Apple developer's conference, so
my replies may not be very speedy (I'm off-line 10-12 hours a day; I
slipped away to get this patch together today) until the weekend.
But I'll try to stay on top of any questions or comments and address
them promptly.
I'm looking forward to making x86 gdb excellent. There's definitely
more work to do and I intend to be a part of that.
Jason Molenda
Apple Computer
[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 21548 bytes --]
Index: i386-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/i386-tdep.c,v
retrieving revision 1.213
diff -u -p -w -r1.213 i386-tdep.c
--- i386-tdep.c 28 May 2005 16:44:28 -0000 1.213
+++ i386-tdep.c 8 Jun 2005 05:24:14 -0000
@@ -21,6 +21,7 @@
Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
+#include <stdint.h>
#include "defs.h"
#include "arch-utils.h"
#include "command.h"
@@ -287,9 +288,10 @@ i386_breakpoint_from_pc (CORE_ADDR *pc,
struct i386_frame_cache
{
- /* Base address. */
+ /* Base address, usually the contents of the EBP. */
CORE_ADDR base;
CORE_ADDR sp_offset;
+ /* The function's start address for this frame. */
CORE_ADDR pc;
/* Saved registers. */
@@ -473,7 +475,7 @@ i386_skip_probe (CORE_ADDR pc)
}
/* Maximum instruction length we need to handle. */
-#define I386_MAX_INSN_LEN 6
+#define I386_MAX_INSN_LEN 7
/* Instruction description. */
struct i386_insn
@@ -495,25 +497,46 @@ i386_match_insn (CORE_ADDR pc, struct i3
op = read_memory_unsigned_integer (pc, 1);
+ /* `push %ebp'? Let the caller look into it. */
+ if (op == 0x55)
+ return NULL;
+
for (insn = skip_insns; insn->len > 0; insn++)
{
if ((op & insn->mask[0]) == insn->insn[0])
{
unsigned char buf[I386_MAX_INSN_LEN - 1];
- size_t i;
+ int i, insn_matched;
+
+ if (insn->len == 1)
+ return insn;
gdb_assert (insn->len > 1);
gdb_assert (insn->len <= I386_MAX_INSN_LEN);
read_memory (pc + 1, buf, insn->len - 1);
+
+ /* `mov %esp, %ebp'? `xor $ebp, $ebp'? `push $0x0'?
+ Let the caller check it out. */
+ if (insn->len == 2
+ && ((op == 0x89 && buf[0] == 0xe5) /* mov %esp, %ebp */
+ || (op == 0x8b && buf[0] == 0xec) /* mov %esp, %ebp */
+ || (op == 0x31 && buf[0] == 0xed) /* xor %ebp, %ebp */
+ || (op == 0x6a && buf[0] == 0x00))) /* push $0x0 */
+ return NULL;
+
+ insn_matched = 1;
for (i = 1; i < insn->len; i++)
{
if ((buf[i - 1] & insn->mask[i]) != insn->insn[i])
+ {
+ insn_matched = 0;
break;
-
- return insn;
}
}
+ if (insn_matched)
+ return insn;
+ }
}
return NULL;
@@ -554,6 +577,91 @@ struct i386_insn i386_frame_setup_skip_i
/* `movl m32, %edx' */
{ 6, { 0x89, 0x15 }, {0xff, 0xff } },
+ /* "01 /r ADD r/m32, r32" */
+ { 2, { 0x01, 0xd0 }, { 0xff, 0xd0 } },
+ /* "0F B6 /r MOVZX r32, r/m8" (aka `movzbl %al, %eax') */
+ { 3, { 0x0f, 0xb6, 0xc0 }, { 0xff, 0xff, 0xc0 } },
+ /* "0F B7 /r MOVZX r32, r/m16" (aka `movzwl r16, r32') */
+ { 3, { 0x0f, 0xb7, 0xc0 }, { 0xff, 0xff, 0xc0 } },
+ /* "25 id AND EAX, imm32" */
+ { 5, { 0x25 }, { 0xff } },
+ /* "31 /r XOR r/m32, r32" */
+ { 2, { 0x31, 0xc0 }, { 0xff, 0xc0 } },
+ /* "40+ rd INC r32" */
+ { 1, { 0x40 }, { 0xf8 } },
+ /* "48+rw DEC r32" */
+ { 1, { 0x48 }, { 0xf8 } },
+ /* "50+rd PUSH r32" */
+ { 1, { 0x50 }, { 0xf8 } },
+ /* "58+ rd POP r32" */
+ { 1, { 0x58 }, { 0xf8 } },
+ /* "B8+ rw MOV r16, imm16" */
+ { 4, { 0x66, 0xb8 }, { 0xff, 0xf8 } },
+ /* "74 cb JE rel8" */
+ { 2, { 0x74 }, { 0xff } },
+ /* "80 /7 ib CMP r/m8, imm8" (aka `cmpb imm8,(r32)') */
+ { 3, { 0x80, 0x38 }, { 0xff, 0xf8 } },
+ /* "81 /4 id AND r/m32, imm32" */
+ { 6, { 0x81, 0xe0 }, { 0xff, 0xf8 } },
+ /* "81 /0 id ADD r/m32, imm32" */
+ { 6, { 0x81, 0xc0 }, { 0xff, 0xf8 } },
+ /* "83 /7 ib CMP r/m32, imm8" */
+ { 7, { 0x83, 0x3d }, { 0xff, 0xff } },
+ /* "83 /0 ib ADD r/m32, imm8" */
+ { 3, { 0x83, 0xc0 }, { 0xff, 0xf8 } },
+ /* "83 /1 ib OR r/m32, imm8" */
+ { 3, { 0x83, 0xc8 }, { 0xff, 0xf8 } },
+ /* "83 /4 ib AND r/m32, imm8" */
+ { 3, { 0x83, 0xe0 }, { 0xff, 0xf8 } },
+ /* "83 /5 ib SUB r/m32, imm8" */
+ { 3, { 0x83, 0xe8 }, { 0xff, 0xf8 } },
+ /* "83 /7 ib CMP r/m32, imm8" */
+ { 3, { 0x83, 0xf8 }, { 0xff, 0xf8 } },
+ /* "85 /r TEST r/m32, r32" */
+ { 2, { 0x85, 0xc0 }, { 0xff, 0xc0 } },
+ /* "89 /r MOV r/m32, r32" (aka `mov r32, (r32)') */
+ { 2, { 0x89, 0x00 }, { 0xff, 0xf8 } },
+ /* "89 /r MOV r/m32, r32" (aka `mov r32, (r32)') */
+ { 2, { 0x89, 0x10 }, { 0xff, 0xf8 } },
+ /* "89 /r MOV r/m32, r32" */
+ { 2, { 0x89, 0xc0 }, { 0xff, 0xc0 } },
+ /* "8B /r MOV r32, r/m32" (aka `mov imm8(r32), r32') */
+ { 3, { 0x8b, 0x40 }, { 0xff, 0xf8 } },
+ /* "8B /r MOV r32, r/m32" */
+ { 6, { 0x8b, 0x80 }, { 0xff, 0xf8 } },
+ /* "8B /r MOV r32, r/m32" (aka `mov imm32(r32), r32') */
+ { 6, { 0x8b, 0x90 }, { 0xff, 0xf8 } },
+ /* "8D /r LEA r32, m" (aka `lea imm8(r32), r32') */
+ { 3, { 0x8d, 0x48 }, { 0xff, 0xf8 } },
+ /* "8D /r LEA r32, m" (aka `lea imm32(r32), r32') */
+ { 6, { 0x8d, 0x90 }, { 0xff, 0xf8 } },
+ /* "90 NOP" */
+ { 1, { 0x90 }, { 0xff } },
+ /* "A9 id TEST EAX, imm32" */
+ { 5, { 0xa9 }, { 0xff } },
+ /* "C1 /4 ib SAL r/m32, imm8" (aka `shl imm8, r32') */
+ { 3, { 0xc1, 0xe0 }, { 0xff, 0xf8 } },
+ /* "C1 /7 ib SAR r/m32, imm8" */
+ { 3, { 0xc1, 0xf8 }, { 0xff, 0xf8 } },
+ /* "C6 /0 MOV r/m8, imm8" (aka `mov imm8, (r32)') */
+ { 3, { 0xc6, 0x00 }, { 0xff, 0xf8 } },
+ /* "C7 /o MOV r/m32, imm32 (aka `mov imm32, (r32)') */
+ { 6, { 0xc7, 0x00 }, { 0xff, 0xf8 } },
+ /* "D9 EE FLDZ" */
+ { 2, { 0xd9, 0xee }, { 0xff, 0xff } },
+ /* "E8 cd CALL rel32" */
+ { 5, { 0xe8 }, { 0xff } },
+ /* "EB cb JMP rel8" */
+ { 2, { 0xeb }, { 0xff } },
+ /* "F7 /6 DIV r/m32" */
+ { 2, { 0xf7, 0xf0 }, { 0xff, 0xf8 } },
+ /* "FC CLD" */
+ { 1, { 0xfc }, { 0xff } },
+ /* "FF /6 PUSH r/m32" */
+ { 6, { 0xff, 0xb0 }, { 0xff, 0xf8 } },
+ /* "FF /4 JMP r/m32" */
+ { 2, { 0xff, 0xe0 }, { 0xff, 0xf8 } },
+
/* Check for `xorl r32, r32' and the equivalent `subl r32, r32'.
Because of the symmetry, there are actually two ways to encode
these instructions; opcode bytes 0x29 and 0x2b for `subl' and
@@ -587,21 +695,71 @@ i386_analyze_frame_setup (CORE_ADDR pc,
gdb_byte op;
int skip = 0;
+ /* This function returns a CORE_ADDR which is the instruction
+ following the last frame-setup instruction we saw such that "frame-setup
+ instruction" is one of push %ebp, push $0x0, mov %esp, %ebp,
+ sub $<imm>, $esp, or enter.
+ Specifically, we may scan past some of these instructions but we don't
+ want to return the last address we scanned to -- we must return the
+ address of the instruction after one of those frame setup insns so that
+ i386_analyze_register_saves () can look for register saves that may exist
+ after them.
+ (and you can have register saves without any frame setup, e.g. in a
+ frameless function.)
+ I pedantically changed all returns to return end_of_frame_setup so it
+ is completely clear what is going on. jmolenda/2005-05-03 */
+
+ CORE_ADDR end_of_frame_setup = pc;
+
if (limit <= pc)
return limit;
+
+ /* Skip over non-prologue instructions until we hit one of
+ push %ebp [ 0x55 ]
+ push $0x0 [ 0x6a 0x00 ]
+ xor %ebp, %ebp [ 0x31 0xed ]
+ the latter instructions being the first frame setup insns in
+ MacOS X and Linux _start()s. */
+
op = read_memory_unsigned_integer (pc, 1);
+ while (op != 0x55
+ && (op != 0x6a ||
+ read_memory_unsigned_integer (pc + skip + 1, 1) != 0x00)
+ && (op != 0x31 ||
+ read_memory_unsigned_integer (pc + skip + 1, 1) != 0xed)
+ && pc + skip <= limit)
+ {
+ insn = i386_match_insn (pc + skip, i386_frame_setup_skip_insns);
+ if (insn)
+ {
+ skip += insn->len;
+ op = read_memory_unsigned_integer (pc + skip, 1);
+ }
+ else
+ break;
+ }
+
+ if (limit <= pc + skip)
+ return end_of_frame_setup;
- if (op == 0x55) /* pushl %ebp */
+ /* Do we have our initial frame setup instruction? */
+ if (op == 0x55 || op == 0x6a || op == 0x31)
{
/* Take into account that we've executed the `pushl %ebp' that
starts this instruction sequence. */
cache->saved_regs[I386_EBP_REGNUM] = 0;
cache->sp_offset += 4;
- pc++;
+
+ /* Skip over the second byte of `push $0x0' and `xor %ebp, %ebp' */
+ if (op == 0x6a || op == 0x31)
+ skip += 2;
+ else
+ skip++;
+ end_of_frame_setup = pc + skip;
/* If that's all, return now. */
- if (limit <= pc)
+ if (limit <= pc + skip)
return limit;
/* Check for some special instructions that might be migrated by
@@ -623,7 +781,7 @@ i386_analyze_frame_setup (CORE_ADDR pc,
/* If that's all, return now. */
if (limit <= pc + skip)
- return limit;
+ return end_of_frame_setup;
op = read_memory_unsigned_integer (pc + skip, 1);
@@ -632,14 +790,14 @@ i386_analyze_frame_setup (CORE_ADDR pc,
{
case 0x8b:
if (read_memory_unsigned_integer (pc + skip + 1, 1) != 0xec)
- return pc;
+ return end_of_frame_setup;
break;
case 0x89:
if (read_memory_unsigned_integer (pc + skip + 1, 1) != 0xe5)
- return pc;
+ return end_of_frame_setup;
break;
default:
- return pc;
+ return end_of_frame_setup;
}
/* OK, we actually have a frame. We just don't know how large
@@ -648,6 +806,7 @@ i386_analyze_frame_setup (CORE_ADDR pc,
instructions mentioned before. */
cache->locals = 0;
pc += (skip + 2);
+ end_of_frame_setup = pc;
/* If that's all, return now. */
if (limit <= pc)
@@ -695,7 +854,7 @@ i386_analyze_frame_setup (CORE_ADDR pc,
return pc + 4;
}
- return pc;
+ return end_of_frame_setup;
}
/* Check whether PC points at code that saves registers on the stack.
@@ -771,6 +930,11 @@ i386_analyze_prologue (CORE_ADDR pc, COR
static CORE_ADDR
i386_skip_prologue (CORE_ADDR start_pc)
{
+ /* FIXME: this code assumes the "call L1; L1: pop $ebx" type of pic base
+ setup, but that's less optimal on x86 than a call to
+ __i686.get_pc_thunk.bx and friends, so you're unlikely to see this
+ sequence these days. jmolenda/2005-06-06. */
+
static gdb_byte pic_pat[6] =
{
0xe8, 0, 0, 0, 0, /* call 0x0 */
@@ -844,6 +1008,95 @@ i386_skip_prologue (CORE_ADDR start_pc)
return pc;
}
+/* Find adjustments of the ESP so we can locate the
+ caller's saved EIP and backtrace out of a frameless function. PC is
+ the start of the function. CURRENT_PC is the current instruction,
+ or the last instruction of the function to limit this search.
+
+ Returns a signed offset of how much the ESP has moved since the
+ start of the function. The return value should be a negative number
+ or 0. */
+
+static int
+i386_find_esp_adjustments (CORE_ADDR pc, CORE_ADDR current_pc)
+{
+ gdb_byte op, next_op;
+ int esp_change = 0;
+ struct i386_insn *insn;
+
+ if (pc == current_pc)
+ return esp_change;
+
+ /* We're looking for PUSH r32, POP r32, SUB $x,ESP, ADD $x ESP. */
+
+ while (pc < current_pc)
+ {
+ op = read_memory_unsigned_integer (pc, 1);
+ next_op = read_memory_unsigned_integer (pc + 1, 1);
+
+ /* `push %ebp'? We shouldn't see that in here; give up. */
+ if (op == 0x55)
+ return esp_change;
+ /* `ret'? We're at the end of the func; stop parsing. */
+ if (op == 0xc3)
+ return esp_change;
+
+ /* 50+rd PUSH r32 */
+ if ((op & 0xf8) == 0x50)
+ {
+ esp_change -= 4;
+ pc += 1;
+ continue;
+ }
+ /* 58+ rd POP r32 */
+ if ((op & 0xf8) == 0x58)
+ {
+ esp_change += 4;
+ pc += 1;
+ continue;
+ }
+ /* 83 /5 ib SUB r/m32, imm8 */
+ if (op == 0x83 && next_op == 0xec)
+ {
+ uint8_t imm8 = read_memory_integer (pc + 2, 1);
+ esp_change -= imm8;
+ pc += 3;
+ continue;
+ }
+ /* 81 /5 id SUB r/m32, imm32 */
+ if (op == 0x81 && next_op == 0xec)
+ {
+ uint32_t imm32 = read_memory_integer (pc + 2, 4);
+ esp_change -= imm32;
+ pc += 6;
+ continue;
+ }
+ /* 83 /0 ib ADD r/m32, imm8 */
+ if (op == 0x83 && next_op == 0xc4)
+ {
+ uint8_t imm8 = read_memory_integer (pc + 2, 1);
+ esp_change += imm8;
+ pc += 3;
+ continue;
+ }
+ /* 81 /0 id ADD r/m32, imm8 */
+ if (op == 0x81 && next_op == 0xc4)
+ {
+ uint32_t imm32 = read_memory_integer (pc + 2, 4);
+ esp_change += imm32;
+ pc += 6;
+ continue;
+ }
+
+ insn = i386_match_insn (pc, i386_frame_setup_skip_insns);
+ if (insn)
+ pc += insn->len;
+ else
+ return esp_change; /* Hit an instruction we don't know; stop here. */
+ }
+ return esp_change;
+}
+
/* This function is 64-bit safe. */
static CORE_ADDR
@@ -856,7 +1109,30 @@ i386_unwind_pc (struct gdbarch *gdbarch,
}
\f
-/* Normal frames. */
+/* Normal frames.
+ This function must handle the following possible function types:
+
+ 0. We're in a function we cannot know anything about - we have no
+ symbol for it; we can't find the start address.
+
+ 1. The function is frameless.
+ a. The ESP hasn't reached its Final Resting Place for the body of
+ the function yet.
+ b. We've finished the prologue (wherein we move the ESP, i.e. SUBs to
+ make space for local storage, PUSHes to preserve saved regs.)
+
+ 2. The function sets up a frame and
+ a. We haven't executed any prologue instructions.
+ b. We've executed the initial push %ebp (this one is critical).
+ c. We've executed the mov %esp, %ebp
+ d. We've completed the entire prologue.
+ e. We're in the middle of a function which has a prologue,
+ but we can't parse it (we hit an unknown instruction mid-prologue).
+
+ When reading i386_frame_cache, keep these three function types in mind
+ and the different stages for #1 and #2 - the behavior of this function
+ differs greatly depending on where you are. */
+
static struct i386_frame_cache *
i386_frame_cache (struct frame_info *next_frame, void **this_cache)
@@ -864,6 +1140,32 @@ i386_frame_cache (struct frame_info *nex
struct i386_frame_cache *cache;
gdb_byte buf[4];
int i;
+ int potentially_frameless;
+ CORE_ADDR prologue_parsed_to = 0;
+ CORE_ADDR current_pc;
+
+ /* If the frame we're examining is frame #0, we could
+ be frameless. If NEXT_FRAME is _sigtramp(), then we could be
+ frameless.
+
+ Explanation: If a frameless function is executing when a
+ signal is caught, the frameless function will have _sigtramp()
+ as its next_frame, followed by whatever signal handler is defined.
+ This is not as rare as you'd think, at least in a testsuite:
+ On MacOS X, sleep() calls nanosleep() which calls mach_wait_until()
+ which is frameless. If an alarm(1) is done before that sequence,
+ you'll get a frameless function in the middle of the stack.
+
+ If potentially_frameless == 0, there's no way the function we're
+ examining is frameless; it has a stack frame set up with
+ the saved-EBP/saved-EIP at the standard locations.
+ (not entirely true -- if gcc's -fomit-frame-pointer is used you can
+ have a function that doesn't ever set up the EBP, but calls other
+ functions. Handling that situation correctly is not easy with
+ i386-tdep.c's frame code as it stands today.) */
+
+ potentially_frameless = frame_relative_level (next_frame) == -1
+ || get_frame_type (next_frame) == SIGTRAMP_FRAME;
if (*this_cache)
return *this_cache;
@@ -871,41 +1173,86 @@ i386_frame_cache (struct frame_info *nex
cache = i386_alloc_frame_cache ();
*this_cache = cache;
- /* In principle, for normal frames, %ebp holds the frame pointer,
- which holds the base address for the current stack frame.
- However, for functions that don't need it, the frame pointer is
- optional. For these "frameless" functions the frame pointer is
- actually the frame pointer of the calling frame. Signal
- trampolines are just a special case of a "frameless" function.
- They (usually) share their frame pointer with the frame that was
- in progress when the signal occurred. */
+ /* Set up reasonable defaults that we'll override later on as necessary. */
+
+ /* For normal frames, saved-%eip is stored at 4(%ebp). */
+ cache->saved_regs[I386_EIP_REGNUM] = 4;
+
+ /* For normal frames, saved-%ebp is stored at 0(%ebp). */
+ cache->saved_regs[I386_EBP_REGNUM] = 0;
frame_unwind_register (next_frame, I386_EBP_REGNUM, buf);
cache->base = extract_unsigned_integer (buf, 4);
if (cache->base == 0)
return cache;
- /* For normal frames, %eip is stored at 4(%ebp). */
- cache->saved_regs[I386_EIP_REGNUM] = 4;
-
cache->pc = frame_func_unwind (next_frame);
+ current_pc = frame_pc_unwind (next_frame);
+
+ /* Only do i386_analyze_prologue () if we found a debug symbol pointing to
+ the actual start of the function. */
if (cache->pc != 0)
- i386_analyze_prologue (cache->pc, frame_pc_unwind (next_frame), cache);
+ i386_analyze_prologue (cache->pc, current_pc, cache);
+
+ /* Let's see if the prologue parser didn't find any prologue instructions.
+ And our current function has the potential to be frameless.
+ If so, let's go frameless. Assume EBP is unused, or not yet used.
+
+ We'll put ESP in the cache->base instead of EBP; for genuinely
+ frameless (e.g. -momit-leaf-frame-pointer) functions, the
+ the debug info for function args will be relative to ESP once its
+ setup/adjustements in the prologue are complete, so cache->base has
+ to hold the stack pointer if we're to find them. */
+
+ /* We found a function-start address,
+ or $pc is at 0x0 (someone jmp'ed thru NULL ptr). */
+ if ((cache->pc != 0 || current_pc == 0)
+ /* The prologue parser didn't find any prologue instructions. */
+ && prologue_parsed_to == cache->pc
+ /* We have the potential to be frameless. */
+ && potentially_frameless)
+ {
+ int esp_offset;
+ CORE_ADDR actual_frame_base;
+ esp_offset = i386_find_esp_adjustments (cache->pc, current_pc);
+ frame_unwind_register (next_frame, I386_ESP_REGNUM, buf);
+ actual_frame_base = extract_unsigned_integer (buf, 4) - esp_offset;
+ cache->base = extract_unsigned_integer (buf, 4);
+ cache->saved_sp = actual_frame_base + 4;
+ cache->saved_regs[I386_EBP_REGNUM] = -1;
+ cache->saved_regs[I386_EIP_REGNUM] = 0;
+ /* NB: There's a good chance we didn't record register saves a la
+ i386_analyze_register_saves. It'd be nice to fix this.
+ For now we'll say "Debugging optimized code is an adventure!"
+ jmolenda/2005-04-27 */
- if (cache->locals < 0)
+ for (i = 0; i < I386_NUM_SAVED_REGS; i++)
+ if (cache->saved_regs[i] != -1)
+ cache->saved_regs[i] += actual_frame_base;
+ return cache;
+ }
+
+ if (cache->locals < 0 && potentially_frameless)
{
- /* We didn't find a valid frame, which means that CACHE->base
- currently holds the frame pointer for our calling frame. If
- we're at the start of a function, or somewhere half-way its
- prologue, the function's frame probably hasn't been fully
- setup yet. Try to reconstruct the base address for the stack
- frame by looking at the stack pointer. For truly "frameless"
- functions this might work too. */
+ /* We've seen PART of a frame setup, but not the whole deal.
+ We've probably executed just the `push %ebp'.
+ Right now, ESP has our real frame base address in it. */
frame_unwind_register (next_frame, I386_ESP_REGNUM, buf);
cache->base = extract_unsigned_integer (buf, 4) + cache->sp_offset;
}
+ /* If we have no idea where this function began (so we can't analyze
+ the prologue in any way), what should we assume? Frameless or
+ not? It's a tough call. On Linux systems, a call to a system
+ library involves a couple of trampoline jumps that have no symbols,
+ so to work correctly on Linux we MUST assume frameless. */
+ if (potentially_frameless && cache->pc == 0)
+ {
+ cache->saved_regs[I386_EIP_REGNUM] = 4;
+ cache->saved_regs[I386_EBP_REGNUM] = -1;
+ }
+
/* Now that we have the base address for the stack frame we can
calculate the value of %esp in the calling frame. */
cache->saved_sp = cache->base + 8;
next reply other threads:[~2005-06-08 5:51 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-06-08 5:51 Jason Molenda [this message]
2005-06-08 13:24 ` Daniel Jacobowitz
2005-06-08 16:58 ` Jason Molenda
2005-06-12 7:48 ` Mark Kettenis
2005-06-12 8:44 ` Eli Zaretskii
[not found] ` <3364FC4D-63FB-493B-9136-D118F74C13BB@mit.edu>
2005-06-12 13:17 ` Eli Zaretskii
2005-06-12 14:28 ` Daniel Jacobowitz
2005-06-13 18:21 ` Michael Snyder
2005-06-13 19:13 ` Eli Zaretskii
2005-06-08 14:52 ` Eli Zaretskii
2005-06-08 17:01 ` Jason Molenda
2005-06-08 18:03 ` Eli Zaretskii
2005-06-08 19:58 ` Andreas Schwab
2005-06-09 6:26 ` Jason Molenda
2005-06-09 9:02 ` Andreas Schwab
2005-06-10 20:23 ` Michael Snyder
2005-06-12 7:57 ` Mark Kettenis
2005-06-10 20:29 ` Michael Snyder
2005-06-10 21:18 ` Eli Zaretskii
2005-06-12 7:07 ` Mark Kettenis
2005-06-12 8:50 ` Eli Zaretskii
2005-06-13 22:04 ` Jason Molenda
2005-06-13 22:35 ` Mark Kettenis
2005-06-13 22:51 ` Daniel Jacobowitz
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=85C775AE-3B05-431E-96D2-49EA9D1413E6@apple.com \
--to=jmolenda@apple.com \
--cc=gdb-patches@sources.redhat.com \
--cc=kettenis@jive.nl \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox