调用堆栈中的参数高于本地而不是低于返回地址? [复制]

Posted

技术标签:

【中文标题】调用堆栈中的参数高于本地而不是低于返回地址? [复制]【英文标题】:Arguments in call stack above locals instead of below return address? [duplicate] 【发布时间】:2022-01-20 14:35:37 【问题描述】:

我试图通过一些实际示例来了解函数的调用堆栈。在所有解释这一点的图表中,它的布局类似于 [局部变量][返回地址][参数](左侧内存不足)。但是当我在 gdb 中并在函数中设置断点时,我会以不同的顺序获取它们:

(gdb) info args
arg1 = 0
arg2 = 0
arg3 = 32767
(gdb) p &arg1
0x7ffff3a4697ec
(gdb) p &arg2
0x7ffff3a4697e8
(gdb) p &arg3
0x7ffff3a4697e4
(gdb) info locals
local1 = 0
local2 = 0
local3 = 0
(gdb) p &local1
0x7ffff3a4697fc
(gdb) p &local2
0x7ffff3a4697f8
(gdb) p &local3
0x7ffff3a4697f4
(gdb) info frame
Stack level 0, frame at 0x7ffff3a469810:
...
Arglist at 0x7ffff3a469800, args: arg1=0, arg2=0, arg3=32767
Locals at 0x7ffff3a469800, Previous frame's sp is 0x7ffff3a469810
Saved registers:
 rbp at 0x7ffff3a469800, rip at 0x7ffff3a469808

为什么函数的参数位于比局部变量和返回指针低的内存地址?所有关于该主题的文献(例如像https://upload.wikimedia.org/wikipedia/commons/thumb/d/d3/Call_stack_layout.svg/342px-Call_stack_layout.svg.png 这样的图表)都暗示参数应该位于比返回地址更高的内存地址?返回地址应该在本地和参数之间,而我在一个连续的块中有本地和参数,最后是返回地址。非常感谢 - 如果我完全误解了,我们深表歉意!

编辑:生成此代码的示例 C 程序:

#include <stdio.h>

void func1(int arg1, int arg2, int arg3) 
  int local1;
  int local2;
  int local3;
  local1 = 2;
  local2 = 3;
  local3 = 4;


int main()
  int a;
  int b;
  int c;
  func1(a, b, c);

在 CentOS x86_64 上使用 gcc code.c -o code 编译此代码。使用 gdb 运行并在 func1 中放置一个断点。查看 arg 变量的地址、局部变量和返回地址。

【问题讨论】:

您链接到的图片没有说明任何地址的值。 栈顶通常表示低内存地址;如果你想要i.stack.imgur.com/Z5cSh.jpg,这里有另一张图片明确说明了这一点 Spectre... ;) 这只是一个在 Centos x86 上用 gcc(无参数)编译的简单 c 程序 @InnocentBystander 用 C 源代码更新了帖子 【参考方案1】:

为什么函数的参数位于比局部变量和返回指针低的内存地址?所有关于该主题的文献 (...) 都暗示参数应该位于比返回地址更高的内存地址?

由于您使用的是 x86_64 ABI,前 6 个参数通常会在寄存器中传递。以下是来自System V Application Binary Interface AMD64 Architecture Processor Supplement 的相关引用(第 20 页,重点是我的,感谢@KamilCuk 提供链接):

传递 一旦参数被分类,寄存器被分配(从左到右 order) 传递如下:

    如果类是 MEMORY,则在堆栈上传递参数。 如果类是 INTEGER,则使用序列 %rdi、%rsi、%rdx、%rcx、%r8 和 %r9 的下一个可用寄存器。

所以,最有可能发生的情况是您在 func1main 之间混合堆栈帧。看看这段代码:

https://godbolt.org/z/88rPebqYj

参数 arg1arg2arg3 在寄存器 rdirsirdx 中传递给 func1

【讨论】:

这是有道理的——没有意识到 x86_64 的情况如此不同!谢谢! @Trimble52 你打赌。谢谢你让我弯曲我的耳间腺。 ;) @Trimble52 如果您将-m32 添加到您的编译器,您将获得x86 代码和您期望的答案。见这里:godbolt.org/z/WK4hPPEW5 这是一个调试版本 (-O0),因此编译器将传入的寄存器 args 溢出到堆栈中,并碰巧将它们放在本地变量之下。 @Trimble52 在比本地或 args 更高的地址处找到返回地址,因此他们没有查看 main 的堆栈帧。所以它是Why are parameters allocated below the frame pointer instead of above? 的副本。但是是的,关键是注册 args。

以上是关于调用堆栈中的参数高于本地而不是低于返回地址? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

glibc backtrace() 如何确定哪些堆栈内存是返回地址?

使用寄存器而不是堆栈从 x64 程序集调用 C 函数

堆栈平衡

Heroku + MEAN 堆栈错误:参数“url”必须是字符串,而不是未定义

用python中的默认值替换高于和低于阈值的列表值?

如何获取行从高于或低于R中的临界值变化的次数的计数