Pau*_*vie 9 c linux x86 assembly x86-64
问题编译器:了解从小程序生成的汇编代码,编译器使用两个局部变量而不调整堆栈指针.
不调整RSP以使用局部变量似乎不会中断安全,因此编译器似乎依赖于硬件在发生中断时自动切换到系统堆栈.否则,出现的第一个中断会将指令指针推入堆栈并覆盖本地变量.
该问题的代码是:
#include <stdio.h>
int main()
{
for(int i=0;i<10;i++){
int k=0;
}
}
Run Code Online (Sandbox Code Playgroud)
该编译器生成的汇编代码是:
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
Run Code Online (Sandbox Code Playgroud)
局部变量i
位于[rbp-0x8]
和k
处[rbp-0x4]
.
谁能照亮这个中断问题呢?硬件确实切换到系统堆栈吗?怎么样?我理解错了吗?
ead*_*ead 10
这就是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
Run Code Online (Sandbox Code Playgroud)
可以在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
Run Code Online (Sandbox Code Playgroud)
可以在此gotbolt.org链接中观察到此代码生成.
归档时间: |
|
查看次数: |
330 次 |
最近记录: |