【问题标题】:what's the purpose of pushing address of local variables on the stack(assembly)将局部变量的地址推入堆栈(程序集)的目的是什么
【发布时间】:2019-02-22 01:47:32
【问题描述】:

让我们有一个函数:

int caller()
{
   int arg1 = 1;
   int arg2 = 2
   int a = test(&arg1, &arg2)
}
test(int *a, int *b)
{
    ...
}

所以我不明白为什么 &arg1 和 &arg2 也必须像这样被压入堆栈

我可以理解,我们可以通过使用来获取被调用者中arg1和arg2的地址

movl  8(%ebp), %edx
movl  12(%ebp), %ecx

但如果我们不将这两个压入堆栈, 我们也可以使用他们的地址:

leal 8(%ebp), %edx
leal 12(%ebp), %ecx 

那么,为什么还要将 &arg1 和 &arg2 推入堆栈呢?

【问题讨论】:

  • 如果你再打电话给自己怎么办? 递归函数,你会把所有这些变量放在哪里?
  • 为什么你的“函数调用前”图在堆栈上显示&arg1 和&arg2?如果您不调用该函数,则永远不会推送地址,即使在调试版本中也是如此。我猜你是在 call 指令之前显示状态?
  • @PeterCordes 是的,就在调用测试函数之前
  • 但这很奇怪,因为你的“之后”图表是在被调用者使用push %ebp / mov %esp, %ebp 制作遗留/调试模式堆栈帧之后,而不是正确之后 i> call 指令。
  • @PeterCordes 好的,所以不要担心“之后”的事情,只是想知道为什么 &arg1 和 &arg2 必须被压入堆栈?

标签: c assembly stack instruction-set


【解决方案1】:

在一般情况下,test 必须在您传递任意指针时工作,包括 extern int global_var 或其他任何指针。然后main 必须根据 ABI / 调用约定来调用它。

所以 test 的 asm 定义不能假设 int *a 指向的位置,例如它指向调用者的栈帧。

(或者您可以将其视为优化本地引用调用中的地址,因此调用者必须将指向的对象放置在传递参数的插槽中,并返回堆栈的这两个双字内存保存*a 和*b 的潜在更新值。)

您在禁用优化的情况下编译。特别是对于调用者将指针传递给本地的特殊情况,解决这个问题的方法是内联整个函数,编译器在启用优化时会这样做。

编译器允许制作test 的私有克隆,它通过值、寄存器或编译器想要使用的任何自定义调用约定获取其参数。但是,大多数编译器实际上并没有这样做,而是依靠内联而不是私有函数的自定义调用约定来摆脱 arg 传递开销。

或者如果它被声明为static test,那么编译器就已经知道它是私有的,并且理论上可以使用它想要的任何自定义调用约定,而无需使用类似test.clone1234 的名称进行克隆。 gcc 有时确实会为持续传播而这样做,例如如果调用者传递了一个编译时常量但 gcc 选择不内联。 (或者不能因为你用了__attribute__((noinline)) static test() {})


顺便说一句,具有良好的 register-args 调用约定,如 x86-64 System V,调用者会执行 lea 12(%rsp), %rdi / lea 8(%rsp), %rsi / call test 之类的。 i386 System V 调用约定陈旧且效率低下,将堆栈上的所有内容都传递给强制存储/重新加载。

您基本上已经确定了堆栈参数调用约定具有较高开销并且通常很糟糕的原因之一。

【讨论】:

    【解决方案2】:

    如果您直接访问arg1 和arg2,这意味着您正在访问不属于该函数的堆栈部分。当有人使用 buffer overflow attack 从调用堆栈访问其他数据时,会发生这种情况。

    当您的调用有参数时,参数被推入堆栈(在您的情况下为&arg1 和&arg2),函数可以将它们用作此函数的有效参数列表。

    【讨论】:

      猜你喜欢
      • 2015-01-09
      • 2018-01-15
      • 2020-06-01
      • 2015-01-03
      • 2014-01-22
      • 2021-10-28
      • 2019-01-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多