Getting an exception call stack from the catch block (C++)
This is my first post in this category so I am really looking forward to your comments :-) Today I would like to cover a specific feature related to our implementation of C++ exception handling. Every day I realize that not many people are familiar with this feature. When they learn about it they really get surprised :-). You need to see their reaction, it is just awesome! So now let’s see yours :-).
Have you ever had a day when you are starring at catch block and willing you knew the stack of original exception? Do you remember how frustrated you were :-)? Well it turns out that the original stack is there under your fingertips. The debugger just hides it . Did I get you going J?
MS implementation of C++ exception handling doesn't actually pop the stack until the flow control leaves catch block. From C++ standard "A throw-expression initializes a temporary object, the type of which is determined by removing any top-level cv-qualifiers from the static type..." It means thatCRT needs to a make copy of an object somewhere when exception is thrown. As you can guess, it makes a copy of it on the stack! It means that by default run time can't pop the stack until exception is dismissed. This feels good J
Here is what actually happens on x86 platform when exception gets thrown. This is oversimplified view but you will get an idea:
- Copy of the object is created on the stack
- Appropriate handler, catch block, is found
- Runtime calls object destructors located on the stack
- EBP is adjusted to a frame containing the catch block
- ESP is NOT TOUCHED
- Flow of control transferred to catch block
- When done with a catch block the flow of control returns back to the place where it came before entering catch block
- ESP is adjusted
- Flow of control is transferred outside of catch block
Amazing right! Now since EBP gets adjusted before getting to the catch block debugger doesn’t show the real stack. It shows you stack as you would expect: without exception on the stack!
Lets take a look at the example:
int __cdecl main (int argc, char* rgArg [])
{
BYTE byte = 10;
try
{
throw (10);
}
catch (int exception)
{
BYTE* pStack;
__asm
{
mov pStack, esp;
}
printf ("Stack difference %d\n",&byte - pStack);
}
// To see real flow control we need to put some code
// here
//
printf (“I am almost done\n”);
return 0;
}
Output:
Stack difference 1595
Wow! Now let’s take a look at the stack. I put a break point in the debugger on the first printf statement:
000aff84 01001469 00000001 002d5cc0 002d3b30 test!main+0x44
000affc0 77e4f38c 00000000 00000000 7ffdf000 test!mainCRTStartup+0xb0
000afff0 00000000 010013b9 00000000 78746341 kernel32!BaseProcessStart+0x23
This is what you would expect right? Now let’s take a look at registers
0:000> r
eax=01001331 ebx=01001331 ecx=00000100 edx=00000002 esi=000afbc8 edi=000aff78
eip=01001334 esp=000af938 ebp=000aff84 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216
test!main+0x44:
01001334 8d4def lea ecx,[ebp-0x11] ss:0023:000aff73=0aff580a
Please notice a big difference between ebp and esp! The real question now is how to get the real stack. The simple way is to make a search for 0001003f pattern. As many of you know this is usually how the first four bytes of CONTEXT look like on x86 platform
0:000> s -d @esp @ebp 0001003f
000afbe8 0001003f 00000000 00000000 00000000 ?...............
All we have to do know is to switch context:
0:000> .cxr 000afbe8
eax=000afeb8 ebx=7ffdf000 ecx=00000000 edx=002d3b30 esi=000aff48 edi=000aff48
eip=77e649d3 esp=000afeb4 ebp=000aff08 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
kernel32!RaiseException+0x51:
77e649d3 5e pop esi
000aff08 7c3929f8 e06d7363 00000001 00000003 kernel32!RaiseException+0x51
000aff48 01001331 000aff68 010018d4 00000000 MSVCR80!_CxxThrowException+0x34
000aff84 01001469 00000001 002d5cc0 002d3b30 test!main+0x41
000affc0 77e4f38c 00000000 00000000 7ffdf000 test!mainCRTStartup+0xb0
000afff0 00000000 010013b9 00000000 78746341 kernel32!BaseProcessStart+0x23
And here you go J. Also we could have used dps command to find a previous frame.
0:000> dps @esp
000af938 7c392a47 MSVCR80!_NLG_Return
000af93c 000aff78
000af940 000afbc8
000af944 000af954
000af948 000aff78
000af94c 01001331 test!main+0x41
000af950 000aff84
000af954 000af980
000af958 7c3929b5 MSVCR80!_CallCatchBlock2+0x4a
000af95c 01001331
000af960 000aff78
000af964 00000100
000af968 000af9d4
Now all we need to do is reset ebp to 000af954
0:000> r ebp=000af954
000af954 7c3929b5 01001331 000aff78 00000100 test!main+0x44
000af980 7c391bc4 000aff78 01001918 01001331 MSVCR80!_CallCatchBlock2+0x4a
000af9e4 7c392081 000afbc8 000aff78 00000001 MSVCR80!CallCatchBlock+0x84
000afa14 7c3923dd 000afbc8 000afbe8 000afba0 MSVCR80!CatchIt+0x5c
000afa70 7c3925c6 000afbc8 000aff78 000afbe8 MSVCR80!FindHandler+0x26e
000afaa4 7c392680 000afbc8 000aff78 MSVCR80!__InternalCxxFrameHandler+0xc5
000afae0 77f68cf6 000afbc8 000aff78 000afbe8 MSVCR80!__CxxFrameHandler3+0x26
000afae0 77f68cf6 000afbc8 000aff78 000afbe8 ntdll!ExecuteHandler2+0x26
000afb04 77f68cc5 000afbc8 000aff78 000afbe8 ntdll!ExecuteHandler2+0x26
000afbb0 77f45275 00000000 000afbe8 000afbc8 ntdll!ExecuteHandler+0x24
000afbb0 77e649d3 00000000 000afbe8 ntdll!KiUserExceptionDispatcher+0xe
000aff08 7c3929f8 e06d7363 00000001 00000003 kernel32!RaiseException+0x51
000aff48 01001331 000aff68 010018d4 00000000 MSVCR80!_CxxThrowException+0x34
000aff84 01001469 00000001 002d5cc0 002d3b30 test!main+0x41
000affc0 77e4f38c 00000000 00000000 7ffdf000 test!mainCRTStartup+0xb0
000afff0 00000000 010013b9 00000000 78746341 kernel32!BaseProcessStart+0x23
This stack dump is even better. It gives you full stack right before we executed jump to catch block!
The consequence of this behavior is that you need to be very careful when re-throwing exceptions from the catch block in recursive or deep functions - you might hit stack overflow! We actually did and not only once J.
As many people say you live you learn :-)
Enjoy the rest of the day!
Comments
Anonymous
January 30, 2005
How did you find the 000af954 value? Is it because it's the next slot on the stack after what looks like the address that the catch block will return to?
000af954 000af980
000af958 7c3929b5 MSVCR80!_CallCatchBlock2+0x4a
By the way, there's more information about .cxr and the magical 0x1003f and 0x1001f numbers in the Mike Stall's blog:
http://blogs.msdn.com/jmstall/archive/2005/01/18/355697.aspxAnonymous
January 30, 2005
I am also interested for the value 000af954. I wrote the sample code and debugged locally also, the pattern is different. Could you explain a little more?Anonymous
January 31, 2005
Yes. It appeared to be the first valid frame. So you can do two things here one is to set ebp to it and see if it works or the other way is run kb=000af954
>>By the way, there's more information about .cxr and the magical 0x1003f and 0x1001f numbers in the Mike Stall's blog:
>>http://blogs.msdn.com/jmstall/archive/2005/01/18/355697.aspx Remove Comment 363443
That is a good point. I saw the post couple of days ago and that is exact reason why I didn't go into details of the magic :-). I forgot to put a pointer. Thank you very much for reminder!Anonymous
January 31, 2005
>> I am also interested for the value 000af954. I wrote the sample code and debugged locally also, the pattern is different. Could you explain a little more?
Yes in you case it will be different. You need to be looking at your thread stack. If you run dps @esp as I did above the first column is thread's stack. The second column is what is stored on the stack. Usually a stack frame contains both address to previous frame and return address. All I did is looked for the pattern that seems to be a valid frame and then tried it. Let me know if you want me to dig more information out for you about how frames getting set and how to traverse them by hand.Anonymous
January 31, 2005
Interesting finds todayAnonymous
February 28, 2005
A call of a function pushes the return value on the stack first, followed by ebp.
When looking at dps/dds output you can identify a likely frame start by looking for a pointer that points to an address just a bit down the stack that could be a valid pushed ebp, followed by what looks like a valid return address. This matches that pattern:
000af954 000af980
000af958 7c3929b5 MSVCR80!_CallCatchBlock2+0x4a
You can pass the address of the likely ebp to the k command (kv=000af954) to start a stack walk at that point. This is also a useful trick to recover the second half of a stack that is truncated due to an FPO frame in a module that you don't have symbols for.Anonymous
February 21, 2006
Very useful blog. Thank you.Anonymous
May 10, 2006
Anyone knows the AMD64 counterpart of the magical 0001003f?Anonymous
October 11, 2007
Good post on how to see the stack when an exception is thrown on Windows at http://blogs.msdn.com/slavao/archive/2005/01/30/363428.aspAnonymous
January 05, 2008
PingBack from http://boxing.247blogging.info/?p=3202Anonymous
April 21, 2009
PingBack from http://jmdesp.free.fr/billeterie/?p=168Anonymous
June 02, 2009
PingBack from http://woodtvstand.info/story.php?id=89954Anonymous
October 20, 2009
Very useful article. It would be better if could get the stack trace within the catch block.