这看起来像是生成的 IL 中的问题test
方法。在崩溃发生时我们正在阅读**c
, and c
是本地号码 5。
IL_00a5 11 05 ldloc.s 0x5
IL_00a7 4a ldind.i4
IL_00a8 4f ldind.r8
IL_00a9 28 11 00 00 0a call 0xA000011
所以在这里我们看到IL说要加载的值c
,然后加载一个 4 字节有符号整数,然后将该整数视为指针并加载 8 字节实数类型(双精度型)。
在 64 位平台上,指针应该是大小中性的或 64 位的。所以ldind.i4
这是有问题的,因为底层地址是 8 字节。由于 IL 指定仅读取 4 个字节,因此 jit 必须扩展结果以获得 8 个字节的值。这里选择签署extend。
library.h @ 27:
00007ffd`b0cf2119 488b45a8 mov rax,qword ptr [rbp-58h]
00007ffd`b0cf211d 8b00 mov eax,dword ptr [rax]
00007ffd`b0cf211f 4863c0 movsxd rax,eax // **** sign extend ****
>>> 00007ffd`b0cf2122 c4e17b1000 vmovsd xmm0,qword ptr [rax]
00007ffd`b0cf2127 e854f6ffff call System.Console.WriteLine(Double) (00007ffd`b0cf1780)
在完整框架上运行时,您显然很幸运,因为数组地址很小并且适合 31 位或更少,因此读取 4 个字节,然后符号扩展到 8 个字节仍然给出正确的地址。但在 Core 上却没有,这就是应用程序崩溃的原因。
看来您使用 Win32 目标生成了库。如果您使用 x64 目标重建它,IL 将使用 64 位加载*c
:
IL_00ab: ldloc.s V_5
IL_00ad: ldind.i8
IL_00ae: ldind.r8
IL_00af: call void [mscorlib]System.Console::WriteLine(float64)
并且该应用程序运行得很好。
看来这是 C++/CLI 中的一个功能——即使在纯模式下,它生成的二进制文件也隐式依赖于体系结构。仅有的/clr:safe
可以生成与体系结构无关的程序集,并且您不能将其与此代码一起使用,因为它包含无法验证的结构(如指针)。
另请注意,.Net Core 2.x 并不支持 C++/CLI 的所有功能。这个特定的示例避免了不支持的位,但更复杂的位可能不会。