编译器使用局部变量而不调整 RSP

Compiler using local variables without adjusting RSP

问题Compilers: Understanding assembly code generated from small programs编译器使用了两个局部变量而没有调整堆栈指针。

不调整RSP使用局部变量似乎中断不安全所以编译器似乎依赖硬件自动切换中断发生时写入系统堆栈。否则,出现的第一个中断会将指令指针压入堆栈并覆盖局部变量。

该问题的代码是:

#include <stdio.h>

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

该编译器生成的汇编代码是:

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    

局部变量是 i[rbp-0x8]k[rbp-0x4]

任何人都可以阐明这个中断问题吗?硬件是否确实切换到系统堆栈?如何?我的理解有误吗?

这就是所谓的"red zone"的x86-64 ABI. A summary from wikipedia:

In computing, a red zone is a fixed-size area in a function's stack frame beyond the current stack pointer which is not preserved by that function. The callee function may use the red zone for storing local variables without the extra overhead of modifying the stack pointer. This region of memory is not to be modified by interrupt/exception/signal handlers. The x86-64 ABI used by System V mandates a 128-byte red zone which begins directly under the current value of the stack pointer.

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


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

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 link.

观察到

对于 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 link.

观察到