[[925]]で紹介した"ADND本"だが、Chapter4のサンプル、04CodeGenがおかしな挙動を示す。 System.Reflection.Emitを使って、DynamicMethodとILGeneratorとで実行時にCLRのコードを出力しメソッドを作り、JITコンパイルを経て実際に呼んでみるサンプル。どんなコードを出力するかというと、int引数を二つ取り、足し算した結果を返すだけのシンプルなコードだ。 04CodeGen.cs: #pre||> using System; using System.Text; using System.Reflection.Emit; namespace Advanced.NET.Debugging.Chapter4 { class CodeGen { private delegate int Add(int a, int b); public static void Main() { Type[] args={typeof(int), typeof(int)}; DynamicMethod dyn = new DynamicMethod("Add", typeof(int), new Type[] { typeof(int), typeof(int) }, typeof(CodeGen), true); ILGenerator gen = dyn.GetILGenerator(); gen.Emit(OpCodes.Ldarg_1); gen.Emit(OpCodes.Ldarg_2); gen.Emit(OpCodes.Add); gen.Emit(OpCodes.Ret); Add a= (Add) dyn.CreateDelegate(typeof(Add)); Console.WriteLine("Press any key to invoke Add method"); Console.ReadKey(); int ret = a(1, 2); Console.WriteLine("1+2={0}", ret); } } } ||< ・・・が。 > 04CodeGen.exe Press any key to invoke Add method 1+2=4 ( ゚д゚)ポカーン (つд⊂)ゴシゴシ ( ゚Д゚)・・・はぁぁああ!!??? デバッグしてみる。環境はWin7(32bit)でCLR2.0(.NET 2.0, 3.0, 3.5) + CLR4.0両方入り。 #more|| #pre||> Microsoft (R) Windows Debugger Version 6.12.0002.633 X86 Copyright (c) Microsoft Corporation. All rights reserved. CommandLine: 04CodeGen.exe Symbol search path is: ... Executable search path is: ModLoad: 00e40000 00e48000 04CodeGen.exe ModLoad: 77ac0000 77bfd000 ntdll.dll ModLoad: 71ed0000 71f1a000 C:\Windows\SYSTEM32\MSCOREE.DLL ModLoad: 76440000 76514000 C:\Windows\system32\KERNEL32.dll ModLoad: 75d50000 75d9a000 C:\Windows\system32\KERNELBASE.dll (1ca8.284): Break instruction exception - code 80000003 (first chance) eax=00000000 ebx=00000000 ecx=0028f3d8 edx=77b06344 esi=fffffffe edi=00000000 eip=77b5ebbe esp=0028f3f4 ebp=0028f420 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!LdrpDoDebuggerBreak+0x2c: 77b5ebbe cc int 3 0:000> g ModLoad: 761c0000 76260000 C:\Windows\system32\ADVAPI32.dll ... ModLoad: 6c5a0000 6c5fb000 C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorjit.dll ||< "Press any key to invoke Add method"が表示されたところでCTRL-BREAKでDebuggerに戻す。 #pre||> (1ca8.1c34): Break instruction exception - code 80000003 (first chance) eax=7ffdb000 ebx=00000000 ecx=00000000 edx=77b5d7eb esi=00000000 edi=00000000 eip=77af3370 esp=03c8ff44 ebp=03c8ff70 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!DbgBreakPoint: 77af3370 cc int 3 0:003> .loadby sos mscorwks 0:003> .load sosex 0:003> bp mscorjit!CILJit::compileMethod 0:003> g Breakpoint 0 hit eax=6c5f7268 ebx=0028e7f8 ecx=6c5e514c edx=00000011 esi=003763d0 edi=00000000 eip=6c5a5dbb esp=0028e63c ebp=0028e6a4 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206 mscorjit!CILJit::compileMethod: 6c5a5dbb 55 push ebp 0:000> kb 3 ChildEBP RetAddr Args to Child 0028e638 6967cacc 6c5f7268 0028e7f8 0028e770 mscorjit!CILJit::compileMethod 0028e6a4 6967cb65 00391f18 0028e7f8 0028e770 mscorwks!invokeCompileMethodHelper+0x72 0028e6e8 6967cbd8 00391f18 0028e7f8 0028e770 mscorwks!invokeCompileMethod+0x31 0:000> dd 0028e770 0028e770 00153208 001535f9 00392978 00000004 ^^^^^^^^ これを"!dumpmd"する ... 0:000> !dumpmd 00153208 Method Name: DynamicClass.Add(Int32, Int32) Class: 00153164 MethodTable: 001531c8 mdToken: 06000000 Module: 00152c5c IsJitted: no CodeAddr: ffffffff 0:000> !dumpil 00153208 This is dynamic IL. Exception info is not reported at this time. If a token is unresolved, run "!do " on the addr given in parenthesis. You can also look at the token table yourself, by running "!DumpArray 01a5755c". IL_0000: ldarg.1 IL_0001: ldarg.2 IL_0002: add IL_0003: ret ||< IL自体も書籍と実際のソースの通りなんだけど。 #pre||> 0:000> !bpmd -md 00153208 MethodDesc = 00153208 This DynamicMethodDesc is not yet JITTED. Placing memory breakpoint at 00153228 0:000> g Unable to insert breakpoint 2 at 0018ce80, Win32 error 0n299 "ReadProcessMemory 要求または WriteProcessMemory 要求の一部だけを完了しました。" The breakpoint was set with BP. If you want breakpoints to track module load/unload state you must use BU. bp2 at 0018ce80 failed WaitForEvent failed eax=00000000 ebx=002e00a8 ecx=00153228 edx=0018ce80 esi=00000000 edi=00000008 eip=695319c4 esp=0028eadc ebp=0028eae8 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 mscorwks!CompareExchangeMP+0x8: 695319c4 c20400 ret 4 ||< メッセージが気になるけど、とりあえず止まってくれたのでもう一度"!dumpmd"してJITされたコードを逆アセンブルしてみる。 #pre||> 0:002> !dumpmd 00153208 Method Name: DynamicClass.Add(Int32, Int32) Class: 00153164 MethodTable: 001531c8 mdToken: 06000000 Module: 00152c5c IsJitted: yes CodeAddr: 002e00a8 ^^^^^^^^ これを"!U"してみる。 0:002> !U 002e00a8 Normal JIT generated code DynamicClass.Add(Int32, Int32) Begin 002e00a8, size 24 >>> 002e00a8 55 push ebp 002e00a9 8bec mov ebp,esp 002e00ab 83ec08 sub esp,8 002e00ae 894dfc mov dword ptr [ebp-4],ecx 002e00b1 8955f8 mov dword ptr [ebp-8],edx 002e00b4 833d142e150000 cmp dword ptr ds:[152E14h],0 002e00bb 7405 je 002e00c2 002e00bd e867b24a69 call mscorwks!JIT_DbgIsJustMyCode (6978b329) 002e00c2 8b45f8 mov eax,dword ptr [ebp-8] ^^^^^^^^ ここにbpしてみる。 002e00c5 030424 add eax,dword ptr [esp] 002e00c8 8be5 mov esp,ebp 002e00ca 5d pop ebp 002e00cb c3 ret 0:002> bl 0 e 6c5a5dbb 0001 (0001) 0:**** mscorjit!CILJit::compileMethod 2 e 0018ce80 0001 (0001) 0:**** 0:002> bc 0 0:002> bc 2 0:002> bl 0:002> bp 002e00c2 0:002> g Breakpoint 0 hit eax=00153208 ebx=0028ed2c ecx=00000001 edx=00000002 esi=003763d0 edi=00000000 eip=002e00c2 esp=0028ec60 ebp=0028ec68 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 002e00c2 8b45f8 mov eax,dword ptr [ebp-8] ss:0023:0028ec60=00000002 0:000> p eax=00000002 ebx=0028ed2c ecx=00000001 edx=00000002 esi=003763d0 edi=00000000 eip=002e00c5 esp=0028ec60 ebp=0028ec68 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 002e00c5 030424 add eax,dword ptr [esp] ss:0023:0028ec60=00000002 0:000> p eax=00000004 ebx=0028ed2c ecx=00000001 edx=00000002 esi=003763d0 edi=00000000 eip=002e00c8 esp=0028ec60 ebp=0028ec68 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 002e00c8 8be5 mov esp,ebp ||< ・・・この時点でEAX=4となっている。 詳しいデバッグログは省くが、このルーチンに入った時点ではECX = 第一引数, EDX=第二引数がセットされている。つまり ECX = 1, EDX = 2 となっている。 sub esp,8 mov dword ptr [ebp-4],ecx mov dword ptr [ebp-8],edx で、この時点でECX, EDX がそれぞれEBPからの相対位置を使ってコピーされる。 なので、 mov eax,dword ptr [ebp-8] この次の add eax,dword ptr [esp] は、本来は add eax,dword ptr [ebp-4] じゃないとおかしくないか? Visual C# 2008 Express Edition (.NET Framework 3.5)でコンパイルしてみる: #pre||> > csc 04CodeGen.cs Microsoft (R) Visual C# 2008 Compiler version 3.5.30729.4926 for Microsoft (R) .NET Framework version 3.5 Copyright (C) Microsoft Corporation. All rights reserved. > 04CodeGen.exe Press any key to invoke Add method 1+2=71 ||< ( д ) ゚ ゚ Visual C# 2010 Express Edition (CLR 4)でコンパイルしてみる: #pre||> > csc 04CodeGen.cs Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1 Copyright (C) Microsoft Corporation. All rights reserved. > 04CodeGen.exe Press any key to invoke Add method 1+2=75 ||< ( ) д ゚ ゚ 謎。とりあえずこの機能、使わないでおこう・・・。