From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 4956 invoked by alias); 8 Jun 2005 05:51:42 -0000 Mailing-List: contact gdb-patches-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sources.redhat.com Received: (qmail 4917 invoked by uid 22791); 8 Jun 2005 05:51:22 -0000 Received: from mail-out4.apple.com (HELO mail-out4.apple.com) (17.254.13.23) by sourceware.org (qpsmtpd/0.30-dev) with ESMTP; Wed, 08 Jun 2005 05:51:21 +0000 Received: from mailgate1.apple.com (a17-128-100-225.apple.com [17.128.100.225]) by mail-out4.apple.com (8.12.11/8.12.11) with ESMTP id j585pJOQ006946 for ; Tue, 7 Jun 2005 22:51:19 -0700 (PDT) Received: from relay2.apple.com (relay2.apple.com) by mailgate1.apple.com (Content Technologies SMTPRS 4.3.17) with ESMTP id ; Tue, 7 Jun 2005 22:51:19 -0700 Received: from [17.201.22.21] (moleja.apple.com [17.201.22.21]) by relay2.apple.com (8.12.11/8.12.11) with ESMTP id j585pH1Z003941; Tue, 7 Jun 2005 22:51:17 -0700 (PDT) Mime-Version: 1.0 (Apple Message framework v728) To: gdb-patches@sources.redhat.com Message-Id: <85C775AE-3B05-431E-96D2-49EA9D1413E6@apple.com> Content-Type: multipart/mixed; boundary=Apple-Mail-6-316259868 Cc: Mark Kettenis Subject: The gdb x86 function prologue parser From: Jason Molenda Date: Wed, 08 Jun 2005 05:51:00 -0000 X-Virus-Checked: Checked by ClamAV on sourceware.org X-SW-Source: 2005-06/txt/msg00060.txt.bz2 --Apple-Mail-6-316259868 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset=US-ASCII; delsp=yes; format=flowed Content-length: 6281 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 --Apple-Mail-6-316259868 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; x-unix-mode=0644; name="patch.txt" Content-Disposition: attachment; filename=patch.txt Content-length: 21625 Index: i386-tdep.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D 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. */ =20 +#include #include "defs.h" #include "arch-utils.h" #include "command.h" @@ -287,9 +288,10 @@ i386_breakpoint_from_pc (CORE_ADDR *pc,=20 =20 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; =20 /* Saved registers. */ @@ -473,7 +475,7 @@ i386_skip_probe (CORE_ADDR pc) } =20 /* Maximum instruction length we need to handle. */ -#define I386_MAX_INSN_LEN 6 +#define I386_MAX_INSN_LEN 7 =20 /* Instruction description. */ struct i386_insn @@ -495,25 +497,46 @@ i386_match_insn (CORE_ADDR pc, struct i3 =20 op =3D read_memory_unsigned_integer (pc, 1); =20 + /* `push %ebp'? Let the caller look into it. */ + if (op =3D=3D 0x55) + return NULL; + for (insn =3D skip_insns; insn->len > 0; insn++) { if ((op & insn->mask[0]) =3D=3D insn->insn[0]) { unsigned char buf[I386_MAX_INSN_LEN - 1]; - size_t i; + int i, insn_matched; + + if (insn->len =3D=3D 1) + return insn; =20 gdb_assert (insn->len > 1); gdb_assert (insn->len <=3D I386_MAX_INSN_LEN); =20 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 =3D=3D 2 + && ((op =3D=3D 0x89 && buf[0] =3D=3D 0xe5) /* mov %esp,= %ebp */ + || (op =3D=3D 0x8b && buf[0] =3D=3D 0xec) /* mov %esp,= %ebp */ + || (op =3D=3D 0x31 && buf[0] =3D=3D 0xed) /* xor %ebp,= %ebp */ + || (op =3D=3D 0x6a && buf[0] =3D=3D 0x00))) /* push $0x0= */ + return NULL; +=20=20=20=20=20=20=20=20=20=20 + insn_matched =3D 1; for (i =3D 1; i < insn->len; i++) { if ((buf[i - 1] & insn->mask[i]) !=3D insn->insn[i]) + { + insn_matched =3D 0; break; - - return insn; } } + if (insn_matched) + return insn; + } } =20 return NULL; @@ -554,6 +577,91 @@ struct i386_insn i386_frame_setup_skip_i /* `movl m32, %edx' */ { 6, { 0x89, 0x15 }, {0xff, 0xff } }, =20 + /* "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 } },=20 + /* "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 } },=20 + /* "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,=20 gdb_byte op; int skip =3D 0; =20 + /* This function returns a CORE_ADDR which is the instruction + following the last frame-setup instruction we saw such that "frame-se= tup + instruction" is one of push %ebp, push $0x0, mov %esp, %ebp,=20 + sub $, $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=20 + address of the instruction after one of those frame setup insns so th= at + i386_analyze_register_saves () can look for register saves that may e= xist + after them.=20=20 + (and you can have register saves without any frame setup, e.g. in a=20 + frameless function.)=20 + 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 =3D pc; + if (limit <=3D pc) return limit; =20 + + /* 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 =3D read_memory_unsigned_integer (pc, 1); + while (op !=3D 0x55 + && (op !=3D 0x6a ||=20 + read_memory_unsigned_integer (pc + skip + 1, 1) !=3D 0x0= 0) + && (op !=3D 0x31 || + read_memory_unsigned_integer (pc + skip + 1, 1) !=3D 0xe= d) + && pc + skip <=3D limit) + { + insn =3D i386_match_insn (pc + skip, i386_frame_setup_skip_insns); + if (insn) + { + skip +=3D insn->len; + op =3D read_memory_unsigned_integer (pc + skip, 1); + } + else + break; + } +=20=20 + if (limit <=3D pc + skip) + return end_of_frame_setup; =20 - if (op =3D=3D 0x55) /* pushl %ebp */ + /* Do we have our initial frame setup instruction? */ + if (op =3D=3D 0x55 || op =3D=3D 0x6a || op =3D=3D 0x31) { /* Take into account that we've executed the `pushl %ebp' that starts this instruction sequence. */ cache->saved_regs[I386_EBP_REGNUM] =3D 0; cache->sp_offset +=3D 4; - pc++; + + /* Skip over the second byte of `push $0x0' and `xor %ebp, %ebp' */ + if (op =3D=3D 0x6a || op =3D=3D 0x31) + skip +=3D 2; + else=20 + skip++; + end_of_frame_setup =3D pc + skip; =20 /* If that's all, return now. */ - if (limit <=3D pc) + if (limit <=3D pc + skip) return limit; =20 /* Check for some special instructions that might be migrated by @@ -623,7 +781,7 @@ i386_analyze_frame_setup (CORE_ADDR pc,=20 =20 /* If that's all, return now. */ if (limit <=3D pc + skip) - return limit; + return end_of_frame_setup; =20 op =3D read_memory_unsigned_integer (pc + skip, 1); =20 @@ -632,14 +790,14 @@ i386_analyze_frame_setup (CORE_ADDR pc,=20 { case 0x8b: if (read_memory_unsigned_integer (pc + skip + 1, 1) !=3D 0xec) - return pc; + return end_of_frame_setup; break; case 0x89: if (read_memory_unsigned_integer (pc + skip + 1, 1) !=3D 0xe5) - return pc; + return end_of_frame_setup; break; default: - return pc; + return end_of_frame_setup; } =20 /* OK, we actually have a frame. We just don't know how large @@ -648,6 +806,7 @@ i386_analyze_frame_setup (CORE_ADDR pc,=20 instructions mentioned before. */ cache->locals =3D 0; pc +=3D (skip + 2); + end_of_frame_setup =3D pc; =20 /* If that's all, return now. */ if (limit <=3D pc) @@ -695,7 +854,7 @@ i386_analyze_frame_setup (CORE_ADDR pc,=20 return pc + 4; } =20 - return pc; + return end_of_frame_setup; } =20 /* 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=20 + __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] =3D { 0xe8, 0, 0, 0, 0, /* call 0x0 */ @@ -844,6 +1008,95 @@ i386_skip_prologue (CORE_ADDR start_pc) return pc; } =20 +/* 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 =3D 0; + struct i386_insn *insn; + + if (pc =3D=3D current_pc) + return esp_change; +=20=20 + /* We're looking for PUSH r32, POP r32, SUB $x,ESP, ADD $x ESP. */ +=20 + while (pc < current_pc) + { + op =3D read_memory_unsigned_integer (pc, 1); + next_op =3D read_memory_unsigned_integer (pc + 1, 1); +=20=20=20=20=20=20 + /* `push %ebp'? We shouldn't see that in here; give up. */ + if (op =3D=3D 0x55) + return esp_change; + /* `ret'? We're at the end of the func; stop parsing. */ + if (op =3D=3D 0xc3) + return esp_change; + + /* 50+rd PUSH r32 */ + if ((op & 0xf8) =3D=3D 0x50) + { + esp_change -=3D 4; + pc +=3D 1; + continue; + } + /* 58+ rd POP r32 */ + if ((op & 0xf8) =3D=3D 0x58) + { + esp_change +=3D 4; + pc +=3D 1; + continue; + } + /* 83 /5 ib SUB r/m32, imm8 */ + if (op =3D=3D 0x83 && next_op =3D=3D 0xec) + { + uint8_t imm8 =3D read_memory_integer (pc + 2, 1); + esp_change -=3D imm8; + pc +=3D 3; + continue; + } + /* 81 /5 id SUB r/m32, imm32 */ + if (op =3D=3D 0x81 && next_op =3D=3D 0xec) + { + uint32_t imm32 =3D read_memory_integer (pc + 2, 4); + esp_change -=3D imm32; + pc +=3D 6; + continue; + } + /* 83 /0 ib ADD r/m32, imm8 */ + if (op =3D=3D 0x83 && next_op =3D=3D 0xc4) + { + uint8_t imm8 =3D read_memory_integer (pc + 2, 1); + esp_change +=3D imm8; + pc +=3D 3; + continue; + } + /* 81 /0 id ADD r/m32, imm8 */ + if (op =3D=3D 0x81 && next_op =3D=3D 0xc4) + { + uint32_t imm32 =3D read_memory_integer (pc + 2, 4); + esp_change +=3D imm32; + pc +=3D 6; + continue; + } + + insn =3D i386_match_insn (pc, i386_frame_setup_skip_insns); + if (insn) + pc +=3D insn->len; + else + return esp_change; /* Hit an instruction we don't know; stop here= . */ + } + return esp_change; +} + /* This function is 64-bit safe. */ =20 static CORE_ADDR @@ -856,7 +1109,30 @@ i386_unwind_pc (struct gdbarch *gdbarch, } =0C =20 -/* 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=20 + 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,=20 + 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. */ + =20 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 =3D 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 =3D=3D 0, there's no way the function we're + examining is frameless; it has a stack frame set up with=20 + the saved-EBP/saved-EIP at the standard locations.=20=20 + (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 =3D frame_relative_level (next_frame) =3D=3D -1=20 + || get_frame_type (next_frame) =3D=3D SIGTRAMP_FRAM= E; =20 if (*this_cache) return *this_cache; @@ -871,41 +1173,86 @@ i386_frame_cache (struct frame_info *nex cache =3D i386_alloc_frame_cache (); *this_cache =3D cache; =20 - /* 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] =3D 4; + + /* For normal frames, saved-%ebp is stored at 0(%ebp). */ + cache->saved_regs[I386_EBP_REGNUM] =3D 0; =20 frame_unwind_register (next_frame, I386_EBP_REGNUM, buf); cache->base =3D extract_unsigned_integer (buf, 4); if (cache->base =3D=3D 0) return cache; =20 - /* For normal frames, %eip is stored at 4(%ebp). */ - cache->saved_regs[I386_EIP_REGNUM] =3D 4; - cache->pc =3D frame_func_unwind (next_frame); + current_pc =3D 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 !=3D 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 instruction= s. + And our current function has the potential to be frameless.=20=20 + 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=20 + 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,=20 + or $pc is at 0x0 (someone jmp'ed thru NULL ptr). */ + if ((cache->pc !=3D 0 || current_pc =3D=3D 0) + /* The prologue parser didn't find any prologue instructions. */ + && prologue_parsed_to =3D=3D cache->pc + /* We have the potential to be frameless. */ + && potentially_frameless) + { + int esp_offset; + CORE_ADDR actual_frame_base; + esp_offset =3D i386_find_esp_adjustments (cache->pc, current_pc); + frame_unwind_register (next_frame, I386_ESP_REGNUM, buf); + actual_frame_base =3D extract_unsigned_integer (buf, 4) - esp_offset; + cache->base =3D extract_unsigned_integer (buf, 4); + cache->saved_sp =3D actual_frame_base + 4; + cache->saved_regs[I386_EBP_REGNUM] =3D -1; + cache->saved_regs[I386_EIP_REGNUM] =3D 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.=20=20=20 + For now we'll say "Debugging optimized code is an adventure!" + jmolenda/2005-04-27 */ =20 - if (cache->locals < 0) + for (i =3D 0; i < I386_NUM_SAVED_REGS; i++) + if (cache->saved_regs[i] !=3D -1) + cache->saved_regs[i] +=3D 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. */ =20 frame_unwind_register (next_frame, I386_ESP_REGNUM, buf); cache->base =3D extract_unsigned_integer (buf, 4) + cache->sp_offset; } =20 + /* 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 =3D=3D 0) + { + cache->saved_regs[I386_EIP_REGNUM] =3D 4; + cache->saved_regs[I386_EBP_REGNUM] =3D -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 =3D cache->base + 8; --Apple-Mail-6-316259868--