c - 程序集中局部变量的大小

标签 c gcc assembly x86-64 word-size

我有以下 C 函数:

void function(int a) {
  char buffer[1];
}

它产生以下汇编代码(gcc 0 优化,64 位机器):

function:
  pushq %rbp
  movq  %rsp, %rbp
  movl  %edi, -20(%rbp)
  nop
  popq  %rbp
  ret

问题:

  • 为什么缓冲区占用20个字节?
  • 如果我声明 char buffer 而不是 char buffer[1] 偏移量是 4 个字节,但我希望看到 8 个字节,因为机器是 64 位的,我认为它将使用 qword(64 位)。

提前致谢,如果问题重复,我很抱歉,我找不到答案。

最佳答案

movl %edi, -20(%rbp) 将函数 arg 从寄存器溢出到堆栈指针下方的红色区域。它有 4 个字节长,在其上方的 RSP 下方留有 16 个字节的空间。

gcc 的 -O0(朴素的反优化)代码生成函数实际上并没有触及它为 buffer[] 保留的内存,所以你不需要不知道它在哪里。

你不能推断 buffer[] 正在用完红色区域中 a 上面的所有 16 个字节,只是 gcc 在有效打包本地人方面做得不好(因为你用 -O0 编译所以它甚至没有尝试)。但它绝对不是 20,因为没有那么多空间了。除非它把 buffer[] 放在 a 中,在 128 字节红色区域的其余部分的其他地方。 (提示:它没有。)


如果我们为数组添加一个初始值设定项,我们可以看到它实际存储字节的位置。

void function(int a) {
  volatile char buffer[1] = {'x'};
}

由 gcc8.2 编译 -xc -O0 -fverbose-asm -Wall on the Godbolt compiler explorer :

function:
    pushq   %rbp
    movq    %rsp, %rbp               # function prologue, creating a traditional stack frame

    movl    %edi, -20(%rbp) # a, a

    movb    $120, -1(%rbp)  #, buffer

    nop                             # totally useless, IDK what this is for
    popq    %rbp                    # tear down the stack frame
    ret     

所以 buffer[] 实际上是一个字节长,正好在保存的 RBP 值之下。

x86-64 System V ABI 要求对至少 16 字节长的自动存储阵列进行 16 字节对齐,但此处情况并非如此,因此该规则不适用。

我不知道为什么 gcc 在溢出的寄存器 arg 之前留下额外的填充; gcc 经常有这种错过的优化。它没有给 a 任何特殊对齐方式。

如果您添加额外的本地数组,它们将填满溢出 arg 上方的 16 个字节,并将其溢出到 -20(%rbp)。 (请参阅 Godbolt 链接中的 function2)

我还在 Godbolt 链接中包含了 clang -O0icc -O3 和 MSVC 优化输出。有趣的事实:ICC 选择优化掉 volatile char buffer[1] = {'x'}; 而不是实际存储到内存中,但 MSVC 将其分配在影子空间中。 (Windows x64 使用不同的调用约定,返回地址上方有 32B 的阴影空间,而不是堆栈指针下方的 128B 红色区域。)

clang/LLVM -O0 选择将 a 溢出到 RSP 正下方,并将数组放在其下方 1 个字节处。


使用 just char buffer 而不是 char buffer[1]

我们从 gcc -O0 得到 movl %edi, -4(%rbp) # a, a。它显然完全优化了未使用和未初始化的局部变量,并在保存的 RBP 正下方溢出 a。 (我没有在 GDB 下运行它,也没有查看调试信息以查看 &buffer 是否会给我们。)

所以,您再次混淆了 abuffer

如果我们用 char buffer = 'x' 初始化它,我们会回到旧的堆栈布局,buffer-1 (%rbp)

或者即使我们只是让它成为 volatile char buffer; 而没有初始化器,那么它的空间存在于堆栈上并且 a 被溢出到 -20 (%rbp) 即使没有对 buffer 进行存储。

关于c - 程序集中局部变量的大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53907745/

相关文章:

c - 在 C 程序中禁用中断

c - printf中栈的使用; gcc 未指定的行为

c - 将 ARM asm 反编译回 C

c - 你为什么用汇编编程?

c - 如何在不终止脚本的情况下终止 bash 脚本中正在运行的进程?

c - 将值附加到动态数组的末尾

c - gcc 内置的 __atomic 或 __sync 函数无法提供对全局变量的一致访问

C++11: g++-4.7 内部编译器错误

linux - gcc make 安装错误

assembly - assembly 中的双重条件检查