如何让GCC恢复函数中使用的$rdx寄存器?汇编与C混合编译的寄存器未恢复问题问询
解答你的汇编与C代码编译问题
先帮你理清两个核心疑问,再处理链接警告的问题:
疑问1:为什么stringEqual中使用了rdx却未恢复?
这是对x86-64 System V调用约定的误解。在这个约定里,寄存器被明确分成两类:
- 被调用者保存寄存器(Callee-saved):
rbp、rbx、r12-r15,被调用函数必须保证这些寄存器的值在返回时和进入时一致(你看到stringEqual开头push %rbp、结尾leave恢复rbp,这完全符合规范)。 - 调用者保存寄存器(Caller-saved):
rax、rdi、rsi、rdx、rcx、r8、r9,这类寄存器被调用函数可以随意修改,不需要恢复。如果调用者需要保留这些寄存器的值,必须在调用函数前自行保存(比如push到栈上)。
你的stringEqual里rdx属于调用者保存寄存器,所以它完全不需要恢复rdx的值。真正的问题出在你的汇编代码prog.asm里:调用stringEqual前,你把checked_malloc的返回值存在了rdx里,但调用stringEqual后,rdx的值被破坏了(因为stringEqual内部调用的strcmp也会修改rdx),导致后续代码使用rdx时拿到错误值。
疑问2:如何设置GCC让它恢复函数内部使用的寄存器?
其实不需要额外设置GCC——GCC生成的代码已经严格遵循x86-64调用约定了。你需要修正的是汇编代码里的调用者逻辑:在调用stringEqual前,把需要保留的rdx寄存器值保存到栈上,调用完成后再恢复。比如修改prog.asm:
; 在调用stringEqual前保存rdx push rdx mov rsi, [rdx + 0] call stringEqual ; 调用完成后恢复rdx pop rdx
处理链接警告
你遇到的两个链接警告和寄存器问题无关,是PIE(位置无关可执行文件)相关的:
relocation in read-only section '.text'和creating DT_TEXTREL in a PIE:你的汇编代码里使用了绝对地址寻址(比如mov rdi, L11),而GCC默认生成PIE,这类绝对寻址会导致只读的.text段需要重定位,从而触发警告。
解决方法有两种:
方法1:禁用PIE编译
修改编译命令,添加-no-pie参数:
gcc prog.o runtime.c -g -no-pie -o run.out
生成非位置无关的可执行文件后,绝对地址寻址就不会触发警告了。
方法2:修改汇编代码使用RIP相对寻址
把汇编里的绝对地址引用改成RIP相对的形式,比如:
; 把 mov rdi, L11 改成 lea rdi, [rip + L11] ; 同理,mov rdi, L12 改成 lea rdi, [rip + L12] ; mov rdi, L13 改成 lea rdi, [rip + L13]
这种方式符合PIE的要求,不会产生重定位警告。
内容的提问来源于stack exchange,提问作者Shore
相关产品推荐
相关产品推荐

