编译器使用局部变量而不调整 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.
观察到
问题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.
观察到