堆栈分配,为什么额外的空间?

时间:2012-03-25 16:48:33

标签: gcc assembly x86 stack memory-alignment

我正在玩一下以更好地掌握调用约定以及如何处理堆栈,但我无法弄清楚为什么main在设置堆栈时会分配三个额外的双字(在<main+0> )。它既没有与8个字节对齐,也没有16个字节,所以这并不是我所知道的原因。正如我所看到的,main需要12个字节才能将两个参数设置为func和返回值。

我错过了什么?

该程序是在x86架构上使用“gcc -ggdb”编译的C代码。

编辑:我从gcc中删除了-O0标志,​​它对输出没有任何影响。

(gdb) disas main
Dump of assembler code for function main:
    0x080483d1 <+0>:    sub    esp,0x18
    0x080483d4 <+3>:    mov    DWORD PTR [esp+0x4],0x7
    0x080483dc <+11>:   mov    DWORD PTR [esp],0x3
    0x080483e3 <+18>:   call   0x80483b4 <func>
    0x080483e8 <+23>:   mov    DWORD PTR [esp+0x14],eax
    0x080483ec <+27>:   add    esp,0x18
    0x080483ef <+30>:   ret    
End of assembler dump.

编辑:当然我应该发布C代码:

int func(int a, int b) {
    int c = 9;
    return a + b + c;
}

void main() {
    int x;
    x = func(3, 7);
}

该平台是Arch Linux i686。

2 个答案:

答案 0 :(得分:5)

当您输入函数时,函数的参数(包括但不限于main)已经在堆栈中。您在函数内分配的空间用于局部变量。对于具有简单返回类型的函数,例如int,返回值通常位于寄存器(eax中,在x86上具有典型的32位编译器。)

例如,如果main是这样的话:

int main(int argc, char **argv) { 
   char a[35];

   return 0;
}

...当我们输入main时,我们希望在堆栈上分配至少35个字节,以便为a腾出空间。假设一个32位实现,通常会四舍五入到4的下一个倍数(在这种情况下为36),以保持堆栈的32位对齐。我们不希望看到为返回值分配任何空间。 argcargv将在堆栈中,但在输入main之前它们已经在堆栈中,因此main不需要做任何事情来分配空间对他们来说。

在上面的案例中,在为a分配空格后,a通常从[esp-36]开始,argv将在[esp-44]和{{1}将在argc(或者这两个可能会被颠倒 - 取决于参数是从左到右还是从右到左推)。如果您想知道为什么我跳过[esp-48],那将是返回地址。

编辑:这是进入该函数的堆栈图,并在设置堆栈帧之后:

enter image description here

编辑2:根据您更新的问题,您所拥有的内容略微迂回,但并不是特别难以理解。进入[esp-40]后,它不仅为main的本地变量分配空间,而且还为您传递给main调用的函数的参数分配空间。

这至少占据了分配的额外空间(虽然不一定全部)。

答案 1 :(得分:2)

这是一致的。我假设某些原因esp从一开始就是一致的,但显然不是。

gcc将堆栈帧对齐为每个默认值16个字节,这就是发生的事情。