[lldb-dev] Problem unwinding from inside of a CRT function

Jason Molenda jmolenda at apple.com
Thu Jan 15 16:24:58 PST 2015


I doubt the ABI very different regarding CreateDefaultUnwindPlan() and CreateFunctionEntryUnwindPlan() -- they describe a very minimal set of how to backtrace without any knowledge.

You'll also need to implement the RegisterIsVolatile() method based on the Windows ABI.  This tells lldb which registers are non-volatile aka preserved.  For the SysV ABI, that says that a function can call another function and the contents of ebx will not be modified -- ebx is non-volatile/preserved.

"step over" relies heavily on the unwinder.  When you say step over/next, lldb instruction steps through a line in your function.  Each time it instruction-steps, it looks to see that it is still in the same function and still within that source line address range.  If it is in a new function, it needs to determine:  Did I call a function?  Or did I return out of my original function during the step?  To tell what happened, it backtraces one frame to see if the caller is the function it originally started in.

So you need to be able to unwind reliably from the first instruction of a function.  Which, without any symbols or unwind info, is going to be pretty difficult to get right.

J


> On Jan 15, 2015, at 4:18 PM, Zachary Turner <zturner at google.com> wrote:
> 
> Yuck :(  Sounds like it's going to be a lot of work to get this working then.  We're working with the Microsoft ABI, not the Itanium ABI, so I'm assuming that's why ABISysV_x86_64::CreateFunctionEntryUnwindPlan isn't doing anything for me.  Presumably I need to implement an ABIMicrosoft_x86_x64 plugin.  
> 
> Which is unfortunate, because it seems to be needed even for basic stepping to work, like step over.  Originally I was just trying to implement stepping, and that's how I ran into this issue.  So that brings me to a related question.  Why is step over as complicated as it is?  It seems to me like step over can be implemented by disassembling 1 opcode, adding the size of the opcode to the current pc, and having the ThreadPlan::ShouldStop always return false unless the pc is equal to old_pc + size_of_opcode.
> 
> It currently has a lot of logic for comparing frames against each other and things like that though.
> 
> On Thu Jan 15 2015 at 4:06:23 PM Jason Molenda <jmolenda at apple.com> wrote:
> lldb is stopped on the first instruction of a function (at address 0x12350a1).  But there's no symbol for this function -- lldb doesn't KNOW it's at the first instruction of a function.  It can't profile the assembly instructions of the function to figure out how the unwind instructions should work.  So lldb falls back to the "architecture default unwind plan", e.g. ABISysV_x86_64::CreateDefaultUnwindPlan(), which assumes that the way to find the caller's eip address is to dereference ebp.
> 
> Basically, you need some source of unwind info for functions.  On Linux systems, this would be eh_frame instructions.  On Mac OS X, there's "compact unwind" info that does the same thing.  I'm sure Windows has something -- it's needed to do exception handling.
> 
> If you don't have any source of unwind information, you need to have accurate function start addresses so lldb can look at the instruction stream and make up an unwind plan from those (v. UnwindAssembly-x86.cpp).  But this means you need to know the start address of all functions in the file.  On Mac OS X we have a special little section (LC_FUNCTION_STARTS) that encodes the length of each function in the file--so even if the function names are stripped before shipping, we can find the start address of each function easily.  lldb adds these to the symbol table and makes up function names for them.
> 
> If you have no compiler-generated unwind information (that lldb can parse) and you don't have start addresses for all functions in the file, things are going to work poorly.
> 
> 
> For what it's worth, when looking at unwind issues it's usually easiest to turn on the unwind logging.  "log enable lldb unwind".  That'll show what lldb was up to.
> 
> Also, another ABI method is useful - see ABISysV_x86_64::CreateFunctionEntryUnwindPlan().  This is the UnwindPlan that lldb will use when it knows it is at the start of a function before any instructions have been executed.
> 
> J
> 
> 
> > On Jan 15, 2015, at 3:56 PM, Zachary Turner <zturner at google.com> wrote:
> >
> > Having some trouble unwinding when I'm broken inside of a CRT function.  Another caveat is that I don't have symbols for this CRT function.  So the problem could be anything from something I've done wrong on my side, to an issue when symbols aren't present, to something else.  Here is the source code of this program:
> >
> > #include <stdio.h>
> >
> > int main (void)
> > {
> >   printf("This is line 1\n");
> >   printf("This is line 2\n");
> >   printf("This is line 3\n");
> >   return 1;
> > }
> >
> > Here is the disassembly of main:
> >
> > (lldb) disassemble -n main -F intel
> >    0x1235040 <main>: push   ebp
> >    0x1235041 <main+1>: mov    ebp, esp
> >    0x1235043 <main+3>: sub    esp, 0x14
> >    0x1235046 <main+6>: lea    eax, [0x1230040]
> >    0x123504c <main+12>: mov    dword ptr [ebp - 0x4], 0x0
> >    0x1235053 <main+19>: mov    dword ptr [esp], eax
> >    0x1235056 <main+22>: call   0x12350a1
> >    0x123505b <main+27>: lea    ecx, [0x1230050]
> >    (snipped for brevity)
> >
> > (Using the argument to "call" as the breakpoint address)
> > (lldb) break set -a 0x12350a1
> > Breakpoint 3: address = 0x012350a1
> > (lldb) run
> > Process 17044 launching
> > (lldb) Process 17044 launched: 'd:\testexe\expr_test.exe' (i386)
> > (lldb) Process 17044 stopped
> > * thread #1: tid = 0x40ec, 0x012350a1 expr_test.exe, stop reason = breakpoint 3.1
> >     frame #0: 0x012350a1 expr_test.exe
> > -> 0x12350a1: pushl  $0xc
> >    0x12350a3: pushl  $0x1241000
> >    0x12350a8: calll  0x1235be0
> >    0x12350ad: xorl   %edi, %edi
> > (lldb) disassemble -b -F intel
> > -> 0x12350a1: 6a 0c           push   0xc
> >    0x12350a3: 68 00 10 24 01  push   0x1241000
> >    0x12350a8: e8 33 0b 00 00  call   0x1235be0
> >    0x12350ad: 33 ff           xor    edi, edi
> >    0x12350af: 89 7d e4        mov    dword ptr [ebp - 0x1c], edi
> >    0x12350b2: 33 c0           xor    eax, eax
> >    0x12350b4: 39 45 08        cmp    dword ptr [ebp + 0x8], eax
> >    0x12350b7: 0f 95 c0        setne  al
> >    0x12350ba: 85 c0           test   eax, eax
> >    0x12350bc: 75 15           jne    0x12350d3
> >
> > Here's my register values:
> > (lldb) register read
> > General Purpose Registers:
> >        eax = 0x01230040
> >        ebx = 0x00000000
> >        ecx = 0x00000001
> >        edx = 0x00000000
> >        edi = 0x00000000
> >        esi = 0x00000000
> >        ebp = 0x00EAF920
> >        esp = 0x00EAF908
> >        eip = 0x012350A1
> >     eflags = 0b00000000000000000000001000010110
> >
> > And using the value of esp to dump the stack (sorry, I don't know how to use the -f argument to format this more nicely),
> >
> > (lldb) memory read 0x00EAF908
> > 0x00eaf908: 5b 50 23 01 40 00 23 01 00 00 00 00 00 00 00 00  [P#. at .#.........
> > 0x00eaf918: 28 f9 ea 00 00 00 00 00 68 f9 ea 00 4e 52 23 01  (.......h...NR#.
> >
> > So the return address is 0x0123505b.  Cross-referencing this with the original disassembly of main(), it looks like this is the correct value.
> >
> > So it seems like the Unwinder has all the information it needs, but yet I'm still only getting 1 frame.  Any suggestions how to dig into this?
> 





More information about the lldb-dev mailing list