gpt4 book ai didi

.net - 为什么 .NET 程序可以在损坏的堆栈中存活? (使用错误的调用约定时)

转载 作者:行者123 更新时间:2023-12-04 07:10:48 24 4
gpt4 key购买 nike

在 VS2010 中,如果您使用错误的调用约定调用函数,托管调试助手会给您一个 pInvokeStackImbalance 异常( pInvokeStackImbalance MDA ),通常是因为您在调用 C 库时没有指定 CallingConvention = Cdecl。例如。你写了

[DllImport("some_c_lib.dll")]
static extern void my_c_function(int arg1, int arg2);

代替
[DllImport("some_c_lib.dll", CallingConvention=CallingConvention.Cdecl)]
static extern void my_c_function(int arg1, int arg2);

因此得到了 StdCall 调用约定而不是 Cdelc。

如果你回答这个,你已经知道区别了,但是对于这个线程的其他访问者:StdCall 意味着被调用者从堆栈中清除参数,而 Cdecl 意味着调用者清理堆栈。

因此,如果您在 C 代码中弄错了调用约定,您的堆栈不会被清理并且您的程序会崩溃。

但是,即使 .NET 程序使用 StdCall 来处理 Cdecl 函数,它们似乎也不会崩溃。 默认情况下,VS2008 上未启用堆栈不平衡检查,因此某些 VS2008 项目使用了作者不知道的错误调用约定。我刚试过 GnuMpDotNet即使缺少 Cdelc 声明,示例也能正常运行。 X-MPIR 也是如此.

它们都在 Debug模式下抛出 pInvokeStackImbalance MDA 异常,但在 Release模式下不会崩溃。为什么是这样? .NET VM 是否包装了对 native 代码的所有调用并在之后恢复堆栈本身?如果是这样,为什么还要为 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

以及它结束的方式:
0000014a  mov         esp,ebp             ; restore stack pointer
0000014c pop ebp ; restore base pointer
0000014d ret

使 esp 值不平衡在这里不是问题,它是从 ebp 寄存器值恢复的。然而,当抖动优化器可以将局部变量存储在 cpu 寄存器中时,它经常会优化它。当 RET 指令从堆栈中检索错误的返回地址时,您将崩溃并烧毁。希望无论如何,当它偶然落在一大块机器代码上时真的很讨厌。

当您在没有调试器的情况下运行发布版本时,这很可能发生,如果您没有 MDA 来帮助您,则很难进行故障排除。

关于.net - 为什么 .NET 程序可以在损坏的堆栈中存活? (使用错误的调用约定时),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5358107/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com