Нужна помощь в расшифровке строки кода на ассемблере, из .ЧИСТЫЙ JITted code
-
13-09-2019 - |
Вопрос
В конструкторе C # это заканчивается вызовом this(...)
, фактический вызов переводится в этот:
0000003d call dword ptr ds:[199B88E8h]
Каково здесь содержимое регистра DS?Я знаю, что это сегмент данных, но выполняется ли этот вызов через VMT-таблицу или что-то подобное?Хотя я сомневаюсь в этом, поскольку this(...)
это был бы не вызов виртуального метода, а просто другой конструктор.
Я спрашиваю, потому что значение в этом местоположении кажется каким-то неправильным, если я нажму F11, trace into (Visual Studio 2008), по этой инструкции вызова программа завершит работу с нарушением доступа.
Код находится глубоко внутри библиотеки управления третьей стороны, где, хотя у меня есть исходный код, у меня нет сборок, скомпилированных с достаточным количеством отладочной информации, чтобы я мог отслеживать ее через код C #, только через дизассемблер, а затем я должен сопоставить это с фактическим кодом.
Код C #, о котором идет речь, выглядит следующим образом:
public AxisRangeData(AxisRange range) : this(range, range.Axis) {
}
Отражатель показывает мне этот IL-код:
.maxstack 8
L_0000: ldarg.0
L_0001: ldarg.1
L_0002: ldarg.1
L_0003: callvirt instance class DevExpress.XtraCharts.AxisBase DevExpress.XtraCharts.AxisRange::get_Axis()
L_0008: call instance void DevExpress.XtraCharts.Native.AxisRangeData::.ctor(class DevExpress.XtraCharts.ChartElement, class DevExpress.XtraCharts.AxisBase)
L_000d: ret
Это тот последний вызов там, к другому конструктору того же класса, который завершается неудачей.Отладчик никогда не появляется внутри другого метода, он просто выходит из строя.
Разборка метода после JITting заключается в следующем:
00000000 push ebp
00000001 mov ebp,esp
00000003 sub esp,14h
00000006 mov dword ptr [ebp-4],ecx
00000009 mov dword ptr [ebp-8],edx
0000000c cmp dword ptr ds:[18890E24h],0
00000013 je 0000001A
00000015 call 61843511
0000001a mov eax,dword ptr [ebp-4]
0000001d mov dword ptr [ebp-0Ch],eax
00000020 mov eax,dword ptr [ebp-8]
00000023 mov dword ptr [ebp-10h],eax
00000026 mov ecx,dword ptr [ebp-8]
00000029 cmp dword ptr [ecx],ecx
0000002b call dword ptr ds:[1889D0DCh] // range.Axis
00000031 mov dword ptr [ebp-14h],eax
00000034 push dword ptr [ebp-14h]
00000037 mov edx,dword ptr [ebp-10h]
0000003a mov ecx,dword ptr [ebp-0Ch]
0000003d call dword ptr ds:[199B88E8h] // this(range, range.Axis)?
00000043 nop
00000044 mov esp,ebp
00000046 pop ebp
00000047 ret
По сути, я спрашиваю вот о чем:
- Какова цель
ds:[ADDR]
косвенность здесь?VMT-таблица предназначена только для виртуальных, не так ли?и это конструктор - Может ли конструктор еще быть JITted, что может означать, что вызов на самом деле будет выполняться через JIT-прокладку?Боюсь, я нахожусь здесь по уши в воде, так что все может помочь.
Редактировать:Что ж, проблема только усугубилась, или улучшилась, или что-то в этом роде.
Мы разрабатываем .Функция NET в проекте C # в решении Visual Studio 2008, а также отладку и разработку с помощью Visual Studio.
Однако в конечном итоге этот код будет загружен в среду выполнения .NET, размещенную в приложении Win32 Delphi.
Чтобы упростить экспериментирование с такими функциями, мы также можем настроить Visual Studio project / solution / debugger на копирование созданных DLL-файлов в каталог приложения Delphi, а затем запустить приложение Delphi через отладчик Visual Studio.
Оказывается, проблема исчезает, если я запускаю программу вне отладчика, но во время отладки она всплывает каждый раз.
Не уверен, что это поможет, но поскольку производственный выпуск кода запланирован только через 6 месяцев или около того, то это снимает с него часть нагрузки перед тестовым выпуском, который у нас скоро будет.
Я углублюсь в воспоминания позже, но, вероятно, не раньше выходных, и опубликую продолжение.
Решение
Сегмент данных - это то место, куда компиляторы обычно помещают глобальные переменные и где находится таблица адресов импорта.
00000029 cmp dword ptr [ecx],ecx
0000002b call dword ptr ds:[1889D0DCh]
Первая строка на самом деле является проверкой на нуль, которая в конечном итоге вызывает NullReferenceException
если указатель, расположенный в регистре ECX, недействителен.
В callvirt
Инструкция MSIL должна выполнить проверку на нуль перед вызовом фактического метода.При этом мы можем с уверенностью предположить, что эти две строки ассемблерного кода имеют следующее представление кода MSIL:
class DevExpress.XtraCharts.AxisBase DevExpress.XtraCharts.AxisRange::get_Axis()
И прокомментированный ассемблерный код:
00000026 mov ecx,dword ptr [ebp-8] // store the pointer to the 'range' in ECX
00000029 cmp dword ptr [ecx],ecx // null-check
0000002b call dword ptr ds:[1889D0DCh] // range.get_Axis()
00000031 mov dword ptr [ebp-14h],eax // store the result in a local variable
00000034 push dword ptr [ebp-14h] // push the result onto a stack
00000037 mov edx,dword ptr [ebp-10h] // this variable was previously loaded with the 'range' pointer
0000003a mov ecx,dword ptr [ebp-0Ch] // here seems to be stored the actual 'this' pointer
0000003d call dword ptr ds:[199B88E8h] // call the this(...) ctor
Мне непонятно, почему происходит сбой, вы пробовали просматривать содержимое ячейки памяти (DS:[199B88E8h]
)?