简体   繁体   中英

Size of local variable in assembly

I have following C function:

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

It produces following assembly code(gcc with 0 optimization, 64 bit machine):

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

Questions:

  • Why buffer occupies 20 bytes?
  • 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).

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. It's 4 bytes long, leaving 16 bytes of space above it below RSP.

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.

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). But it's definitely not 20 because there isn't that much space left. Unless it put buffer[] below a , somewhere else in the rest of the 128-byte red-zone. (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 :

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.

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.

I don't know why gcc leaves extra padding before the spilled register arg; gcc often has that kind of missed optimization. It's not giving a any special alignment.

If you add extra local arrays, they will fill up that 16 bytes above the spilled arg, still spilling it to -20(%rbp) . (See function2 in the Godbolt link)

I also included clang -O0 , and icc -O3 and MSVC optimized output, in the Godbolt link. Fun fact: ICC chooses to optimize away volatile char buffer[1] = {'x'}; without actually storing to memory, but MSVC allocates it in the shadow space. (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.)

clang/LLVM -O0 chooses to spill a right below RSP, and put the array 1 byte below that.


With just char buffer instead of char buffer[1]

We get movl %edi, -4(%rbp) # a, a from gcc -O0 . It apparently optimizes away the unused and uninitialized local variable entirely, and spills a right below the saved RBP. (I didn't run it under GDB or look at the debug info to see if &buffer would give us.)

So again, you're mixing up a with buffer .

If we initialize it with char buffer = 'x' , we're back to the old stack layout, with buffer at -1(%rbp) .

Or even if we just make it 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 .

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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