编译器使用局部变量而不调整 RSP [英] Compiler using local variables without adjusting RSP
问题描述
有问题的编译器:了解从小程序编译器使用两个局部变量,不调整栈指针.
In question Compilers: Understanding assembly code generated from small programs the compiler uses two local variables without adjusting the stack pointer.
不调整RSP以使用局部变量似乎中断安全,因此编译器似乎依赖硬件在发生中断时自动切换到系统堆栈.否则,出现的第一个中断会将指令指针压入堆栈并覆盖局部变量.
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.
那个问题的代码是:
#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
局部变量是 [rbp-0x8]
处的 i
和 [rbp-0x4]
处的 k
.
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?
推荐答案
这就是 x86-64 ABI.维基百科的摘要:
在计算中,红色区域是函数堆栈帧中超出当前堆栈指针的固定大小区域,该区域未由该函数保留.被调用函数可以使用红色区域来存储局部变量无需修改堆栈指针的额外开销.这个内存区域不会被中断/异常/信号处理程序修改.System V 使用的 x86-64 ABI 规定了一个 128 字节的红色区域,该区域直接在堆栈指针的当前值下方开始.
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 个字节即可.这是叶函数最突出使用的优化,即不调用其他函数的函数,
In 64-bit Linux user code it is OK, as long as no more than 128 bytes are used. It is an optimization used most prominently by leaf-functions, i.e. functions which don't call other functions,
如果您使用 -mno-red-zone
选项将示例程序编译为带有 GCC(或兼容编译器)的 64 位 Linux 程序,您d 看到这样生成的代码:
If you were to compile the example program as a 64-bit Linux program with GCC (or compatible compiler) using the -mno-red-zone
option you'd see code like this generated:
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
将显示如下代码:
For a 32-bit Linux user program it would be a bad thing not to adjust the stack pointer. If you were to compile the code in the question as 32-bit code (using -m32
option) main
would appear something like the following code:
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 链接.
这篇关于编译器使用局部变量而不调整 RSP的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!