As a follow up to
this post, it would seem that the disassembler in
version 6.6.7.5 of
WinDbg appears to be broken when it comes to disassembling normal JITed code. I haven't noticed any other issues, but this one is a killer for me, so I've reverted to using
version 6.6.3.5, which does not suffer from disassembler bug.
Disassembling native or pre-JITed code also appears to work. The bug seems to be isolated to the disassembly of normal JITed code. So if you're doing much work in windbg with managed code that involves looking at the disassembled output of managed methods, you might consider reverting to version 6.6.3.5. If you need the new/updated features/fixes of version 6.6.7.5, be advised that the disassembly you see for normal JITed methods may be buggy.
Note that you'll see the same buggy output using the windbg u command as well as the sos extension's u command. That's because sos.u doesn't implement its own disassembler - it just defers to the active debugger's disassembler; providing CLR-aware annotations to the output. As a result, if you use the !sos.u command within the context of the Visual Studio 2005 debugger (which does not appear to have this disassembler bug), you'll see valid output.
Ironically, if you look at the
what's new page for version 6.6.7.5 of windbg, you'll find this line item:
Updated disassembler with new instruction support.
Thanks, but no thanks.
My test case is a simple C# console application that looks like so:
class Program {
static void Main() {
System.Console.WriteLine(GetText());
}
static string GetText() {
return ("Hello, buggy windbg!");
}
}
Using the !sos.bpmd extension command to break when Program.Main is called shows the following disassembly in version 6.6.3.5 of windbg (using !sos.u so that we can see the CLR-aware annotations):
0:000> !u eip
Normal JIT generated code
Program.Main()
Begin 01200070, size 23
01200070 56 push esi
01200071 833dc82da20000 cmp dword ptr [00a22dc8],0x0
01200078 7405 jz 0120007f
0120007a e87f22e978 call mscorwks!JIT_DbgIsJustMyCode (7a0922fe)
0120007f 90 nop
01200080 ff152c30a200 call dword ptr [00a2302c] (Program.GetText(), mdToken: 06000002)
01200086 8bf0 mov esi,eax
01200088 8bce mov ecx,esi
0120008a e855901b78 call mscorlib_ni+0x2f90e4 (793b90e4) (System.Console.WriteLine(System.String), mdToken: 06000764)
0120008f 90 nop
01200090 90 nop
01200091 5e pop esi
01200092 c3 ret
In particular, note that the disassembled code shown above for instruction address 0120008a. The raw instruction sequence at that address is e855901b78. As I described in
my previous post, the 1st byte (e8) is the Intel 'call' instruction, and the following 4 bytes (55901b78) indicate the target of the call instruction,
computed relative to the instruction pointer, and not counting the 5 byte instruction itself. So we can manually compute the target of the call instruction in windbg like so:
0:000> ? 0120008a + 5 + poi(0120008a + 1)
Evaluate expression: 2033946852 = 793b90e4
The above calculation shows that the target of the call instruction is address 793b90e4, which matches windbg's output:
...
0120008a e855901b78 call mscorlib_ni+0x2f90e4 (793b90e4) (System.Console.WriteLine(System.String), mdToken: 06000764)
...
Repeating the same experiment with version 6.6.7.5 of windbg (the buggy version) shows the following:
0:000> !u eip
Normal JIT generated code
Program.Main()
Begin 01200070, size 23
01200070 56 push esi
01200071 833dc82da20000 cmp dword ptr ds:[0A22DC8h],0
01200078 7405 je 0120007f
0120007a e87f22e978 call mscorwks!JIT_DbgIsJustMyCode (7a0922fe)
0120007f 90 nop
01200080 ff152c30a200 call dword ptr ds:[0A2302Ch]
01200086 8bf0 mov esi,eax
01200088 8bce mov ecx,esi
0120008a e855901b78 call MSVCR80!_NULL_IMPORT_DESCRIPTOR+0x2cdd (781b9055) (MSVCR80!_NULL_IMPORT_DESCRIPTOR)
0120008f 90 nop
01200090 90 nop
01200091 5e pop esi
01200092 c3 ret
There are a couple of things wrong with this output:
- the sos.u command doesn't realize that the 2nd call instruction goes to Program.GetText
- it incorrectly reports that the 3rd call instruction will land somewhere in MSVCR80.DLL (at address 781b9055).
Not likely.
Performing our same call decoding math for instruction 0120008a (the supposed call to address 781b9055 in MSVCR80.DLL) yields the following:
0:000> ? 0120008a + 5 + poi(0120008a + 1)
Evaluate expression: 2033946852 = 793b90e4
Note that our manual decoding of the call instruction yields a different target address: 793b90e4; as opposed to 781b9055 as shown by the !u command. If you disassemble that address, we'll see where execution will actually transfer:
0:000> !u eip
preJIT generated code
System.Console.WriteLine(System.String)
Begin 793b90e4, size 35
793b90e4 57 push edi
793b90e5 56 push esi
793b90e6 8bf9 mov edi,ecx
793b90e8 ba86000000 mov edx,86h
793b90ed b901000000 mov ecx,1
793b90f2 e8d1a9ab00 call mscorwks!JIT_Writeable_Thunks_Buf+0x198 (79e73ac8) (JitHelp: CORINFO_HELP_GETSHARED_GCSTATIC_BASE)
793b90f7 8bf0 mov esi,eax
793b90f9 837e6800 cmp dword ptr [esi+68h],0
793b90fd 750a jne mscorlib_ni+0x2f9109 (793b9109)
793b90ff b901000000 mov ecx,1
793b9104 e80347f9ff call mscorlib_ni+0x28d80c (7934d80c) (System.Console.InitializeStdOutError(Boolean), mdToken: 0600070f)
793b9109 8b4e68 mov ecx,dword ptr [esi+68h]
793b910c 8bd7 mov edx,edi
793b910e 8b01 mov eax,dword ptr [ecx]
793b9110 ff90d8000000 call dword ptr [eax+0D8h]
793b9116 5e pop esi
793b9117 5f pop edi
793b9118 c3 ret
And, in fact, that's exactly what the C# code we're looking at does at that address - call System.Console.WriteLine.
So be advised - the disassembly included in WinDbg version 6.6.7.5 is broken when it comes to decoding normal JITed code.
Posted
Jan 15 2007, 02:31 PM
by
mike-woodring