Joh*_*lds 8 .net pinvoke dllimport
在VS2010中,如果使用错误的调用约定调用函数,则托管调试助手将为您提供pInvokeStackImbalance异常(pInvokeStackImbalance MDA),这通常是因为在调用C库时未指定CallingConvention = Cdecl.比如你写的
[DllImport("some_c_lib.dll")]
static extern void my_c_function(int arg1, int arg2);
Run Code Online (Sandbox Code Playgroud)
代替
[DllImport("some_c_lib.dll", CallingConvention=CallingConvention.Cdecl)]
static extern void my_c_function(int arg1, int arg2);
Run Code Online (Sandbox Code Playgroud)
因此获得了StdCall调用约定而不是Cdelc.
如果你回答这个问题,你已经知道了这个差异,但是对于这个线程的其他访问者:StdCall意味着被调用者清除堆栈中的参数,而Cdecl意味着调用者清理堆栈.
因此,如果您的C代码中的调用约定错误,则您的堆栈不会被清理并且程序崩溃.
但是,即使.NET程序使用StdCall进行Cdecl功能,.NET程序似乎也不会崩溃.默认情况下,VS2008上没有启用堆栈不平衡检查,因此一些VS2008项目使用了他们的作者不知道的错误调用约定.我刚尝试过GnuMpDotNet,即使缺少Cdelc声明,样本运行也很好.X-MPIR也是如此.
它们都在调试模式下抛出pInvokeStackImbalance MDA异常,但在发布模式下不会崩溃.为什么是这样?.NET VM是否将所有对本机代码的调用包装起来并在之后恢复堆栈本身?如果是这样,为什么还要使用CallingConvention属性呢?
这是因为方法退出时堆栈指针的恢复方式.一个方法的标准序言,显示为x86抖动;
00000000 push ebp ; save old base pointer
00000001 mov ebp,esp ; setup base pointer to point to activation frame
00000003 sub esp,34h ; reserve space for local variables
Run Code Online (Sandbox Code Playgroud)
它的结束方式:
0000014a mov esp,ebp ; restore stack pointer
0000014c pop ebp ; restore base pointer
0000014d ret
Run Code Online (Sandbox Code Playgroud)
获取esp值不平衡不是问题,它从ebp寄存器值恢复.但是,当抖动优化器可以在cpu寄存器中存储局部变量时,它不会很少优化它.当RET指令从堆栈中检索错误的返回地址时,您将崩溃并刻录.无论如何,当它恰巧落在一大堆机器代码上时,真的很讨厌.
当您在没有调试器的情况下运行发布版本时,这很容易发生,如果您没有MDA来帮助您,则很难进行故障排除.
归档时间: |
|
查看次数: |
1133 次 |
最近记录: |