C# Emit によって生成された動的コードをデバッグするにはどうすればよいですか?

まず最初に、これは非常に深いテーマであり、実際に遭遇した友人でもあることを宣言しておきますが、DynamicMethod + ILGenerator を使用して多くの動的メソッドを生成しますが、この動的メソッドでは時々オーバーフロー例外が頻繁に発生します。動的メソッド本体をデバッグする方法ですが、Visual Studio を使用してデバッグすると、個人的には難しいと思います。現時点では、windbg しか使用できません。次に、具体的なデバッグ手順について説明します。

1. テストコード

説明の便宜上、前回のテストコードです。

class Program
    {
        private delegate int AddDelegate(int a, int b);

        static void Main(string[] args)
        {
            var dynamicAdd = new DynamicMethod("Add", typeof(int), new[] { typeof(int), typeof(int) }, true);
            var il = dynamicAdd.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Add);
            il.Emit(OpCodes.Ret);

            var addDelegate = (AddDelegate)dynamicAdd.CreateDelegate(typeof(AddDelegate));

            Console.WriteLine(addDelegate(10, 20));
        }
    }

これは動的に生成された Add(int a,int b) メソッドです。そのメソッド本体をデバッグするにはどうすればよいでしょうか? ここには 2 つのトリックがあります。

最初: Debugger.Break(); を使用します。このステートメントは、プロセスに接続されているデバッガー、つまり Windbg に割り込みを通知できます。

2 番目: Marshal.GetFunctionPointerForDelegate を使用して、デリゲート メソッドの関数ポインター アドレスを取得します。

上記の 2 点を踏まえて、コードを次のように変更します。

static void Main(string[] args)
        {
            var dynamicAdd = new DynamicMethod("Add", typeof(int), new[] { typeof(int), typeof(int) }, true);
            var il = dynamicAdd.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Add);
            il.Emit(OpCodes.Ret);

            var addDelegate = (AddDelegate)dynamicAdd.CreateDelegate(typeof(AddDelegate));
            Console.WriteLine("Function Pointer: 0x{0:x16}", Marshal.GetFunctionPointerForDelegate(addDelegate).ToInt64());

            Debugger.Break();

            Console.WriteLine(addDelegate(10, 20));
        }

次に、windbg を使用して exe プログラムを起動すると、次のようにコンソールに出力が表示されます。

写真

2. コードヒープでメソッド本体のバイトコードを見つける

次に、関数ポインター 0x00000000023d062e を逆コンパイルします。

0:000> !U 0x00000000023d062e
Unmanaged code
023d062e b818063d02      mov     eax,23D0618h
023d0633 e9e4c934fe      jmp     0071d01c
023d0638 ab              stos    dword ptr es:[edi]
023d0639 ab              stos    dword ptr es:[edi]
023d063a ab              stos    dword ptr es:[edi]
023d063b ab              stos    dword ptr es:[edi]
023d063c ab              stos    dword ptr es:[edi]
023d063d ab              stos    dword ptr es:[edi]
023d063e ab              stos    dword ptr es:[edi]
023d063f ab              stos    dword ptr es:[edi]

上記の 23D0618h は最終的な実際の動的メソッド ポインター アドレスであり、dp を使用してポインターの値を確認します。

0:000> dp 23D0618h L1
023d0618  00a90050

次に、00a90050 アドレスを逆コンパイルして、メソッド本体のアセンブリ コードを確認します。

0:000> !U 00a90050
Normal JIT generated code
DynamicClass.Add(Int32, Int32)
Begin 00a90050, size 5
>>> 00a90050 8bc1            mov     eax,ecx
00a90052 03c2            add     eax,edx
00a90054 c3              ret

次に、2 つのパスがあります。

  • おなじみのモード

アンマネージ コマンド bp 00a90050 を使用して、デバッグ用のブレークポイントを直接設定します。

  • ハードモード

マネージド コマンド !bpmd xxx を使用して、メソッド記述子の下のブレークポイント デバッグを見つけます。

ここでは難しいモードを選択して対処します。

3. bpmd を使用してブレークポイントを設定します

!bpmd を使用してブレークポイントを設定するには、メソッド記述子が必要です。codeaddr ができたので、逆に記述子を見つけるにはどうすればよいでしょうか? !mln はここから入手できます。

0:000> !mln 00a90050
Method instance: (BEGIN=00a90050)(MD=0071537c disassemble)[DynamicClass.Add(Int32, Int32)]

上記の MD=0071537c 出力はメソッド記述子のアドレスであり、!bpmd -md 0071537c を使用してブレークポイントを設定できます。

0:000> !bpmd -md 0071537c
MethodDesc = 0071537c
Setting breakpoint: bp 00A90050 [DynamicClass.Add(Int32, Int32)]
0:000> g
Breakpoint 0 hit
eax=02505fe8 ebx=0019f5ac ecx=0000000a edx=00000014 esi=0250230c edi=0019f4fc
eip=00a90050 esp=0019f488 ebp=0019f508 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
00a90050 8bc1            mov     eax,ecx

出力から、ブレークポイントが正常にヒットし、clr が自動的に bp 00A90050 に転送されました。次に、ヒットしたブレークポイントの図を見てください。

写真

上記 2 つのアセンブリ命令は、a+b の結果、つまり、a が ecx に配置され、b が edx に配置されます。信じられない場合は、2 回ステップを実行することもできます。

0:000> t
eax=0000000a ebx=0019f5ac ecx=0000000a edx=00000014 esi=0250230c edi=0019f4fc
eip=00a90052 esp=0019f488 ebp=0019f508 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
00a90052 03c2            add     eax,edx
0:000> t
eax=0000001e ebx=0019f5ac ecx=0000000a edx=00000014 esi=0250230c edi=0019f4fc
eip=00a90054 esp=0019f488 ebp=0019f508 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
00a90054 c3              ret

ここでは ecx=0000000a edx=00000014 です。

おすすめ

転載: blog.csdn.net/wangonik_l/article/details/132693573