12

In question Compilers: Understanding assembly code generated from small programs the compiler uses two local variables without adjusting the stack pointer.

Not adjusting RSP for the use of local variables seems not interrupt safe and so the compiler seems to rely on the hardware automatically switching to a system stack when interrupts occur. Otherwise, the first interrupt that came along would push the instruction pointer onto the stack and would overwrite the local variable.

The code from that question is:

#include <stdio.h>

int main()
{
    for(int i=0;i<10;i++){
        int k=0;
    }
}

The assembly code generated by that compiler is:

00000000004004d6 <main>:
  4004d6:       55                      push   rbp
  4004d7:       48 89 e5                mov    rbp,rsp
  4004da:       c7 45 f8 00 00 00 00    mov    DWORD PTR [rbp-0x8],0x0
  4004e1:       eb 0b                   jmp    4004ee <main+0x18>
  4004e3:       c7 45 fc 00 00 00 00    mov    DWORD PTR [rbp-0x4],0x0
  4004ea:       83 45 f8 01             add    DWORD PTR [rbp-0x8],0x1
  4004ee:       83 7d f8 09             cmp    DWORD PTR [rbp-0x8],0x9
  4004f2:       7e ef                   jle    4004e3 <main+0xd>
  4004f4:       b8 00 00 00 00          mov    eax,0x0
  4004f9:       5d                      pop    rbp
  4004fa:       c3                      ret    

The local variables are i at [rbp-0x8] and k at [rbp-0x4].

Can anyone shine light on this interrupt problem? Does the hardware indeed switch to a system stack? How? Am I wrong in my understanding?

4

1 回答 1

14

这是x86-64 ABI的所谓“红色区域” 。来自维基百科的摘要:

在计算中,红色区域是函数堆栈帧中超出当前堆栈指针的固定大小区域,该函数不保留该区域。被调用函数可以使用红色区域来存储局部变量,而无需修改堆栈指针的额外开销。这个内存区域不能被中断/异常/信号处理程序修改。System V 使用的 x86-64 ABI 要求有一个 128 字节的红色区域,该区域直接从堆栈指针的当前值开始。

在 64 位 Linux 用户代码中是可以的,只要使用不超过 128 个字节。它是叶函数最突出使用的优化,即不调用其他函数的函数,


如果您使用GCC(或兼容的编译器)选项将示例程序编译为 64 位 Linux 程序,-mno-red-zone您会看到生成的代码如下:

main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16;     <<============  Observe RSP is now being adjusted.
        mov     DWORD PTR [rbp-4], 0
.L3:
        cmp     DWORD PTR [rbp-4], 9
        jg      .L2
        mov     DWORD PTR [rbp-8], 0
        add     DWORD PTR [rbp-4], 1
        jmp     .L3
.L2:
        mov     eax, 0
        leave
        ret

此代码生成可以在此godbolt.org链接中观察到。


对于 32 位 Linux 用户程序,不调整堆栈指针将是一件坏事。如果您要将问题中的代码编译为 32 位代码(使用-m32选项)main,则会出现类似于以下代码的内容:

main:
        push    ebp
        mov     ebp, esp
        sub     esp, 16;     <<============  Observe ESP is being adjusted.
        mov     DWORD PTR [ebp-4], 0
.L3:
        cmp     DWORD PTR [ebp-4], 9
        jg      .L2
        mov     DWORD PTR [ebp-8], 0
        add     DWORD PTR [ebp-4], 1
        jmp     .L3
.L2:
        mov     eax, 0
        leave
        ret

此代码生成可在此gotbolt.org链接中查看。

于 2017-03-25T08:20:54.257 回答