Where is my exception handler code in the function disassembly?
This post discusses how compiler actually stores the exception filters and exception handler in the stack required for exception handling mechanism provided by OS. Have a look at the disassembly of following main function and observe that on issuing uf main in windbg, the output doesn't show the code under __except block. So where are my exception filter function and excpetion handler.
int main()
{
int i=5;
__try
{
i = 5/(i-i);
}
__except(FilterFunction2(1))
{
printf("__Except block\n");
}
return 1;
}
0:000> uf main
DivideByZero!main [c:\program files\microsoft visual studio 10.0\vc\homework\dividebyzero.cpp @ 13]:
13 01061040 55 push ebp
13 01061041 8bec mov ebp,esp
13 01061043 6afe push 0FFFFFFFEh
13 01061045 6830af0701 push offset DivideByZero!__rtc_tzz+0x104 (0107af30)
13 0106104a 68a0120601 push offset DivideByZero!_except_handler4 (010612a0)
13 0106104f 64a100000000 mov eax,dword ptr fs:[00000000h]
13 01061055 50 push eax
13 01061056 83c4f4 add esp,0FFFFFFF4h
13 01061059 53 push ebx
13 0106105a 56 push esi
13 0106105b 57 push edi
13 0106105c a100c00701 mov eax,dword ptr [DivideByZero!__security_cookie (0107c000)]
13 01061061 3145f8 xor dword ptr [ebp-8],eax
13 01061064 33c5 xor eax,ebp
13 01061066 50 push eax
13 01061067 8d45f0 lea eax,[ebp-10h]
13 0106106a 64a300000000 mov dword ptr fs:[00000000h],eax
13 01061070 8965e8 mov dword ptr [ebp-18h],esp
14 01061073 c745e405000000 mov dword ptr [ebp-1Ch],5
15 0106107a c745fc00000000 mov dword ptr [ebp-4],0
17 01061081 8b4de4 mov ecx,dword ptr [ebp-1Ch]
17 01061084 2b4de4 sub ecx,dword ptr [ebp-1Ch]
17 01061087 b805000000 mov eax,5
17 0106108c 99 cdq
17 0106108d f7f9 idiv eax,ecx
17 0106108f 8945e4 mov dword ptr [ebp-1Ch],eax
18 01061092 c745fcfeffffff mov dword ptr [ebp-4],0FFFFFFFEh
18 01061099 eb22 jmp DivideByZero!main+0x7d (010610bd)
DivideByZero!main+0x7d [c:\program files\microsoft visual studio 10.0\vc\homework\dividebyzero.cpp @ 24]:
24 010610bd b801000000 mov eax,1
25 010610c2 8b4df0 mov ecx,dword ptr [ebp-10h]
25 010610c5 64890d00000000 mov dword ptr fs:[0],ecx
25 010610cc 59 pop ecx
25 010610cd 5f pop edi
25 010610ce 5e pop esi
25 010610cf 5b pop ebx
25 010610d0 8be5 mov esp,ebp
25 010610d2 5d pop ebp
25 010610d3 c3 ret
This code is compiled in VC, some of the below details might be different for different compilers.
Durig compilation if a function has exception handling blocks( __try and __except) in it, then compiler pushes an Extended Exception Registration Record to stack. One important thing here is that, for one function there will be only Extended Exception Resgistration Record pushed onto the stack, irrespective of number of __try block in the functions. Lets see what this record is:
0:000:x86> dt PEH4_EXCEPTION_REGISTRATION_RECORD -r1
DivideByZero!PEH4_EXCEPTION_REGISTRATION_RECORD
Ptr32 +0x000 SavedESP : Ptr32 Void
+0x004 ExceptionPointers : Ptr32 _EXCEPTION_POINTERS
+0x000 ExceptionRecord : Ptr32 _EXCEPTION_RECORD
+0x004 ContextRecord : Ptr32 _CONTEXT
+0x008 SubRecord : _EXCEPTION_REGISTRATION_RECORD
+0x000 Next : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : Ptr32 _EXCEPTION_DISPOSITION
+0x010 EncodedScopeTable : Uint4B
+0x014 TryLevel : Uint4B
TryLevel is used as an index to the ScopeTable array to fetch out the right __except block( since there may be more than one nested try blocks). If you see from the above disassembly TryLevel is at ebp-4, hence [ebp-4] gets updated every time the compiler sees that a new try block is entered/exited. Now lets see where this record is pushed in the stack.
Below is the snip from start of main function:
13 01061040 55 push ebp
13 01061041 8bec mov ebp,esp
//This is try level, this value would be changed when a try block is entered.
13 01061043 6afe push 0FFFFFFFEh
//This is the scope table we would talk about this in a moment.
13 01061045 6830af0701 push offset DivideByZero!__rtc_tzz+0x104 (0107af30)
//This is common windows handler function called on exceptions. This in turn call the exception FilterFunction defined for the function.
13 0106104a 68a0120601 push offset DivideByZero!_except_handler4 (010612a0)
//This is Next Field of SubRecord struct shown above
13 0106104f 64a100000000 mov eax,dword ptr fs:[00000000h]
13 01061055 50 push eax
// Here would come other two members(SavedEsp and Exception POinters) of the record.
13 01061056 83c4f4 add esp,0FFFFFFF4h
For any exception raised, _except_handler4 function is invoked by OS and is passed the Exception Registration Record as a paremeter. This function subtracts an offset 8 (offset of SubRecord field) to get the Extended Exception Registration record that was pushed on to stack.
In Extended exception registration record, Scope table contains the actual Exception Filter Function and exception handler. Now lets take a look at the Scope Table:
0:000> dt _EH4_SCOPETABLE
DivideByZero!_EH4_SCOPETABLE
+0x000 GSCookieOffset : Uint4B
+0x004 GSCookieXOROffset : Uint4B
+0x008 EHCookieOffset : Uint4B
+0x00c EHCookieXOROffset : Uint4B
+0x010 ScopeRecord : [1] _EH4_SCOPETABLE_RECORD
The ScopeRecord field is an array of _SCOPETABLE_RECORD which contains the Exception Filter Function and Exception Handler for each try block. Here TryLevel (discussed above) is used as an index to get the right Filter function and handler for corresponding _try block. ScopeRecord is at offset 0x10 of this structure. The scope table is at 0x0107af30, lets dump out the Scope record. Since for above main function TryLevel is 0, the corresponding excpetion Filter function and handler would be at index 0.
0:000> dd 0x0107af30+10 l4
0107af40 fffffffe 0106109b 010610a6 00000000
0:000> dt _EH4_SCOPETABLE_RECORD
DivideByZero!_EH4_SCOPETABLE_RECORD
+0x000 EnclosingLevel : Uint4B
+0x004 FilterFunc : Ptr32 long
+0x008 u : <unnamed-tag>
FilterFunc is at offset 4 let's see what filter function is:
0:000> uf 0106109b
DivideByZero!main+0x5b [c:\program files\microsoft visual studio 10.0\vc\homework\dividebyzero.cpp @ 19]:
19 0106109b 6a01 push 1
19 0106109d e863ffffff call DivideByZero!ILT+0(?FilterFunction2YAHHZ) (01061005)
19 010610a2 83c404 add esp,4
19 010610a5 c3 ret
As you can compare with the code in the very begining, this is the assembly for Exception Filter Function(i.e. the code inside the __except()).
Now lets see where is our Exception handler Code(i.e the code insie __except {}). _EH4_SCOPETABLE_RECORD.u contains the address of exception handler block. Lets dump this:
0:000> u 010610a6
DivideByZero!main+0x66 [c:\program files\microsoft visual studio 10.0\vc\homework\dividebyzero.cpp @ 19]:
010610a6 8b65e8 mov esp,dword ptr [ebp-18h]
010610a9 68d87e0701 push offset DivideByZero!std::_Iosb<int>::end+0x4 (01077ed8)
010610ae e84e000000 call DivideByZero!printf (01061101)
010610b3 83c404 add esp,4
010610b6 c745fcfeffffff mov dword ptr [ebp-4],0FFFFFFFEh
010610bd b801000000 mov eax,1
010610c2 8b4df0 mov ecx,dword ptr [ebp-10h]
010610c5 64890d00000000 mov dword ptr fs:[0],ecx
0:000> u
DivideByZero!main+0x8c [c:\program files\microsoft visual studio 10.0\vc\homework\dividebyzero.cpp @ 25]:
010610cc 59 pop ecx
010610cd 5f pop edi
010610ce 5e pop esi
010610cf 5b pop ebx
010610d0 8be5 mov esp,ebp
010610d2 5d pop ebp
Again if you compare, 010610a6 is the start address of code lines inside __except {} block.
Comments
Anonymous
December 29, 2011
It helps understand what happens under the hood. Is the processing for X64 code different? I observed some differences when I tried the similar steps for x64 function assembly.Anonymous
January 04, 2012
Yes for X64, protocol is different form X86.