Есть ли способ просмотреть DynamicMethod результирующий код сборки x86? - PullRequest
1 голос
/ 08 мая 2019

Я строю DynamicMethod на лету, вставляя коды операций с помощью ILGenerator.Я использую плагин Visual Studio для просмотра кода IL в DynamicMethod, так что это не проблема.

Однако я бы хотел увидеть окончательный код x86, выпущенный JITer.Visual Studio 2017 не позволит мне войти в код сборки x86, что бы я ни пытался.Он отображается как «легкая функция» в стеке, и VS просто перешагнет через него.

Есть ли способ увидеть код сборки x86, созданный путем компиляции DynamicMethod?

1 Ответ

3 голосов
/ 13 мая 2019

Кажется, я не могу найти способ сделать это из Visual Studio (по крайней мере VS2017).Поэтому вам, возможно, повезет больше с использованием WinDbg (доступно как часть Windows SDK).

Чтобы упростить задачу, я предлагаю заставить ваше приложение выводить некоторые полезные данные, которые помогут найти код в памяти с помощью WinDbg.В частности, если вы можете вывести результат вызова Marshal.GetFunctionPointerForDelegate() для делегата, созданного из вашего динамического метода, это очень приблизит вас к коду метода.Для этого вам нужно будет использовать неуниверсальный делегат, поэтому, например, если вы создаете делегат Func<...> из своего динамического метода, вам необходимо временно заменить его чем-то неуниверсальным.

Какпример:

private delegate int AddDelegate(int a, int b);

public static void DynamicMethodTest()
{
    // Create a DynamicMethod that adds its two int parameters
    // Passing "true" as the final (restrictedSkipVisibility) parameter causes the method to be JITted immediately when you call .CreateDelegate()
    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);

    // Use the non-generic AddDelegate defined above, rather than a generic one like Func<int, int, int> so that Marshal.GetFunctionPointerForDelegate() works
    var addDelegate = (AddDelegate)dynamicAdd.CreateDelegate(typeof(AddDelegate));
    Console.WriteLine("Function Pointer: 0x{0:x16}", Marshal.GetFunctionPointerForDelegate(addDelegate).ToInt64());

    Debugger.Break();
}

Если мы вызываем этот метод, когда приложение работает под WinDbg, мы должны получить что-то вроде следующего вывода в окне консоли, прежде чем автоматически взломать отладчик:

Function Pointer: 0x0000000012345678

Отсюда было несколько шагов до просмотра кода динамического метода.

Сначала используйте команду u (unassemble) на выводе указателя функции выше:

0:000> u 0x0000000012345678 L1

00000000`12345678 49ba2143658700000000 mov r10,87654321h

Здесь первая инструкция загружает адрес указателя на действительный код динамического метода в r10, поэтому мы используем команду dp (Показать память - указатель), чтобы получить цель указателя:

0:000> dp 0x87654321 L1
00000000`87654321  000007fe`9abcdef0

Запустите u (дизассемблировать) по этому адресу или введите адрес в окне «Разборка» («Просмотр» -> «Разборка»), чтобы получить код динамического метода:

0:000> u 000007fe`9abcdef0

000007fe`9abcdef0 8d0411          lea     eax,[rcx+rdx]
000007fe`9abcdef3 c3              ret
...

По умолчанию команда unassemble выводит 8 инструкций, вы можете добавить спецификатор длины к команде, чтобы изменить это (например, добавление «L20» приведет к выводу 32 (0x20) инструкций) - вы сами должны определить полноеЭкстент функции.

В качестве альтернативы может оказаться проще использовать расширение отладки .NET для WinDbg для выполнения последнего шага дампинга кода динамического метода, и в этом случае вам сначала нужно загрузить расширение (толькотребуется один раз на сеанс отладки), используя .loadby sos clr, затем вместо кода используйте !u на кодовом адресе:

0:000> !u 000007fe`9abcdef0

Normal JIT generated code
DynamicClass.Add(Int32, Int32)
Begin 000007fe9abcdef0, size 4
>>> 000007fe`9abcdef0 8d0411          lea     eax,[rcx+rdx]
000007fe`9abcdef3 c3              ret

Все приведенные выше примеры работают в 64-битном режиме, но в 32-битном режимеМетод практически идентичен.

...