简体   繁体   English

汇编中局部变量的大小

[英]Size of local variable in assembly

I have following C function: 我有以下C函数:

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

It produces following assembly code(gcc with 0 optimization, 64 bit machine): 它产生以下汇编代码(优化为0的gcc,64位计算机):

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

Questions: 问题:

  • Why buffer occupies 20 bytes? 为什么缓冲区占用20个字节?
  • If I declare char buffer instead of char buffer[1] the offset is 4 bytes, but I expected to see 8, since machine is 64 bit and I thought it will use qword(64 bit). 如果我声明使用char buffer而不是char buffer[1]则偏移量是4个字节,但是我希望看到8,因为计算机是64位的,我认为它将使用qword(64位)。

Thanks in advance and sorry if question is duplicated, I was not able to find the answer. 在此先感谢您,如果问题重复,对不起,我找不到答案。

movl %edi, -20(%rbp) is spilling the function arg from a register into the red-zone below the stack pointer. movl %edi, -20(%rbp)正在将函数arg从寄存器溢出到堆栈指针下方的红色区域中。 It's 4 bytes long, leaving 16 bytes of space above it below RSP. 它的长度为4个字节,在RSP下方留有16个字节的空间。

gcc's -O0 (naive anti-optimized) code-gen for you function doesn't actually touch the memory it reserved for buffer[] , so you don't know where it is. gcc的-O0 (天真反优化)代码源为您提供的功能实际上并没有触摸它为buffer[]保留的内存,因此您不知道它在哪里。

You can't infer that buffer[] is using up all 16 bytes above a in the red zone, just that gcc did a bad job of packing locals efficiently (because you compiled with -O0 so it didn't even try). 您不能推断出buffer[]在红色区域中的a上方占用了全部16个字节,只是gcc不能有效地打包本地-O0 (因为您使用-O0编译,因此甚至都没有尝试)。 But it's definitely not 20 because there isn't that much space left. 但这绝对不是20,因为空间不多了。 Unless it put buffer[] below a , somewhere else in the rest of the 128-byte red-zone. 除非将buffer[] 放在 a 之下 ,否则在128字节红色区域的其余部分中。 (Hint: it didn't.) (提示:没有。)


If we add an initializer for the array, we can see where it actually stores the byte. 如果我们为数组添加一个初始化程序,我们可以看到它实际存储该字节的位置。

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

compiled by gcc8.2 -xc -O0 -fverbose-asm -Wall on the Godbolt compiler explorer : 由gcc8.2 -xc -O0 -fverbose-asm -Wall 在Godbolt编译器浏览器上编译

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     

So buffer[] is in fact one byte long, right below the saved RBP value. 因此, buffer[]实际上是一个字节长, 在保存的RBP值的下方。

The x86-64 System V ABI requires 16-byte alignment for automatic storage arrays that are at least 16 bytes long, but that's not the case here so that rule doesn't apply. 对于长度至少为16个字节的自动存储阵列,x86-64 System V ABI要求16个字节的对齐方式,但这不是这种情况,因此该规则不适用。

I don't know why gcc leaves extra padding before the spilled register arg; 我不知道为什么gcc在溢出的寄存器arg之前留下了多余的填充。 gcc often has that kind of missed optimization. gcc经常会错过这种优化。 It's not giving a any special alignment. 它不给a什么特殊的定位。

If you add extra local arrays, they will fill up that 16 bytes above the spilled arg, still spilling it to -20(%rbp) . 如果添加额外的本地数组,它们将在溢出的arg上方填充16个字节,仍然将其溢出到-20(%rbp) (See function2 in the Godbolt link) (见function2在Godbolt链接)

I also included clang -O0 , and icc -O3 and MSVC optimized output, in the Godbolt link. 我还在Godbolt链接中包括了clang -O0icc -O3以及MSVC优化的输出。 Fun fact: ICC chooses to optimize away volatile char buffer[1] = {'x'}; 有趣的事实:ICC选择优化掉volatile char buffer[1] = {'x'}; without actually storing to memory, but MSVC allocates it in the shadow space. 而不实际存储到内存中,但是MSVC在影子空间中分配它。 (Windows x64 uses a different calling convention, and has 32B shadow space above the return address instead of a 128B red zone below the stack pointer.) (Windows x64使用不同的调用约定,并且返回地址上方具有32B阴影空间,而不是堆栈指针下方的128B红色区域。)

clang/LLVM -O0 chooses to spill a right below RSP, and put the array 1 byte below that. 铛/ LLVM -O0选择溢出a下面RSP右,并把以下所述阵列1个字节。


With just char buffer instead of char buffer[1] 仅使用 char buffer而不是char buffer[1]

We get movl %edi, -4(%rbp) # a, a from gcc -O0 . 我们从gcc -O0得到movl %edi, -4(%rbp) # a, a It apparently optimizes away the unused and uninitialized local variable entirely, and spills a right below the saved RBP. 显然,它完全优化了未使用和未初始化的局部变量,并在保存的RBP下方溢出a右键。 (I didn't run it under GDB or look at the debug info to see if &buffer would give us.) (我没有在GDB下运行它,也没有查看调试信息以查看&buffer是否会给我们。)

So again, you're mixing up a with buffer . 再说一次,您要混合a with和buffer

If we initialize it with char buffer = 'x' , we're back to the old stack layout, with buffer at -1(%rbp) . 如果我们用char buffer = 'x'初始化它,我们回到旧的堆栈布局, buffer-1(%rbp)

Or even if we just make it volatile char buffer; 或者,即使我们只是将其设置为volatile char buffer; without an initializer, then space for it exists on the stack and a is spilled to -20(%rbp) even with no store done to buffer . 如果没有初始化程序,则堆栈上将存在用于其的空间,即使不进行buffer存储, a也会溢出到-20(%rbp)

4个字节对齐的字符,8个字节推RBP,8个字节a所述的= 20开始ADDRES a是当前堆栈指针减20

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM