1. 现象
最近在 macos 上写编译器时遇到一个异常,生成的目标文件执行时报 segmentation fault。 用 lldb 调试跟踪最终在 movaps %xmm0, -0xa0(%rbp) 指令时报错:
stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
2. 分析
由上图可以看到是在调用 printf 函数时报错的,而操作 xmm0 xmm1 这些寄存器需要 rsp/rbp 16字节对齐,查看一下寄存器的值,果然是没有对齐。
经过仔细检查最后发现是一个汇编写的内部函数中没有把 rsp 对齐,加一条 push 即可解决问题。在 Windows + VS2022 上做了同样的测试,结果正常没有报异常,原因是 VC 编译器的 printf 函数实现不同,没有一上来就将 xmm 寄存器入栈。
3. 意外发现
在分析过程中发现调用 printf 函数之前 clang/gcc 编译器会写一条 mov al, #0 的指令:
但是调用其它函数却没有,分析 printf 的汇编发现有一条 test al, al 指令判断 al 是否为 0,是刚跳转,不是则 xmm0 ~ xmm7 入栈。
函数定义:int printf(const char * fmt, ...) ; 与普通函数声明相比也就是加了个不定参数,写一个函数测试一下看看,果然如此!
void func3(){
}
void func4(int a, ...){
}
至于这个 al 寄存器在这里的具体用法去研究一下 gcc/llvm 再说,目前我的编译器先不支持这个操作,回头再写它的具体用法。