为啥 gcc 4.x 在调用方法时默认为 linux 上的堆栈保留 8 个字节?

Posted

技术标签:

【中文标题】为啥 gcc 4.x 在调用方法时默认为 linux 上的堆栈保留 8 个字节?【英文标题】:why gcc 4.x default reserve 8 bytes for stack on linux when calling a method?为什么 gcc 4.x 在调用方法时默认为 linux 上的堆栈保留 8 个字节? 【发布时间】:2011-01-24 20:07:55 【问题描述】:

作为asm的初学者,我正在检查gcc -S生成的asm代码来学习。

为什么 gcc 4.x 在调用方法时默认为堆栈保留 8 个字节?

func18 是没有返回没有参数没有定义本地变量的空函数。 我不知道为什么这里保留了 8 个字节(任何论坛/网站都没有提到这个原因,人们似乎认为这是理所当然的) 是为了 %ebp 只是推动?还是返回类型?!非常感谢!

      .globl _func18
  _func18:
     pushl   %ebp 
     movl    %esp, %ebp 
     subl    $8, %esp 
     .text 

【问题讨论】:

实际上,这些指令都没有意义……没有任何局部变量的函数应该不需要设置帧指针 这可能取决于优化级别。您能否包含该函数的完整生成代码? 这不是给返回地址和栈帧指针的空间吗? thx martin & richard, richard@ 我刚刚用 -Os 做了,是的,你是对的,现在 subl $8, %esp 行不见了。但我仍然想知道它这样做的原因。我在回复中附上代码 Martin:帧指针在没有局部变量的函数中仍然有用,因此调试器可以访问函数的参数作为从%ebp 的常量偏移量。 【参考方案1】:

某些指令要求将某些数据类型对齐到多达 16 字节的边界(特别是 SSE 数据类型 __m128)。为了满足这个要求,gcc 确保堆栈最初是 16 字节对齐的,并以 16 字节的倍数分配堆栈空间。如果只需要压入一个 4 字节的返回地址和 4 字节的帧指针,则需要 8 个额外的字节来保持堆栈与 16 字节边界对齐。然而,如果 gcc 确定额外的对齐是不必要的(即没有使用花哨的数据类型并且没有调用外部函数),那么它可能会省略任何用于对齐堆栈的额外指令。确定这一点所需的分析可能需要执行某些优化过程。

另请参阅选项 -mpreferred-stack-boundary=num 的 gcc 文档。

【讨论】:

thx 标记,因此在使用 sse 进行优化时,这一切都是为了数据对齐,非常明智,因为我禁用了优化并且 subl $8 %esp 消失了。 gcc ref 非常有用!!!只有一件事,当我调整 -mpreferred-stack-boundary 时,保留只在 3 到 4 之间变化,从 4 到 12,它坚持 8 个字节,我认为保留应该是 20 个字节,不是吗? 如果你使用了 -mpreferred-stack-boundary=12,那么在任何调用外部函数的函数中,它都会以 2^12=4096 字节的倍数分配堆栈空间。如果您没有调用任何外部函数,那么它通常能够发现它生成的代码不需要保持这种对齐(取决于您的确切 gcc 版本、选项和目标架构)。 所以你的意思是在 func 不调用外部 func 的情况下,gcc 默认只保留 8 个字节? @nikcname:我在 gcc 4.4.1 (Ubuntu 9.10) 上看不到它的空函数。您使用的是什么版本和编译选项?【参考方案2】:

正如上面 Richard 所说,这都是因为优化,如下所示。 但我仍然不知道为什么保留 8 个字节是优化的东西?!

原作

void func18() 
int main() return 0;

编译时不指定优化标志

    .text                                                                                   
.globl _func18
_func18:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $8, %esp
    leave
    ret
.globl _main
_main:                                                                                      
    pushl   %ebp
    movl    %esp, %ebp
    subl    $8, %esp
    movl    $0, %eax
    leave
    ret
    .subsections_via_symbols

带 -Os 优化标志,不再保留堆栈

    .text
.globl _func18
_func18:
    pushl   %ebp
    movl    %esp, %ebp
    leave
    ret
.globl _main
_main:
    pushl   %ebp
    xorl    %eax, %eax
    movl    %esp, %ebp
    leave
    ret
    .subsections_via_symbols

【讨论】:

把这个放在你的问题中,而不是单独的答案中【参考方案3】:

简单的方法:你有没有空函数调用另一个带有一个参数的函数。如果参数直接存储到堆栈中(不推送),那么这就是额外空间的用途。

【讨论】:

以上是关于为啥 gcc 4.x 在调用方法时默认为 linux 上的堆栈保留 8 个字节?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在分配没有后续函数调用的大数组时,GCC 会向堆栈指针减去错误的值?

gcc库文件和头文件搜索路径

arm-linu-gcc安装

为啥`/usr/include`不在gcc默认搜索路径中

为啥使用gcc编译时总显示找不到文件

为啥在 GCC 5.1 中仍然启用 COW std::string 优化?