为何syscall指令会破坏rcx与r11寄存器?相关疑问解析
x86-64系统调用中rcx/r11被破坏的常见疑问解答
先给大家铺垫两个权威来源的信息:
来自《Low-Level Programming: C, Assembly, and Program Execution on Intel® 64 Architecture》的说明:
系统调用参数说明
系统调用的参数存储寄存器与函数调用不同:第四个参数存在r10中,而函数调用的第四个参数存于rcx中!原因是syscall指令会隐式使用rcx,且系统调用最多接受6个参数。
另外还有Stack Overflow上的权威回答:
系统调用通过
syscall指令实现,该指令会破坏%rcx、%r11以及%rax寄存器,其余寄存器则会被保留。
接下来咱们逐个拆解你的疑问:
为什么syscall指令会破坏rcx和r11?
这是x86-64架构硬件层面的硬规定,和操作系统无关:
- 执行
syscall时,CPU会自动把当前的rip(也就是系统调用完成后要回到用户态继续执行的指令地址)存入rcx——内核需要这个地址来完成返回跳转,这一步是硬件强制做的,所以rcx必然被覆盖。 - 同时,CPU会把当前的
rflags(标志寄存器,记录着比如进位、零标志这些状态)存入r11,内核处理完系统调用后要恢复用户态的标志位,所以r11也会被硬件自动改写。 - 至于
rax,它本来就是用来传系统调用号的,返回时又用来存返回值,被覆盖是大家都默认的约定。
是否有破坏rcx/r11的特定系统调用列表?
完全没有!因为这是syscall指令本身的硬件行为,不管你调用哪个系统调用,只要用了syscall触发,rcx和r11都会被破坏。操作系统根本没法干预这个硬件机制——不管是read、write这种基础调用,还是更复杂的系统调用,只要走syscall入口,这俩寄存器肯定保不住。
这种破坏是否有相关约定?
必须有,这是x86-64系统调用ABI(应用二进制接口)里写得明明白白的规则:
- 用户态程序在调用系统调用前,必须默认
rcx、r11、rax会被破坏,如果这两个寄存器里有需要保留的数据,一定要在执行syscall之前先把它们压栈保存好。 - 其他通用寄存器(比如
rbx、rbp、r12到r15这些)则会被内核完整保留,系统调用结束后它们的值和调用前完全一致,这也是ABI的硬性约定。
是否存在不会破坏它们的系统调用?
不存在。只要是通过syscall指令触发的系统调用,这两个寄存器都会被硬件自动覆盖。不过有个非主流的例外:如果你用老式的32位系统调用入口int 0x80(x86-64下也能兼容使用),它不会破坏rcx和r11,但这种方式效率极低,而且传参规则和syscall完全不同,现在几乎没人用了。
内容的提问来源于stack exchange,提问作者Evan Carroll
相关产品推荐
相关产品推荐

