为什么在生成汇编代码时gcc会这样做?

8

我正在使用gcc -S玩耍,以了解内存和堆栈的工作原理。在这些玩耍中,我发现有几件事情不太清楚。您能帮助我理解原因吗?

  1. When calling function sets arguments for a called one it uses mov to esp instead push. What is the advantage not using push?

  2. Function which works with its stack located arguments points to them as ebp + (N + offset) (where N is a size reserved for return address). I expect to see esp - offset which is more understandable. What is the reason to use ebp as fundamental point everywhere? I know these ones are equal but anyway?

  3. What is this magic for in the beginning of main? Why esp must be initialized in this way only?

    and    esp,0xfffffff0
    
感谢您的信件。

3
这可能是三个不同的问题。无论如何,第三个问题的答案是堆栈对齐。 - Mysticial
1个回答

7
I will assume you are working under a 32-bit environment because in a 64-bit environment arguments are passed in registers.
问题1
也许您正在传递浮点参数。在32位运行时中,push指令一次只能推送4个字节,因此您无法直接推送这些参数,您需要将值拆分开来。有时更容易从esp减去8再将8字节的数据移到[esp]中。
问题2
ebp经常用于索引32位代码中堆栈帧中的参数和局部变量。这使得堆栈帧内的偏移量固定,即使堆栈指针移动也不会改变。例如,请考虑
void f(int x) {
    int a;
    g(x, 5);
}

现在,如果你只使用esp访问堆栈帧内容,那么a[esp]处,返回地址在[esp+4]处,x[esp+8]处。现在让我们生成调用g的代码。我们必须先推入5然后推入x。但是在推入5之后,x相对于esp的偏移量已经改变了!这就是为什么要使用ebp的原因。通常,在进入函数时,我们将ebp的旧值推入堆栈以保存它,然后将esp复制到ebp中。现在,ebp可以用来访问堆栈帧内容。当我们正在传递参数时,它不会移动。
问题3:
这个and指令将esp的最后4位清零,将其对齐到16字节边界。由于堆栈向下增长,这很好而且安全。

1
Q3:它将最后4位清零。 - Gunther Piez
哦,谢谢@drhirsch。 没有思考就打字,太糟糕了。 非常感谢!已经编辑过了。 - Ray Toal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接