真的很奇怪的 gcc 怪癖。看看这个:
main() { int a[100]; a[0]=1; }
产生这个程序集:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 81 ec 18 01 00 00 sub $0x118,%rsp
b: c7 85 70 fe ff ff 01 movl $0x1,-0x190(%rbp)
12: 00 00 00
15: c9 leaveq
16: c3 retq
堆栈的顶部显然是 400,因为它是一个 100 * 4 的数组。因此,当它写入第一个条目时,它会执行 rbp - 400(“b”行)。很好。但是为什么它从堆栈(第 4 行)指针中减去 280?那不是指向数组的中间吗?
如果我们之后添加一个函数调用,gcc 会做正确的事情:
b() {}
main() { int a[100]; a[0]=1; b(); }
产生这个程序集:
0000000000000000 <b>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: c9 leaveq
5: c3 retq
0000000000000006 <main>:
6: 55 push %rbp
7: 48 89 e5 mov %rsp,%rbp
a: 48 81 ec 90 01 00 00 sub $0x190,%rsp
11: c7 85 70 fe ff ff 01 movl $0x1,-0x190(%rbp)
18: 00 00 00
1b: b8 00 00 00 00 mov $0x0,%eax
20: e8 00 00 00 00 callq 25 <main+0x1f>
25: c9 leaveq
26: c3 retq
在这里,它正确地减去了 400(“a”行)。
添加函数调用时为什么会发生变化? gcc 只是懒惰,因为无关紧要而做得不对吗?发生什么事了?显然,这只发生在为 x86_64 编译时,而不是为普通的 x86 编译时。这与 x86_64 的“redzone”有什么奇怪的关系吗?究竟发生了什么?
最佳答案
你的猜测是正确的。这是一个“红区”。红色区域是从 rsp-128 到 rsp 的空间,它可以被函数用于局部变量和临时存储。该空间不受中断和异常处理程序的影响。很明显,红区是被函数调用破坏的,所以如果调用了任何函数,红区是不能有局部变量的。
红色区域只能在 64 位 Linux、BSD 和 Mac 上使用。它在内核代码中不可用。
它可用于优化空间,因为在红色区域中,您可以使用短指令引用最多 512 字节的局部变量,仅基于 rsp 和 ebp。如果没有红色区域,则只有 384 个字节可用。所有超出此限制的局部变量都可以使用更长的代码或附加寄存器进行访问。
对于您的示例,不需要使用红色区域,但 gcc 更喜欢将它用于所有“叶”功能。以这种方式实现编译器更容易。
关于gcc - 为什么 GCC 在分配一个没有后续函数调用的大数组时将错误的值减去堆栈指针?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8337783/