x86-64架构下setjmp调用时jmp_buf需存的处理器状态问询
好问题!确实setjmp()的具体行为是依赖于实现的,但在x86-64架构下,主流类Unix系统(像Linux、BSD)都遵循System V AMD64 ABI规范,我们可以基于这个来明确哪些处理器状态会被存入jmp_buf,哪些不会。
需要存入
jmp_buf的处理器状态 - 被调用者保存寄存器:按照ABI规范,这些寄存器包括
%rbx、%rbp、%r12、%r13、%r14、%r15。因为调用者不会负责保存这些寄存器的值,setjmp必须把它们存进jmp_buf,否则longjmp跳转回来后,这些寄存器的原有值会被后续函数调用破坏。 - 栈指针寄存器
%rsp:栈的位置是程序执行的核心基础,必须准确恢复,否则后续的局部变量访问、函数调用都会直接出错。 - 程序计数器
%rip:也就是当前的指令执行地址,longjmp需要精准跳回setjmp第一次返回后的执行点,所以这个值必须保存。 - 浮点控制状态寄存器MXCSR:如果程序用到了SSE/AVX等浮点扩展,
setjmp会保存这个寄存器,它控制着浮点运算的舍入模式、异常掩码等关键状态,确保longjmp后的浮点运算行为一致。
不需要存入
jmp_buf的处理器状态 - 调用者保存寄存器:包括
%rax、%rcx、%rdx、%rsi、%rdi、%r8至%r11。这些寄存器的规则是“调用者自行保存”,调用setjmp的代码如果需要保留这些寄存器的值,会在调用前自行处理,所以setjmp不会把它们存入jmp_buf,longjmp回来后这些寄存器的值是不确定的,不能依赖。 - 临时浮点/向量寄存器:比如XMM0-XMM15、YMM0-YMM15等,这些同样属于调用者保存的范畴,
setjmp不会保存它们的状态,longjmp跳转后这些寄存器的值可能已经被修改,无法保证和setjmp调用时一致。 - EFLAGS寄存器的大部分标志位:除了和浮点状态相关的部分,像进位、溢出、零标志这些通用标志位,
setjmp不会保存。longjmp回来后这些标志位的状态是不可预测的,程序不应该依赖它们。 - 栈上的局部变量:
jmp_buf不会保存栈内存的内容,所以如果在setjmp之后调用了其他函数(栈帧被压入),或者栈的位置发生了变化,longjmp回来后栈上的局部变量值可能已经被覆盖,这也是为什么绝对不能在栈帧销毁后调用longjmp的原因。
额外说明:setjmp本身不会保存信号掩码,如果需要保存信号状态,应该使用sigsetjmp和siglongjmp。
内容的提问来源于stack exchange,提问作者gammaALpha
相关产品推荐
相关产品推荐

