.NET JIT コードからアセンブラ コードの行を解読するのに助けが必要です
-
13-09-2019 - |
質問
C# コンストラクターでは、最終的には次の呼び出しになります。 this(...)
, 、実際の呼び出しは次のように変換されます。
0000003d call dword ptr ds:[199B88E8h]
ここのDSレジスタの内容は何でしょうか?データ セグメントであることはわかっていますが、この呼び出しは VMT テーブルなどを介して行われているのでしょうか?私はそれを疑いますが、 this(...)
これは仮想メソッドの呼び出しではなく、単なる別のコンストラクターです。
その場所の値が何らかの形で間違っているようなので質問しました。F11 キーを押して (Visual Studio 2008) をトレースすると、その呼び出し命令でプログラムがアクセス違反でクラッシュします。
コードはサードパーティのコントロール ライブラリの奥深くにあり、ソース コードは持っていますが、C# コードを通じてトレースできるほど十分なデバッグ情報を含めてコンパイルされたアセンブリはなく、逆アセンブラを通じてのみ追跡できます。それを実際のコードと照合します。
問題の C# コードは次のとおりです。
public AxisRangeData(AxisRange range) : this(range, range.Axis) {
}
Reflector は次の 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-tableは仮想専用ですよね?そしてこれはコンストラクターです - コンストラクターがまだ JIT されていない可能性があります。これは、呼び出しが実際に JIT シムを介して呼び出される可能性があることを意味する可能性があります。残念ながらここは深いところにいるので、何か役に立つかもしれませんし、役立つかもしれません。
編集:まあ、問題は悪化したのか、良くなったのか、あるいはその他のことです。
私たちは、Visual Studio 2008 ソリューションの C# プロジェクトで .NET 機能を開発し、Visual Studio を通じてデバッグと開発を行っています。
ただし、最終的には、このコードは Win32 Delphi アプリケーションによってホストされる .NET ランタイムに読み込まれます。
このような機能の簡単な実験を容易にするために、生成された DLL を Delphi アプリのディレクトリにコピーし、Visual Studio デバッガーを通じて Delphi アプリを実行するように Visual Studio プロジェクト/ソリューション/デバッガーを構成することもできます。
結局のところ、デバッガの外部でプログラムを実行すると問題は解決しますが、デバッグ中に毎回問題が発生します。
それが役立つかどうかはわかりませんが、コードが製品版リリースされる予定はあと 6 か月ほどあるため、近日中にリリースされるテスト リリースに向けてのプレッシャーがいくらか軽減されます。
思い出の部分については後で詳しく説明しますが、おそらく週末までは続かないので、フォローアップを投稿します。
解決
データセグメントがある。
00000029 cmp dword ptr [ecx],ecx
0000002b call dword ptr ds:[1889D0DCh]
最初の行は、実際にECXレジスタに位置ポインタが無効な場合、最終的にNullReferenceException
を上げるヌルチェック、である。
callvirt
MSIL命令は、実際のメソッドを呼び出す前にnullのチェックを行う必要があります。それは言われて、私たちは安全にアセンブリコードの2行は、次の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]
)の内容を見て試してみましたが、私には不明ですか?