为什么没有崩溃的PInvoke的情况下侵犯调用约定(在.NET 3.5)的? [英] Why isn't PInvoke crashing in case of violated calling convention (in .NET 3.5)?

查看:192
本文介绍了为什么没有崩溃的PInvoke的情况下侵犯调用约定(在.NET 3.5)的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的解决方案有一个托管C ++ DLL,其中出口功能,以及托管应用程序PInvokes此功能。

My solution has an unmanaged C++ DLL, which exports a function, and a managed application that PInvokes this function.

我刚刚转换的解决方案,从.NET 3.5到.NET 4.0和得到这个PInvokeStackImbalance的,调用PInvoke的函数[...]有不平衡栈的异常。事实证明,我打电话__cdecl'ed功能,因为它是__stdcall:

I've just converted the solution from .NET 3.5 to .NET 4.0 and got this PInvokeStackImbalance "A call to PInvoke function [...] has unbalanced the stack" exception. As it turned out, I was calling __cdecl'ed function, as it was __stdcall:

C ++部分(被叫):

C++ part (callee):

__declspec(dllexport) double TestFunction(int param1, int param2); // by default is __cdecl

C#部分(来电):

C# part (caller):

[DllImport("TestLib.dll")] // by default is CallingConvention.StdCall
private static extern double TestFunction(int param1, int param2);

所以,我已经修复了这一错误,但现在我感兴趣的是如何在.NET 3.5中做这项工作?为什么(多次重复)的情况时,没有人(无论被叫,主叫方也没有)清理堆栈,并没有导致堆栈溢出或其他不当行为,但只是工作好不好?是否有某种形式的PInvoke的检查,就像在他的文章? 这也是有趣的,为什么打破惯例的相反类型(有__stdcall被调用方PInvoked就像是__cdecl)工作不到位,致使刚刚EntryPointNotFoundException。

So, I've fixed the bug, but now I'm interested in how did this work in .NET 3.5? Why the (many times repeated) situation when nobody (neither callee, nor caller) cleans the stack, did not caused stack overflow or some other misbehavior, but just worked OK? Is there some sort of a check in PInvoke, like mentioned by Raymond Chen in his article? It's also interesting, why the opposite type of breaking convention (having __stdcall callee be PInvoked like being __cdecl) is not working at all, causing just EntryPointNotFoundException.

推荐答案

经过一番调查:

的辅助,即从崩溃保存的情况下,是另一个寄存器 - EBP,基指针指向堆栈帧的开头。为函数的局部变量的所有访问通过此指针(除了优化code,见下面的编辑)完成。在函数返回前,堆栈指针被重置为基指针的值。

The helper, that saves the situation from crashing, is another register - EBP, base pointer that points to the beginning of stack frame. All access to function's local variables is done through this pointer (except for optimized code, see the edit below). Before the function returns, the stack pointer is reset to the base pointer's value.

函数之前(说的PInvoke)调用另一个函数(进口DLL的功能),堆栈指针指向调用函数的局部变量的结束。然后调用者推参数堆栈,并调用其他的功能。

Before a function (say PInvoke) calls another function (imported DLL's function), the stack pointer points to the end of the caller function's local variables. Then the caller pushes parameters to the stack and calls that other function.

在上面描述的情况,当一个函数调用另一个函数作为__stdcall,而它实际上是__cdecl,没人清除栈,从这些参数。因此,从被叫方返回后,堆栈指针指向推参数的端部阻塞。这就像调用函数(PInvoke的)只是得到了更多的局部变量。

In the described situation, when a function calls another function as being __stdcall, while it is actually __cdecl, nobody clears the stack from these parameters. So, after return from the callee, the stack pointer points to the end of the pushed parameters block. It is like the caller function (PInvoke) just got several more local variables.

由于访问调用者的局部变量是通过基指针完成的,它不会破坏任何东西。唯一不好的事情可能发生,如果是被调用函数将被调用一次,许多次。在这种情况下,协议栈将增长并且可能会溢出。但是,由于的PInvoke调用DLL的功能只有一次,然后返回,堆栈指针刚复位到基本指针,一切都很好。 修改作为此处指出,在code也可以优化存储本地变量在CPU只注册。在这种情况下的EBP没有使用,因此无效的ESP可能导致返回到无效地址。

Since access to the caller's local variables is done through the base pointer, it does not break anything. The only bad thing that may happen, is if the callee function will be called many times at once. In this case the stack will grow and may overflow. But since PInvoke calls the DLL's function only once, and then returns, the stack pointer just resets to the base pointer, and all is well. As noted here, the code may also be optimized to store local variables in CPU registers only. In this case EBP is not used and thus invalid ESP may cause returning to invalid address.

这篇关于为什么没有崩溃的PInvoke的情况下侵犯调用约定(在.NET 3.5)的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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