When debugging, you may find that the stack trace falls apart:
ChildEBP RetAddr
001af118 773806a0 ntdll!KiFastSystemCallRet
001af11c 7735b18c ntdll!ZwWaitForSingleObject+0xc
001af180 7735b071 ntdll!RtlpWaitOnCriticalSection+0x154
001af1a8 2f6db1a9 ntdll!RtlEnterCriticalSection+0x152
001af1b4 2fe8d533 ABC!CCriticalSection::Lock+0x12
001af1d0 2fe8d56a ABC!CMessageList::Lock+0x24
001af234 2f6e47ac ABC!CMessageWindow::UpdateMessageList+0x231
001af274 2f6f040e ABC!CMessageWindow::UpdateContents+0x84
001af28c 2f6e4474 ABC!CMessageWindow::Refresh+0x1a8
001af360 2f6e4359 ABC!CMessageWindow::OnChar+0x4c
001af384 761a1a10 ABC!CMessageWindow::WndProc+0xb31
00000000 00000000 USER32!GetMessageW+0x6e
This can't possible be the complete stack. I mean, where's the thread procedure? That should be at the start of the stack for any thread.
What happened is that the EBP chain got broken, and the debugger can't walk the stack any further. If the code was compiled with frame pointer optimization (FPO), then the compiler will not create EBP frames, permitting it to use EBP as a general purpose register instead. This is great for optimization, but it causes trouble for the debugger when it tries to take a stack trace through code compiled with FPO for which it does not have the necessary information to decode these types of stacks.
Begin digression: Traditionally, every function began with the sequence
push ebp ;; save caller's EBP
mov ebp, esp ;; set our EBP to point to this "frame"
sub esp, n ;; reserve space for local variables
and ended with
mov esp, ebp ;; discard local variables
pop ebp ;; recover caller's EBP
ret n
This pattern is so common that the x86 has dedicated instructions for it. The ENTER n,0 instruction does the push / mov / sub, and the LEAVE instruction does the mov / pop. (In C/C++, the value after the comma is always zero.)
Read more: The old new thing
0 comments:
Post a Comment