【问题标题】:Understanding pushing onto the stack (x86 IA32 assembly)了解推入堆栈(x86 IA32 程序集)
【发布时间】:2014-10-03 12:53:45
【问题描述】:

假设我们有一个 16 字节的数组和一些 4 字节的整数。在调用函数之前,我们将数组压入堆栈;然后我们压入整数。

现在,我的理解是基指针的“下方”是返回地址和参数;正上方和顶部的堆栈指针“下方”是我们压入堆栈(和寄存器)的局部变量。

如何确定数组的起始地址以及整数的位置?简单地对数组执行“ebp - 16”,对整数执行 -20 是否准确,或者是否有我遗漏的细节?我们是否也可以参考这些与 esp 相关的东西,或者这是非常规的?

此外,是否有可能以不同方式处理此寻址的实现?

最后,是否有必要在我们的函数结束之前从堆栈中弹出这些东西?

【问题讨论】:

    标签: assembly x86 memory-address


    【解决方案1】:

    你推送的是数组的地址,而不是数组的全部内容。

    此地址的大小在 32 位系统上为 4 个字节,在 64 位系统上为 8 个字节。

    既然你知道你使用的是什么系统,计算每个参数的地址就很容易了。


    函数返回之前“留在堆栈上”的最后一件事是……返回值。

    所以是的,除此之外的所有内容都会在函数返回之前从堆栈中弹出。

    附带说明,一些编译器通过寄存器而不是堆栈传递一些参数。

    【讨论】:

    • 实际上返回值存储在 eax 中(如果有多个值,可能是 edx),而不是在堆栈中。返回值无法入栈,因为 RET 指令将返回地址从栈中弹出。
    • @MikaLammi:感谢您指出这一点……我相信答案的底部已经说明了这一点。我相信您对特定寄存器的概念对于特定的编译器是正确的,而不是任何标准的普遍事实。
    • 是的,这取决于编译器,但我认为 eax 是最常见的。关于被压入堆栈的数组地址也可以这样说。一些编译器可能会推送小数组/结构的实际数据。
    • 虽然 C/C++ 不允许函数返回数组,但它允许函数返回结构。使用典型的编译器,调用代码将从堆栈中分配本地空间(从堆栈指针中减去)以用于要返回的结构,然后压入参数,然后将返回结构的函数调用到调用代码分配的空间中。
    【解决方案2】:

    “Pushing”的意思是:减去从 ESP 推送的元素的大小,然后在 ESP 处将元素写入内存。调用函数时会推送“返回地址”。

    这意味着:当进入函数时,返回地址将位于 ESP+0,整数位于 ESP+4,数组的第一个字节位于 ESP+8。

    由被调用函数来初始化 EBP 寄存器。通常,EBP 将初始化为 ESP 的原始值减 4,因此整数位于 EBP+8 处,而数组位于 EBP+12(十进制)处。通常使用以下代码:

    push EBP    ; Save on stack so the old value can be restored later
    mov EBP,ESP ; Initialize the EBP register
    

    然而——再一次——如果调用函数没有初始化 EBP 寄存器,它就没有有用的价值。

    编程函数的程序员(或编译 C 代码的编译器)必须知道堆栈上的数据以及这些数据的位置。所以不可能“找出”数据在哪里,但您必须以数据位于给定地址的方式设计程序。

    顺便说一句:用户“barak manos”说得对,他说通常你推送的是数组的地址,而不是数组本身。但是,将数组的内容压入堆栈的情况很少见(例如函数“WinUsb_ControlTransfer”)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-27
      • 1970-01-01
      • 1970-01-01
      • 2013-12-31
      • 1970-01-01
      相关资源
      最近更新 更多