or
Stepping Through the JIT Thunk Layer
This article examines the JIT thunk layers that your code executes when a method is run for the first time, i.e., it needs just-in-time compilation or jitting, and is run with any subsequent invocations. I have included with this article a small C# WinForms application with names that whimsically recall Bilbo Baggin's adventure in the book, The Hobbit. The sample program is not a console application because we will want to step through the code a second time. For this exploration I am using my own user mode, native Win32 debugger, PEBrowse Interactive.
After you have built and compiled the sample program, start debugging it with PEBrowse Interactive by selecting File/Start Debugging. The program should stop executing and break with four child-windows that look something like the following:
Figure 1
My debugger stops on the first JITted method, or System.AppDomain::SetupDomain. Expand Wilderland.exe and the ".NET Methods" node and look for the Hobbiton_Button_Click method and set a breakpoint on the method by selecting View/Add Breakpoint. Then let the debugger continue until the application appears in all its glory.
Figure 2
Press the button labelled "Lonely Mountain" and PEBrowse Interactive will present a disassembly window containing the x86 and IL for the method, Wilderland.WilderlandForm::Hobbiton_Button_Click:
Disassembly of JITTED Wilderland.WilderlandForm::Hobbiton_Button_Click (06000006) at 0x071DF018 ; Stack Size (in BYTES): 24 (0x00000018) ; Number of Parameters: 1 ; Local Variables Size (in BYTES): 12 (0x0000000C) ; Prologue Size (in BYTES): 23 (0x17) ; Standard Frame > 0x71DF018: 55 PUSH EBP 0x71DF019: 8B EC MOV EBP,ESP 0x71DF01B: 83 EC 0C SUB ESP,0xC 0x71DF01E: 57 PUSH EDI 0x71DF01F: 56 PUSH ESI 0x71DF020: 68 80 7B ED 06 PUSH 0x6ED7B80 0x71DF025: E8 4A 25 E2 08 CALL 0x10001574 0x71DF02A: 89 55 F8 MOV DWORD PTR [EBP-0x8],EDX; VAR:0x8 0x71DF02D: 8B F9 MOV EDI,ECX ; end of prologue 0x71DF02F: 33 F6 XOR ESI,ESI ; IL_0000: ldc.i4 0x00009731 ; IL_0005: stloc.0 0x71DF031: BE 31 97 00 00 MOV ESI,0x9731 ; IL_0006: ldarg.0 ; IL_0007: ldloc.0 ; IL_0008: call Wilderland.WilderlandForm::LonelyMountain() 0x71DF036: 8B D6 MOV EDX,ESI 0x71DF038: 8B CF MOV ECX,EDI 0x71DF03A: FF 15 68 81 ED 06 CALL DWORD PTR [0x6ED8168] ;<==Note #1 ; IL_000D: ret 0x71DF040: 90 NOP 0x71DF041: EB 00 JMP 0x71DF043 0x71DF043: 68 80 7B ED 06 PUSH 0x6ED7B80 0x71DF048: E8 27 25 E2 08 CALL 0x10001574 0x71DF04D: 5E POP ESI 0x71DF04E: 5F POP EDI 0x71DF04F: 8B E5 MOV ESP,EBP 0x71DF051: 5D POP EBP 0x71DF052: C2 04 00 RET 0x4
The beginning of our journey through the JIT thunk layer will start at the call statement looking something like "CALL DWORD PTR [0x6ED8168]" (Note #1), so single step by pressing the F10 key until the debugger is positioned on this statement. Note that along the way the value 0x9731, called the TheOneRing in the source code, is moved into the ESI and then the EDX registers. Before continuing select the option Tools/Configure/Memory and change the Default Alignment to DWord. Examine the destination of the call by pressing F4 and entering the address in the call statement:
+0x06ED8168 06ED7B6B ..{k
Step into this call statement by pressing F11.
Disassembly of THUNK at 0x06ED7B6B > 0x6ED7B6B: E8 A0 2C 27 F9 CALL 0x14A810
| | 1st page | next page |