r11寄存器出现异常行为的技术排查请求
问题重现
你编写了用于打印寄存器状态的汇编调试代码,测试逻辑如下:
call _getDebugString mov r9, debugString call _printTextSafe call _getDebugString mov r9, debugString call _printTextSafe mov r11, -24 call _getDebugString mov r9, debugString call _printTextSafe call _getDebugString mov r9, debugString call _printTextSafe call _exit
执行后发现r11寄存器每次在_printTextSafe调用后被修改为582,输出结果:
rax = 1 rbx = -2 rcx = 3 rdx = -4 rsi = 5 rdi = -6 r8 = 7 r9 = -8 r10 = 9 r11 = 0 r12 = 11 r13 = -12 r14 = 13 r15 = -14
rax = 1 rbx = -2 rcx = 3 rdx = -4 rsi = 5 rdi = -6 r8 = 7 r9 = 4202678r10 = 9 r11 = 582 r12 = 11 r13 = -12 r14 = 13 r15 = -14
rax = 1 rbx = -2 rcx = 3 rdx = -4 rsi = 5 rdi = -6 r8 = 7 r9 = 4202678r10 = 9 r11 = -24 r12 = 11 r13 = -12 r14 = 13 r15 = -14
rax = 1 rbx = -2 rcx = 3 rdx = -4 rsi = 5 rdi = -6 r8 = 7 r9 = 4202678r10 = 9 r11 = 582 r12 = 11 r13 = -12 r14 = 13 r15 = -14
原因分析
- r11是x86-64调用约定中的易失性寄存器:在System V AMD64(Linux/macOS通用)调用约定里,r11属于调用者保存寄存器——被调用函数(比如
_printTextSafe)无需保存和恢复r11的值。如果你的代码需要在调用其他函数后保留r11的内容,必须由你自己在调用前将r11压栈保存,调用完成后再出栈恢复。 _printTextSafe内部的syscall指令会直接修改r11:如果_printTextSafe底层调用了系统调用(如write),syscall指令执行时会将当前RFLAGS寄存器的值存入r11,这个过程会直接覆盖r11原有内容。你看到的582就是调用syscall前RFLAGS的十进制数值。- 测试流程遗漏了r11的保存步骤:从输出可以看到,直接修改r11后调用
_getDebugString,输出的r11值是正确的(-24),说明_getDebugString确实正确保存了它自身用到的寄存器;但调用_printTextSafe后,r11被修改,而你没有在调用_printTextSafe前保存r11,因此再次调用_getDebugString时,r11已经是被修改后的数值。
另外,r9寄存器同样被修改,这也符合调用约定——r9作为第3个参数寄存器,同样属于易失性寄存器,_printTextSafe会随意使用它而不恢复。
解决方法
- 在调用
_printTextSafe前,手动保存需要保留的易失性寄存器,调用后恢复:call _getDebugString push r11 ; 保存r11 push r9 ; 若需要保留r9也可保存 mov r9, debugString call _printTextSafe pop r9 ; 恢复r9 pop r11 ; 恢复r11 call _getDebugString - 不推荐修改
_printTextSafe让它保存恢复r11,这会违反x86-64调用约定的规范,导致代码兼容性问题。
内容的提问来源于stack exchange,提问作者MindW1n

