[解決済み】型のイニシャライザーを見つけると、なぜNullReferenceExceptionがスローされるのでしょうか?
質問
これには困っています。私はNoda Timeのためにいくつかのテストを最適化しようとしていたのですが、そこではいくつかの型のイニシャライザーがチェックされています。私は、型があるかどうかを見つけると思いました。
があります。
に読み込む前に、型イニシャライザ(静的コンストラクタまたはイニシャライザ付き静的変数)を使用する必要があります。
AppDomain
. 驚いたことに、この小さなテストでは
NullReferenceException
- にNULL値がないにもかかわらず
私の
というコードがあります。それは
だけ
は、デバッグ情報なしでコンパイルすると、例外を投げます。
以下は、この問題を実証するための短いながらも完全なプログラムです。
using System;
class Test
{
static Test() {}
static void Main()
{
var cctor = typeof(Test).TypeInitializer;
Console.WriteLine("Got initializer? {0}", cctor != null);
}
}
そして、コンパイルと出力の記録。
c:\Users\Jon\Test>csc Test.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17626
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.
c:\Users\Jon\Test>test
Unhandled Exception: System.NullReferenceException: Object reference not set to
an instance of an object.
at System.RuntimeType.GetConstructorImpl(BindingFlags bindingAttr, Binder bin
der, CallingConventions callConvention, Type[] types, ParameterModifier[] modifi
ers)
at Test.Main()
c:\Users\Jon\Test>csc /debug+ Test.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17626
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.
c:\Users\Jon\Test>test
Got initializer? True
ここで、私が.NET 4.5 (リリース候補) を使っていることにお気づきでしょう。 かもしれない ということです。他の様々なオリジナルのフレームワーク(特に "vanilla" .NET 4)でテストするのは少々厄介ですが、他のフレームワークのマシンに簡単にアクセスできる人がいれば、その結果に興味があるところです。
その他詳細
- 私はx64マシンを使用していますが、この問題はx86とx64の両方のアセンブリで発生します。
-
のデバッグ性"です。
呼び出し
上記のテストケースでは、それ自身のアセンブリでテストしていますが、Noda Timeに対してこれを試したところ、再コンパイルする必要はありませんでした。
NodaTime.dll
のみで、その違いを確認することができます。Test.cs
を参照した。 - Mono 2.10.8 で "broken" アセンブリを実行する。 はしません。 投げる
何か思い当たることはありますか?フレームワークのバグ?
EDIT: 不思議なことに、ますます不思議です。もし、あなたが
Console.WriteLine
を呼び出します。
using System;
class Test
{
static Test() {}
static void Main()
{
var cctor = typeof(Test).TypeInitializer;
}
}
それは今
だけ
でコンパイルした場合、失敗します。
csc /o- /debug-
. 最適化をオンにすると、(
/o+
) が動作します。しかし、もしあなたが
Console.WriteLine
の呼び出しは、オリジナルと同じように、両方のバージョンで失敗します。
解決方法は?
と
csc test.cs
:
(196c.1874): Access violation - code c0000005 (first chance)
mscorlib_ni!System.RuntimeType.GetConstructorImpl(System.Reflection.BindingFlags, System.Reflection.Binder, System.Reflection.CallingConventions, System.Type[], System.Reflection.ParameterModifier[])+0xa3:
000007fe`e5735403 488b4608 mov rax,qword ptr [rsi+8] ds:00000000`00000008=????????????????
から読み込もうとする
[rsi+8]
いつ
@rsi
はNULLです。この関数を検証してみましょう。
0:000> ln 000007fe`e5735403
(000007fe`e5735360) mscorlib_ni!System.RuntimeType.GetConstructorImpl(System.Reflection.BindingFlags, System.Reflection.Binder, System.Reflection.CallingConventions, System.Type[], System.Reflection.ParameterModifier[])+0xa3
0:000> uf 000007fe`e5735360
Flow analysis was incomplete, some code may be missing
mscorlib_ni!System.RuntimeType.GetConstructorImpl(System.Reflection.BindingFlags, System.Reflection.Binder, System.Reflection.CallingConventions, System.Type[], System.Reflection.ParameterModifier[]):
000007fe`e5735360 53 push rbx
000007fe`e5735361 55 push rbp
000007fe`e5735362 56 push rsi
000007fe`e5735363 57 push rdi
000007fe`e5735364 4154 push r12
000007fe`e5735366 4883ec30 sub rsp,30h
000007fe`e573536a 498bf8 mov rdi,r8
000007fe`e573536d 8bea mov ebp,edx
000007fe`e573536f 48c744242800000000 mov qword ptr [rsp+28h],0
000007fe`e5735378 488bb42480000000 mov rsi,qword ptr [rsp+80h]
000007fe`e5735380 4889742420 mov qword ptr [rsp+20h],rsi
000007fe`e5735385 41b903000000 mov r9d,3
...
mscorlib_ni!System.RuntimeType.GetConstructorImpl(System.Reflection.BindingFlags, System.Reflection.Binder, System.Reflection.CallingConventions, System.Type[], System.Reflection.ParameterModifier[])+0x97:
000007fe`e57353f7 488b4b08 mov rcx,qword ptr [rbx+8]
000007fe`e57353fb 85c9 test ecx,ecx
000007fe`e57353fd 0f848e000000 je mscorlib_ni!System.RuntimeType.GetConstructorImpl(System.Reflection.BindingFlags, System.Reflection.Binder, System.Reflection.CallingConventions, System.Type[], System.Reflection.ParameterModifier[])+0x131 (000007fe`e5735491)
mscorlib_ni!System.RuntimeType.GetConstructorImpl(System.Reflection.BindingFlags, System.Reflection.Binder, System.Reflection.CallingConventions, System.Type[], System.Reflection.ParameterModifier[])+0xa3:
000007fe`e5735403 488b4608 mov rax,qword ptr [rsi+8]
000007fe`e5735407 85c0 test eax,eax
000007fe`e5735409 7545 jne mscorlib_ni!System.RuntimeType.GetConstructorImpl(System.Reflection.BindingFlags, System.Reflection.Binder, System.Reflection.CallingConventions, System.Type[], System.Reflection.ParameterModifier[])+0xf0 (000007fe`e5735450)
...
@rsi
から読み込まれます。
[rsp+20h]
であるため、呼び出し元から渡される必要がある。呼び出し側を見てみましょう。
0:000> k3
Child-SP RetAddr Call Site
00000000`001fec70 000007fe`8d450110 mscorlib_ni!System.RuntimeType.GetConstructorImpl(System.Reflection.BindingFlags, System.Reflection.Binder, System.Reflection.CallingConventions, System.Type[], System.Reflection.ParameterModifier[])+0xa3
00000000`001fecd0 000007fe`ecb6e073 image00000000_01120000!Test.Main()+0x60
00000000`001fed20 000007fe`ecb6dcb2 clr!CoUninitializeEE+0x7ae1f
0:000> ln 000007fe`8d450110
(000007fe`8d4500b0) image00000000_01120000!Test.Main()+0x60
0:000> uf 000007fe`8d4500b0
image00000000_01120000!Test.Main():
000007fe`8d4500b0 53 push rbx
000007fe`8d4500b1 4883ec40 sub rsp,40h
000007fe`8d4500b5 e8a69ba658 call mscorlib_ni!System.Console.get_In() (000007fe`e5eb9c60)
000007fe`8d4500ba 4c8bd8 mov r11,rax
000007fe`8d4500bd 498b03 mov rax,qword ptr [r11]
000007fe`8d4500c0 488b5048 mov rdx,qword ptr [rax+48h]
000007fe`8d4500c4 498bcb mov rcx,r11
000007fe`8d4500c7 ff5238 call qword ptr [rdx+38h]
000007fe`8d4500ca 488d0d7737eeff lea rcx,[000007fe`8d333848]
000007fe`8d4500d1 e88acb715f call clr!CoUninitializeEE+0x79a0c (000007fe`ecb6cc60)
000007fe`8d4500d6 4c8bd8 mov r11,rax
000007fe`8d4500d9 48b92012531200000000 mov rcx,12531220h
000007fe`8d4500e3 488b09 mov rcx,qword ptr [rcx]
000007fe`8d4500e6 498b03 mov rax,qword ptr [r11]
000007fe`8d4500e9 4c8b5068 mov r10,qword ptr [rax+68h]
000007fe`8d4500ed 48c744242800000000 mov qword ptr [rsp+28h],0
000007fe`8d4500f6 48894c2420 mov qword ptr [rsp+20h],rcx
000007fe`8d4500fb 41b903000000 mov r9d,3
000007fe`8d450101 4533c0 xor r8d,r8d
000007fe`8d450104 ba38000000 mov edx,38h
000007fe`8d450109 498bcb mov rcx,r11
000007fe`8d45010c 41ff5228 call qword ptr [r10+28h]
000007fe`8d450110 48bb1032531200000000 mov rbx,12533210h
000007fe`8d45011a 488b1b mov rbx,qword ptr [rbx]
000007fe`8d45011d 33d2 xor edx,edx
000007fe`8d45011f 488bc8 mov rcx,rax
000007fe`8d450122 e829452e58 call mscorlib_ni!System.Reflection.ConstructorInfo.op_Equality(System.Reflection.ConstructorInfo, System.Reflection.ConstructorInfo) (000007fe`e5734650)
000007fe`8d450127 0fb6c8 movzx ecx,al
000007fe`8d45012a 33c0 xor eax,eax
000007fe`8d45012c 85c9 test ecx,ecx
000007fe`8d45012e 0f94c0 sete al
000007fe`8d450131 0fb6c8 movzx ecx,al
000007fe`8d450134 894c2430 mov dword ptr [rsp+30h],ecx
000007fe`8d450138 488d542430 lea rdx,[rsp+30h]
000007fe`8d45013d 488d0d24224958 lea rcx,[mscorlib_ni+0x682368 (000007fe`e58e2368)]
000007fe`8d450144 e807246a5f call clr+0x2550 (000007fe`ecaf2550)
000007fe`8d450149 488bd0 mov rdx,rax
000007fe`8d45014c 488bcb mov rcx,rbx
000007fe`8d45014f e81cab2758 call mscorlib_ni!System.Console.WriteLine(System.String, System.Object) (000007fe`e56cac70)
000007fe`8d450154 90 nop
000007fe`8d450155 4883c440 add rsp,40h
000007fe`8d450159 5b pop rbx
000007fe`8d45015a c3 ret
(私のディスアセンブルでは
System.Console.get_In
を追加したからです。
Console.GetLine()
デバッガでブレークする機会を持つために、test.csに追加しました。動作が変わらないことを確認しました。)
この呼び出しに
000007fe8d45010c 41ff5228 call qword ptr [r10+28h]
(私たちのAVフレームretアドレスは、この直後の命令です。
call
).
これをコンパイルしたときの結果と比較してみましょう。
csc /debug test.cs
. を設定することができます。
bp 000007fee5735360
幸いにも、モジュールは同じアドレスでロードされます。をロードする命令で
@rsi
:
0:000> r
rax=000007fee58e2f30 rbx=00000000027c6258 rcx=00000000027c6258
rdx=0000000000000038 rsi=00000000002debd8 rdi=0000000000000000
rip=000007fee5735378 rsp=00000000002de990 rbp=0000000000000038
r8=0000000000000000 r9=0000000000000003 r10=000007fee58831c8
r11=00000000002de9c0 r12=0000000000000000 r13=00000000002dedc0
r14=00000000002dec58 r15=0000000000000004
iopl=0 nv up ei pl nz na po nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
mscorlib_ni!System.RuntimeType.GetConstructorImpl(System.Reflection.BindingFlags, System.Reflection.Binder, System.Reflection.CallingConventions, System.Type[], System.Reflection.ParameterModifier[])+0x18:
000007fe`e5735378 488bb42480000000 mov rsi,qword ptr [rsp+80h] ss:00000000`002dea10=a0627c0200000000
なお
@rsi
は00000000002debd8です。この関数をステップ実行すると、このアドレスは後で悪いexeが爆発する場所でデリファレンスされることがわかります(つまり。
@rsi
は変化しない)。スタックは非常に興味深いものです
というのは、余分なフレームが表示されるからです。
:
0:000> k3
Child-SP RetAddr Call Site
00000000`002de990 000007fe`e5eddf68 mscorlib_ni!System.RuntimeType.GetConstructorImpl(System.Reflection.BindingFlags, System.Reflection.Binder, System.Reflection.CallingConventions, System.Type[], System.Reflection.ParameterModifier[])+0x18
00000000`002de9f0 000007fe`8d460119 mscorlib_ni!System.Type.get_TypeInitializer()+0x48
00000000`002dea30 000007fe`ecb6e073 good!Test.Main()+0x49*** WARNING: Unable to verify checksum for good.exe
0:000> ln 000007fe`e5eddf68
(000007fe`e5eddf20) mscorlib_ni!System.Type.get_TypeInitializer()+0x48
0:000> uf 000007fe`e5eddf20
mscorlib_ni!System.Type.get_TypeInitializer():
000007fe`e5eddf20 53 push rbx
000007fe`e5eddf21 4883ec30 sub rsp,30h
000007fe`e5eddf25 488bd9 mov rbx,rcx
000007fe`e5eddf28 ba22010000 mov edx,122h
000007fe`e5eddf2d b901000000 mov ecx,1
000007fe`e5eddf32 e8d1a075ff call CORINFO_HELP_GETSHARED_GCSTATIC_BASE (000007fe`e5638008)
000007fe`e5eddf37 488b88f0010000 mov rcx,qword ptr [rax+1F0h]
000007fe`e5eddf3e 488b03 mov rax,qword ptr [rbx]
000007fe`e5eddf41 4c8b5068 mov r10,qword ptr [rax+68h]
000007fe`e5eddf45 48c744242800000000 mov qword ptr [rsp+28h],0
000007fe`e5eddf4e 48894c2420 mov qword ptr [rsp+20h],rcx
000007fe`e5eddf53 41b903000000 mov r9d,3
000007fe`e5eddf59 4533c0 xor r8d,r8d
000007fe`e5eddf5c ba38000000 mov edx,38h
000007fe`e5eddf61 488bcb mov rcx,rbx
000007fe`e5eddf64 41ff5228 call qword ptr [r10+28h]
000007fe`e5eddf68 90 nop
000007fe`e5eddf69 4883c430 add rsp,30h
000007fe`e5eddf6d 5b pop rbx
000007fe`e5eddf6e c3 ret
0:000> ln 000007fe`8d460119
呼び出しは同じです
call qword ptr [r10+28h]
でインライン化されたのでしょう。
Main()
というわけで、フレームが1つ増えるというのは赤信号です。この
call qword ptr [r10+28h]
という指示があることに気づきます。
mov qword ptr [rsp+20h],rcx
. これは、最終的に参照されるアドレスをロードするものです。
@rsi
. 良い場合は、このように
@rcx
が読み込まれます。
000007fe`e5eddf32 e8d1a075ff call CORINFO_HELP_GETSHARED_GCSTATIC_BASE (000007fe`e5638008)
000007fe`e5eddf37 488b88f0010000 mov rcx,qword ptr [rax+1F0h]
悪い場合は、かなり違って見えます。
000007fe`8d4600d9 48b92012721200000000 mov rcx,12721220h
000007fe`8d4600e3 488b09 mov rcx,qword ptr [rcx]
これは非常に異なっています。CORINFO_HELP_GETSHARED_GCSTATIC_BASE を呼び出し、AV の原因となるクリティカルポインタをオフセットのメンバーから読み取る良いケースとは異なります。
1F0
をリターン構造体から読み込むと、最適化されたコードではスタティックアドレスから読み込まれます。そしてもちろん、12721220hにはNULLが含まれています。
0:000> dp 12721220h L8
00000000`12721220 00000000`00000000 00000000`00000000
00000000`12721230 00000000`00000000 00000000`02722198
00000000`12721240 00000000`027221c8 00000000`027221f8
00000000`12721250 00000000`02722228 00000000`02722258
残念ながら今さら深堀りするのは無理ですが、分解された
CORINFO_HELP_GETSHARED_GCSTATIC_BASE
は些細なことではありません。CLRの内部についてより詳しい人が理解してくれることを期待して、これを投稿します(ご覧のように、私は本当にネイティブ命令の視点だけで問題を考え、ILを完全に無視しました)。
関連
-
[解決済み] Visual Studioの "ターゲットフレームワーク "が見つからない
-
[解決済み] ファイルまたはアセンブリ 'System.Data.SQLite' をロードできませんでした。
-
[解決済み] 到達不能なホストに対してソケット操作を行おうとした
-
[解決済み] .NET Framework 4 マルチターゲティングパックがアンインストールできない
-
[解決済み] 1は有効なWin32アプリケーションではありません "を解決するには?
-
[解決済み] このコンピュータには、.NET Framework 4.6またはそれ以降の更新プログラムがすでにインストールされています。
-
[解決済み] 埋め込みリソーステキストファイルの読み方
-
[解決済み] .Any() vs .Count() > 0のどちらのメソッドがより良いパフォーマンスを発揮しますか?
-
[解決済み] System.Timers.Timer vs System.Threading.Timer
-
[解決済み] WCF - メッセージサイズのクォータを増加させる方法
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] COMExceptionエラー80040154を修復する方法は?
-
[解決済み] VS2017/2015 で .xproj ファイルを開く方法
-
[解決済み] ファイルまたはアセンブリ 'System.Data.SQLite' をロードできませんでした。
-
[解決済み] .Netの配列のLongLengthの目的は何ですか?
-
[解決済み] ASP.NET control to render a <div>
-
[解決済み] 権限 '*' を持つ SSL/TLS の安全なチャネルを確立できませんでした。
-
[解決済み] 列挙型を文字列に変換する
-
[解決済み] .Any() vs .Count() > 0のどちらのメソッドがより良いパフォーマンスを発揮しますか?
-
[解決済み] Visual Studioの「Any CPU」ターゲットはどういう意味ですか?
-
[解決済み] プライベートメソッドのユニットテストはどのように行うのですか?