gpt4 book ai didi

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

转载 作者:IT老高 更新时间:2023-10-28 22:24:28 28 4
gpt4 key购买 nike

在一个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,这意味着调用者从右到左将参数压入堆栈,然后在调用返回后清理堆栈。

所以在你的情况下,调用者:

  1. 插入 b
  2. 推一个
  3. 推送返回地址
  4. 调用函数。

此时堆栈看起来像这样(例如假设 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] 中有什么。所以被调用者将看到 ab 的正确值,但会将发生在 [esp+12] 的任何未知垃圾解释为c.

此时它几乎是未定义的,并且取决于您的函数对 c 的实际作用。

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


64 位

64 位机器上的机制不同,但最终结果大致相同。微软使用 the following calling convention在 64 位机器上不管 __cdecl 或其他任何东西(您指定的任何约定都将被忽略,并且都被同等对待):

  • 前四个整数或指针参数放置在寄存器 rcxrdxr8r9 中,在这个顺序,从左到右。
  • 前四个浮点参数放在寄存器 xmm0xmm1xmm2xmm3 中,在这个顺序,从左到右。
  • 剩余的所有内容都会从右到左插入堆栈。
  • 调用者负责恢复 esp 以及恢复 all volatile registers 的值。通话后。

所以在你的情况下,调用者:

  1. a 放入 rcx
  2. b 放入 rdx
  3. 在堆栈上分配额外的 32 字节“影子空间”(参见那篇 MS 文章)。
  4. 推送返回地址。
  5. 调用函数。

但被调用者期待:

  • a 假定在 rcx 中(检查!)
  • b 假定在 rdx 中(检查!)
  • c 假定在 r8 中(问题)

因此,与 32 位情况一样,被调用者将发生在 r8 中的任何内容解释为 c,随后出现潜在的问题,最终效果取决于关于被调用者对 c 所做的事情。返回时,假设程序没有崩溃,调用者 restores all volatile registers (rcxrdx,一般也包括r8和 friend )和恢复esp

关于c++ - 如果我在调用 DLL 中的函数时没有传递足够的参数,会发生什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43134570/

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