c - 堆栈分配、填充和对齐

标签 c gcc assembly x86 stack

我一直在努力更深入地了解编译器如何生成机器代码,更具体地说,GCC 如何处理堆栈。在这样做的过程中,我一直在编写简单的 C 程序,将它们编译成汇编,并尽我最大的努力来理解结果。这是一个简单的程序及其生成的输出:

asmtest.c:

void main() {
    char buffer[5];
}

asmtest.s:

pushl   %ebp
movl    %esp, %ebp
subl    $24, %esp
leave
ret

令我困惑的是为什么要为堆栈分配 24 个字节。我知道由于处理器寻址内存的方式,堆栈必须以 4 为增量进行分配,但如果是这种情况,我们应该只将堆栈指针移动 8 个字节,而不是 24 个字节。作为引用,缓冲区为 17 bytes 产生一个堆栈指针移动 40 个字节,并且根本没有缓冲区移动堆栈指针 8。1 到 16 个字节之间的缓冲区移动 ESP 24 个字节。

现在假设 8 个字节是一个必要的常量(需要它做什么?),这意味着我们正在分配 16 个字节的 block 。为什么编译器会以这种方式对齐?我使用的是 x86_64 处理器,但即使是 64 位字也只需要 8 字节对齐。为什么会出现差异?

作为引用,我在运行 10.5 和 gcc 4.0.1 且未启用优化的 Mac 上编译它。

最佳答案

这是一个由 -mpreferred-stack-boundary=n 控制的 gcc 功能,其中编译器试图使堆栈上的项目与 2^n 对齐。如果将 n 更改为 2,它只会在堆栈上分配 8 个字节。 n 的默认值为 4 即它会尝试对齐到 16 字节边界。

为什么会有“默认”8字节然后24=8+16字节是因为堆栈已经包含8字节用于leaveret,所以编译后的代码必须首先将堆栈调整 8 个字节以使其对齐到 2^4=16。

关于c - 堆栈分配、填充和对齐,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43864172/

相关文章:

c++ - makefile中汇编文件的生成

c++ - [[可能]] 和 [[不太可能]] 影响程序汇编的简单示例?

c - 在汇编程序中分支到标签时 2f 意味着什么?

c - 将数据包从一台linux服务器发送到另一台服务器(LINUX)

c++ - 将 C 风格的 pretty-print 重构为 C++ 风格的 pretty-print

c - 这个增量和减量分配试图做什么?

c++ - 未定义对 `__stack_chk_fail' 的引用

assembly - 使用 gdb 和 QEMU 调试引导加载程序/BIOS 时如何跳过中断调用?

assembly - 从堆栈中弹出而不压入

c# - 什么会导致内存泄漏(或不断增加)?共享内存 DLL + C# 应用程序