13

真是奇怪的 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”有什么奇怪的关系吗?究竟发生了什么?

4

2 回答 2

14

你的猜测是正确的。这是一个“红色区域”。红色区域是从 rsp-128 到 rsp 的空间,它可能被函数用于局部变量和临时存储。该空间不受中断和异常处理程序的影响。很明显,红区是被函数调用破坏的,所以如果调用任何函数,红区就不能有局部变量。

红色区域只能用于 64 位 Linux、BSD 和 Mac。它在内核代码中不可用。

它可用于优化空间,因为在红色区域中,您可以使用短指令引用最多 512 字节的局部变量,仅基于 rsp 和 ebp。没有红色区域,只有 384 字节可用。超出此限制的所有局部变量都可以使用更长的代码或其他寄存器来访问。

对于您的示例,不需要使用红色区域,但 gcc 更喜欢将它用于所有“叶子”功能。以这种方式实现编译器更容易。

于 2011-12-01T13:08:22.327 回答
5

x86-64 ABI 要求在堆栈指针之外有一个 128 字节的“红色区域”,无需修改即可使用%rsp。在第一个例子中,main()是一个叶函数,所以编译器正在优化堆栈空间的使用——也就是说,没有函数调用,所以这个区域不会被覆盖。

于 2011-12-01T13:09:01.830 回答