如果在调用 DLL 中的函数时没有传递足够的参数,会发生什么情况?
If I do not pass enough parameters when calling a function in a DLL, what will happen?
在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
。
在这一点上,它几乎是未定义的,并且取决于您的函数实际对c
做什么。
在所有这些结束并且被调用方返回后,假设您的程序没有崩溃,调用方将恢复esp
并且堆栈指针将返回到应有的位置。因此,从调用方的 POV 来看,一切可能都很好,堆栈指针最终回到了应有的位置,但被调用方看到了c
垃圾。
64 位
64 位机器上的机制不同,但最终结果大致相同。Microsoft 在 64 位计算机上使用以下调用约定,而不考虑__cdecl
或任何约定(您指定的任何约定都将被忽略,并且所有约定都以相同的方式处理):
- 中的前四个整数或指针参数按从左到右的顺序依次排列在寄存器
rcx
、rdx
、r8
和r9
。
寄存器 - 中的前四个浮点参数按从左到右的顺序排列在寄存器
xmm0
、xmm1
、xmm2
和xmm3
。 - 剩余的任何内容都会从右到左被推到堆栈中。
- 调用方负责恢复
esp
以及在调用后恢复所有易失寄存器的值。
因此,在您的情况下,调用方:
- 把
a
放在rcx
. - 把
b
放在rdx
. - 在堆栈上分配额外的 32 字节"影子空间"(请参阅 MS 文章)。
- 推送返回地址。
- 调用函数。
但被叫方期待:
a
假设在rcx
(检查!b
假设在rdx
(检查!c
假定在r8
(问题)
因此,与 32 位情况一样,被调用方将r8
中发生的任何内容解释为c
,随之而来的是潜在的 hijink,最终效果取决于被调用方如何处理c
。当它返回时,假设程序没有崩溃,调用者恢复所有易失性寄存器(rcx
和rdx
,通常还包括r8
和朋友)并恢复esp
。
- 将指针分配给另一个指针时会发生什么情况?
- 如何在不知道C++中有多少可选参数的情况下在循环中使用va_arg?
- 如何在不强制转换每个参数的情况下删除初始值设定项列表中从 int 到 char 的缩小转换?
- 在什么情况下,两个堆栈分配的结构对象的 this 点指向同一个地址?
- 在什么情况下,我想在 C/C++ 代码中使用内联汇编代码
- 如何在不传递命令行参数的情况下在 c++ 中设置环境变量
- C++有什么方法可以在既不调用函数模板也不提供其模板参数的情况下引用函数模板?
- 发生注入类名时会发生什么情况?(C++)
- 是否可以在不填充自己的参数的情况下将模板函数作为参数传递?
- 在什么情况下,需要共享智能指针而无法使用唯一指针?
- 重新分配向量时,向量中的内存会发生什么情况
- 如果结构中的字符串比使用的 p/调用签名长或短,会发生什么情况?
- 为什么或在什么情况下,你会将参数作为C++中的引用(或指针)传递给函数?
- 在什么情况下,使用'const T*'输入参数比'const T&'更可取?
- 如果在调用 DLL 中的函数时没有传递足够的参数,会发生什么情况?
- 如果参数传递两次,会发生什么情况?一次按值,一次按引用?是否会修改
- 在参数中传递给 exec*() 函数系列的内存会发生什么情况
- 在什么情况下,C++函数中允许缺少模板参数
- 当按引用传递参数存储在被调用方对象中,然后被调用方删除时会发生什么情况
- 在什么情况下,用于实例化模板的参数不可用?