次の方法で共有


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.