c – 如果在DLL中调用函数时我没有传递足够的参数,会发生什么? - 代码日志

c – 如果在DLL中调用函数时我没有传递足够的参数,会发生什么?

在dll项目中,功能如下:

extern "C" __declspec(dllexport) void foo(const wchar_t* a, const wchar_t* b, const wchar_t* c)

在其他项目中,我将使用foo函数,但是我在头文件中声明了foo函数

extern "C" __declspec(dllimport) void foo(const wchar_t* a, const wchar_t* b)

我只用两个参数来调用它。

结果是成功,我认为它关于__cdecl调用,但我想知道这个细节。

32位

默认调用约定是__cdecl,这意味着调用者将参数从左到右推送到堆栈,然后在调用返回后清除堆栈。

所以在你的情况下,来电者:

推动b
>推一下
>推送返回地址
>调用函数。

在这一点上,堆栈看起来像这样(例如,假定4个字节的指针,并记住堆栈指针向后推动的东西):

+-----+ <--- this is where esp is after pushing stuff
| ret | [esp]
+-----+
|  a  | [esp+4]
+-----+
|  b  | [esp+8]
+-----+ <--- this is where esp was before we started
| ??? | [esp+12 and beyond]
+-----+

太好了。现在问题发生在被叫方。被调用者期望参数位于堆栈上的某些位置,因此:

a被假定为[esp 4]
> b被假定为[esp 8]
> c被假定为[esp 12]

这就是问题所在:我们不知道[esp 12]中有什么。所以被调用者将会看到a和b的正确值,但会将任何未知的垃圾发生在[esp 12]中。

在这一点上,它几乎没有定义,取决于你的功能实际上与c。

毕竟这一切结束,被调用者返回,假设你的程序没有崩溃,调用者将恢复esp,堆栈指针将返回到应该是的位置。所以从呼叫者的POV来看,一切都可能很好,堆栈指针终结在它本来应该是的地方,但是被叫方看到c的垃圾。

64位

64位机器的机制是不同的,但最终的结果是大致相同的效果。 Microsoft在64位机器上使用the following calling convention,而不考虑__cdecl或任何(任何您指定的约定都被忽略,并且都被视为相同)

>以从左到右的顺序放置在寄存器rcx,rdx,r8和r9中的前四个整数或指针参数。
>前四个浮点参数以从左到右的顺序放置在寄存器xmm0,xmm1,xmm2和xmm3中。
>剩下的任何东西被推到堆栈,从右到左。
>呼叫者负责恢复esp,以及恢复呼叫后的all volatile registers的值。

所以在你的情况下,来电者:

>在rcx中放一个。
>将b放在rdx中。
>在堆栈上分配一个额外的32个字节的“阴影空间”(请参阅​​MS文章)。
>推送返回地址。
>调用函数。

但被调查人期待:

>假设在rcx(检查!)
> b假定是在rdx(检查!)
> c假定在r8(问题)

所以,就像32位的情况一样,被叫方将任何发生在r8中的事情解释为c,并且潜在的Hijinks随之发生,最终效果取决于被叫方对c的影响。当它返回时,假设程序没有崩溃,调用者restores all volatile registers(rcx和rdx,也通常包括r8和朋友)并恢复esp。

http://stackoverflow.com/questions/43134570/if-i-do-not-pass-enough-parameters-when-calling-a-function-in-a-dll-what-will-h

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:c – 如果在DLL中调用函数时我没有传递足够的参数,会发生什么?