C ++ / CLI MSIL汇编中的指针数组 [英] Array of pointers in C++/CLI MSIL assembly

查看:102
本文介绍了C ++ / CLI MSIL汇编中的指针数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试包装一些旧的C代码,以与在.NET Core上运行的C#结合使用。我正在使用此处提供的方法来创建一个C ++包装程序,该程序可以编译为纯MSIL。它适用于简单的函数,但是我发现,如果我的代码曾经使用指针到指针或指针数组,它将因内存冲突而崩溃。通常它会使Visual Studio崩溃,而我必须重新启动一切,这很乏味。

I'm trying to wrap some legacy C code for use with C# running on .NET Core. I'm using the approach given here to create a C++ wrapper that compiles to pure MSIL. It's working well for simple functions, but I've found that if my code ever uses pointers-to-pointers or arrays of pointers it will crash with a memory violation. Often it crashes Visual Studio and I have to restart everything, which is tedious.

例如,以下代码将导致崩溃:

For example, the following code will cause the crashes:

public ref class example
    {
    public:

        static void test() {
            Console::WriteLine("\nTesting pointers.");

            double a[5] = {5,6,7,8,9}; //Array.
            double *b = a; //Pointer to first element in array.

            Console::WriteLine("\nTesting bare pointers.");
            Console::WriteLine(a[0]); //Prints 5.
            Console::WriteLine(b[0]); //Prints 5.

            Console::WriteLine("\nTesting pointer-to-pointer.");
            double **c = &b;
            Console::WriteLine(c == &b); //Prints true.
            Console::WriteLine(b[0]); //Works, prints 5.
            Console::WriteLine(**c); //Crashes with memory access violation.

            Console::WriteLine("\nTesting array of pointers.");
            double* d[1];
            d[0] = b;
            Console::WriteLine(d[0] == b); //Prints false???
            Console::WriteLine(b[0]); //Works, prints 5.
            Console::WriteLine(d[0][0]); //Crashes with memory access violation.

            Console::WriteLine("\nTesting CLI array of pointers.");
            cli::array<double*> ^e = gcnew cli::array<double*> (5);
            e[0] = b;
            Console::WriteLine(e[0] == b); //Prints false???
            Console::WriteLine(b[0]); //Works, prints 5.
            Console::WriteLine(e[0][0]); //Crashes with memory access violation.
        }
}

请注意,仅使用指针不会引起任何问题。

Note that simply using pointers doesn't cause any problem. It's only when there is that extra level of indirection.

如果我将代码放到CLR C ++控制台应用程序中,它将完全按预期运行,并且不会崩溃。仅当使用 clr:pure 将代码编译为MSIL程序集并从.NET核心应用程序运行时,才会发生崩溃。

If I drop the code in a CLR C++ console app, it works exactly as expected and doesn't crash. The crash only happens when compiling the code into an MSIL assembly with clr:pure and running from a .NET core app.

可能会发生什么?

更新1:以下是Visual Studio文件: https://app.box.com/s/xejfm4s46r9hs0inted2kzhkh9qzmjpb 这是两个项目。 MSIL程序集称为 library ,而 CoreApp 是C#控制台应用程序,将调用该库。警告,运行Visual Studio时很可能使它崩溃。

Update 1: Here are the Visual Studio files: https://app.box.com/s/xejfm4s46r9hs0inted2kzhkh9qzmjpb It's two projects. The MSIL assembly is called library and the CoreApp is a C# console app that'll call the library. Warning, it's likely to crash Visual Studio when you run it.

更新2:我也注意到了这一点:

Update 2: I noticed this too:

        double a[5] = { 5,6,7,8,9 };
        double* d[1];
        d[0] = a;
        Console::WriteLine(d[0] == a); //Prints true.
        Console::WriteLine(IntPtr(a)); //Prints a number.
        Console::WriteLine(IntPtr(d[0])); //Prints a completely different number.


推荐答案

这看起来像是生成的IL中的问题 test 方法。在崩溃时,我们正在读取 ** c ,而 c 是本地数字5。

This looks like an issue in the generated IL for the test method. At the point of the crash we're reading **c, and c is local number 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字节实型(双精度)。

So here we see the IL says to load the value of c, then load a 4 byte signed integer, then treat that integer as a pointer and load an 8 byte real type (double).

在64位平台上,指针应为大小无关或64位。因此 ldind.i4 有问题,因为基础地址为8个字节。并且由于IL指定仅读取4个字节,因此jit必须扩展结果以获取8个字节的值。

On a 64 bit platform pointers should be either size-neutral or 64 bits. So the ldind.i4 is problematic as the underlying address is 8 bytes. And since the IL specifies reading only 4 bytes, the jit must extend the result to get an 8 byte value. Here it chooses to sign 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上却没有,所以这就是应用程序在那里崩溃的原因。

You apparently get lucky when running on full framework as the array address is small and fits in 31 bits or less, so reading 4 bytes and then sign-exending to 8 bytes still gives the right address. But on Core it doesn't and so that's why the app crashes there.

似乎您使用Win32目标生成了库。如果使用x64目标进行重建,则IL将对 * c 使用64位加载:

It appears you generated your library using the Win32 target. If you rebuild it with an x64 target the IL will use a 64 bit load for *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 可以生成与体系结构无关的程序集,并且您不能在此代码中使用它,因为它包含指针等无法验证的构造。

It appears this is a feature in C++/CLI -- the binaries it produces are implicitly architecture dependent even in pure mode. Only /clr:safe can produce architecture-independent assemblies, and you can't use that with this code since it contains unverifiable constructs like pointers.

还请注意,.Net Core 2.x不支持C ++ / CLI的所有功能。这个特定的示例避免了不支持的位,但是更复杂的位可能不会。

Also please note not all features of C++/CLI are supported in .Net Core 2.x. This particular example avoids unsupported bits, but more complex ones may not.

这篇关于C ++ / CLI MSIL汇编中的指针数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆