为什么自制编译器生成的x86_64汇编代码会触发段错误?
问题分析
- 核心错误是违反了x86_64 macOS平台的函数调用栈对齐规则
macOS遵循System V x86_64调用约定,要求调用任意函数前,栈指针%rsp必须16字节对齐。进入_main函数时,callq _main自动压入8字节返回地址,随后执行pushq %rbp又压入8字节,此时%rsp已经16字节对齐,后续subq $16, %rsp操作后仍保持对齐。但你额外执行了pushq %rdi,这一步会让%rsp再减8,直接破坏了16字节对齐要求,此时调用_strcmp触发系统层面的对齐检查,直接抛出段错误。 - 次要遗漏点:变参函数
printf调用前需要设置%rax值
System V约定中,调用可变参数函数前,%rax需要存放传入的向量寄存器数量,你这段代码没有用到向量寄存器,需要在调用_printf前执行xorl %eax, %eax将%rax置0,否则也可能引发不可预知的运行错误。
修复方案
方案1:保留压栈逻辑,补充对齐操作
如果要保留你原本压栈保存格式串地址的逻辑,只需要在压栈后补充栈对齐操作即可:
leaq S0(%rip), %rax movq %rax, %rdi pushq %rdi subq $8, %rsp # 补充8字节偏移,让%rsp恢复16字节对齐 leaq S1(%rip), %rax movq %rax, %rdi leaq S2(%rip), %rax movq %rax, %rsi callq _strcmp addq $8, %rsp # 调用结束后回补8字节 popq %rdi movq %rax, %rsi xorl %eax, %eax # 补充变参函数要求的rax置0 callq _printf
方案2:移除不必要的压栈操作,调用printf前重新加载格式串
这也是你测试过可用的方案,相比方案1指令更少、执行效率更高,也不会引入栈对齐风险:
leaq S1(%rip), %rdi leaq S2(%rip), %rsi callq _strcmp leaq S0(%rip), %rdi movq %rax, %rsi xorl %eax, %eax callq _printf
内容的提问来源于stack exchange,提问作者Dovahkiin
相关产品推荐
相关产品推荐

