【发布时间】:2021-02-14 20:32:12
【问题描述】:
有一些调用约定(例如pascal、stdcall),但就我而言,C 确实使用cdecl(C 声明)。这些约定中的每一个在调用者将参数加载到堆栈上的方式上都略有不同,分别由哪个(调用者/被调用者)执行cleanup。
谈到清理,这是我的问题。我不明白:有三种不同的东西吗?
- 堆栈清理
- 将指针移回倒数第二个堆栈帧
- 堆栈恢复
或者我应该怎么看它们?
此外,这个问题的目标基本上是可变参数函数如何在调用约定(如 Pascal 或stdcall)中被调用者清除/清理/恢复(我不知道哪个操作)堆栈 - 但他没有'不知道它会收到多少参数。
编辑
为什么将参数压入堆栈的顺序如此重要?您仍然有第一个参数(不是来自省略号的稳定参数),它为您提供有关 - 例如 - 变量参数数量的信息。还有一个“守护者”可以添加到省略号标点符号中,并且可以用作变量部分结束的标记,独立于调用约定。 In this link 为什么调用者和被调用者都应该恢复这些寄存器的值,如果他们在搞砸之前都保存了它们的状态?不应该只有其中一个(例如调用者)在调用函数之前将它们保存在堆栈中,仅此而已?另外,在同一个链接上
"所以,堆栈指针ESP可能会上下波动,但EBP寄存器 保持固定。这很方便,因为这意味着我们可以随时参考 将第一个参数设为 [EBP + 8],无论推多少和 弹出是在函数中完成的。”
推送变量和局部变量在内存中是连续的。使用 EBP 推荐他们的优势在哪里?即使堆栈大小发生变化,它们之间也永远不会有一些动态偏移。
我读过的材料之一是this site(只是开始),以便更好地了解堆栈框架。
然后我继续寻找这些stack overview 和call stack 教程,但他们不知何故错过了我需要的部分。 What does exactly happends when you call the function(我不明白指令“调用地址”后面跟着下一条指令a push 堆栈上的值,这意味着返回值)。谁控制退货地址?呼叫者,召集者?被调用者?当被调用者返回时,程序继续执行一条指令,该指令是从寄存器中读取操作还是什么?
【问题讨论】:
-
恢复 == 恢复?回答您的问题:在从函数返回的上下文中,所有 3 种描述本质上是相同的。请注意,这个问题也适用于其他上下文,例如线程上下文切换,其中堆栈恢复有些不同。
-
@dxiv,谢谢,非常有趣的事情。不错的阅读
-
@CătălinaSîrbu 您的其他评论听起来更适合该问题。目前还不清楚这个问题的更广泛背景是什么。一旦你谈到“堆栈”,你就离开了 C 标准领域,一旦你开始讨论堆栈指针和框架,它就是关于一些特定的实现和/或 ABI。非常笼统地说,在完全受控的环境中,可变参数函数可能“猜测”它传递的参数数量,或者至少它们消耗的堆栈空间。但这需要大量脆弱的假设。
-
这些答案可能会有所帮助:How exactly does the callstack work?、How does a system call work。如果您真的想更深入地挖掘,那么我建议您阅读 Abraham Silberschatz 的操作系统概念。
标签: c x86 variadic-functions calling-convention cdecl