【问题标题】:gcc 8.2+ doesn't always align the stack before a call on x86?gcc 8.2+ 在调用 x86 之前并不总是对齐堆栈?
【发布时间】:2021-12-16 16:51:54
【问题描述】:

当前 (Linux) 版本的 SysV i386 ABI 需要在调用前进行 16 字节堆栈对齐:

输入参数区域的结尾应在 16(32,如果 __m256 在堆栈上传递)字节边界上对齐。换句话说,当控制转移到函数入口点时,值 (%esp + 4) 始终是 16 (32) 的倍数。

在 GCC 8.1 上,此代码在调用 callee 之前将堆栈与 16 字节边界对齐:(Godbolt)

source # bytes
call 4
push ebp 4
sub esp, 24 24
sub esp, 4 4
push eax 4
push eax 4
push eax 4
Total 48

在 GCC 8.2 及更高版本的所有版本中,它与 4 字节边界对齐:(Godbolt)

source # bytes
call 4
push ebp 4
sub esp, 16 16
push eax 4
push eax 4
push eax 4
Total 36

如果我们shortenraise callee 所需的参数数量,则很容易验证。

更改-mprefered-stack-boundary 会奇怪地将操作数更改为子指令,但不会更改实际的堆栈对齐方式:(Godbolt)

那么,呃,什么给了?

【问题讨论】:

    标签: c linux gcc x86 abi


    【解决方案1】:

    由于您在同一个翻译单元中提供了函数的定义,显然 GCC 认为该函数不关心堆栈对齐,也不会太在意它。显然,即使在-O0,这种基本的过程间分析/优化 (IPA) 也是默认开启的。

    当我搜索“ipa”选项in the manual 时,这个选项甚至有一个明显的名称:-fipa-stack-alignment 默认打开,即使在-O0。使用 -fno-ipa-stack-alignment 手动将其关闭会产生您所期望的结果,第二个 sub 的值取决于推送次数 (Godbolt),确保 ESP 在调用 like modern Linux versions of the i386 SysV ABI use 之前对齐 16。


    或者如果您将定义更改为仅声明,则生成的 asm 符合预期,完全尊重 -mpreferred-stack-boundary

    void callee(void* a, void* b) {
    }
    

    void callee(void* a, void* b);
    

    Using -fPIC also forces GCC to not assume anything about the callee, so it does respect the possibility of function interposition (e.g. via LD_PRELOAD) with the appropriate option.

    在不为共享库编译的情况下,允许 GCC 假定它看到的任何全局函数定义都是定义,这要归功于 ISO C 的单一定义规则。


    如果您在函数定义中使用__attribute__((noipa)),则调用站点将不会根据定义进行任何假设。就像你重命名了定义(所以你仍然可以查看它)并且只提供了调用者使用的名称的声明。

    如果您只是想停止内联,您可以改用__attribute__((noinline,noclone)),以仍然允许调用站点与优化器简单地选择不内联时一样,但仍然可以看到此定义。这可能是也可能不是你想要的。

    另请参阅How to remove "noise" from GCC/clang assembly output? re:编写有趣的 asm 函数以及编译器选项。


    顺便说一句,我发现将声明/定义更改为可变参数是最简单的,因此我可以添加或删除 args,只需更改调用者即可。即使push 数量随着额外的 arg 发生变化,我仍然能够重现您的 not 改变 sub 数量的结果,当有一个定义时,但不仅仅是一个声明。

    void callee(void* a, ...)  // {}   // comment out a body or not
    ;
    

    【讨论】:

    • @nickelpro:在查看 GCC 输出之前,我已经看到了其他 IPA 效果,所以我抢先猜测这是一个(以及在手册中搜索的名称)。
    猜你喜欢
    • 2017-01-04
    • 1970-01-01
    • 2014-03-11
    • 1970-01-01
    • 1970-01-01
    • 2011-02-15
    • 2010-10-24
    • 2010-10-14
    • 1970-01-01
    相关资源
    最近更新 更多