macOS崩溃日志中EXC_BAD_ACCESS (SIGILL)是什么?Unity程序崩溃排查
首先明确:EXC_BAD_ACCESS (SIGILL) 不是单纯的内存访问错误,SIGILL代表CPU尝试执行非法指令,常见于iOS/macOS平台,下面是具体的排查方向:
原生插件架构或接口不匹配
这是最常见的原因:- 插件编译的CPU架构和目标设备不兼容,比如用x86_64编译的插件部署到arm64设备上,会直接触发非法指令。可以用
lipo -info [插件路径]查看插件支持的架构,确保和Unity PlayerSettings里勾选的一致。 - C#调用插件时参数、返回值类型不匹配,比如把C#的
int传给插件的float参数,或者插件返回的指针被C#错误转换,都会导致执行非法指令。务必逐行核对插件的C#绑定代码和原生代码的接口定义。
- 插件编译的CPU架构和目标设备不兼容,比如用x86_64编译的插件部署到arm64设备上,会直接触发非法指令。可以用
IL2CPP编译优化问题
开启Release模式的IL2CPP高优化时,编译器可能生成有问题的机器码,尤其是涉及:unsafe代码块的指针操作- 复杂泛型类型的内存布局
- 结构体的强制类型转换
排查时可以临时把PlayerSettings里的Optimization Level设为None,如果崩溃消失,说明是优化导致。找到对应的代码块,要么调整写法避免歧义,要么给该代码所在的类/方法加[Il2CppSetOption(Option.Optimization, 0)]属性单独禁用优化。
内存越界破坏代码段
虽然报错是SIGILL,但内存越界操作(比如数组越界写入、指针错误寻址)可能覆盖了程序代码段的内存,导致CPU执行被篡改后的非法指令。
排查时可以:- 启用Unity Memory Profiler,追踪异常的内存分配和写入操作
- 在Xcode中开启Address Sanitizer(Product -> Scheme -> Edit Scheme -> Diagnostics -> Address Sanitizer),运行程序时会自动检测内存越界并定位到代码行。
硬件指令集不兼容
如果代码或插件使用了目标设备不支持的CPU指令集扩展,比如在armv7设备上使用arm64专属指令,或者在老款Intel CPU上使用AVX指令,会触发SIGILL。
检查PlayerSettings里的CPU Architecture选项,只勾选目标设备支持的架构;如果是原生插件,确认编译时没有启用超出目标设备能力的指令集(比如Xcode里的Build Settings -> Other C Flags不要加-mavx这类选项)。托管对象被GC回收导致的原生代码错误
当C#把托管对象的指针传给原生代码后,如果该对象被GC回收,原生代码再访问这块内存时,内存可能已经被重新分配为其他数据(甚至是代码),进而触发非法指令。
解决方法是用GCHandle.Alloc(对象, GCHandleType.Pinned)固定托管对象,确保在原生代码使用期间不会被GC回收,用完后调用GCHandle.Free()释放。
内容的提问来源于stack exchange,提问作者Nikolai Vorobiev

