[英]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: 问题:
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 -O0
和icc -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个字节。
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.