You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ARM macOS下显式调用raise()触发信号时栈返回地址乱码问题排查

ARM macOS上raise()触发信号时栈回溯地址乱码的原因与解决办法

你碰到的这个ARM macOS上的栈回溯乱码问题,本质是苹果的安全机制在搞事情——raise()函数触发信号时会偷偷修改栈里的返回地址,加了个高位的混淆标记,而你的手动回溯逻辑没处理这个标记,所以就出现了奇怪的乱码地址。

为啥raise()会搞出这个问题?

在ARM64的macOS里,raise()为了抵御ROP攻击(一种常见的内存漏洞利用方式),会在触发信号前做个小动作:

  • 它给返回地址的高16位塞了一个随机的“混淆值”(就是你看到的0x116a000、0x8f6a000这类前缀)
  • 这个操作是在用户态完成的,只有像raise()这种主动触发信号的函数才会这么干
  • 但你用访问空指针触发的段错误不一样,那是硬件直接抛出的异常,栈里的返回地址完全是原生的,没被篡改过

你的回溯代码哪里漏了?

你手动遍历帧指针的代码直接把被篡改的返回地址读出来用了:

programCounter = *(uint64_t*)(framePointer + 8);

这个地址的高16位是无效的混淆值,传给backtrace_symbols()后,自然就解析出乱码了(比如0x116a000185422e14其实是正确地址0x185422e14加了高16位的混淆前缀)。

怎么解决?

很简单,把高16位的混淆值去掉就行。ARM64的有效地址只需要低48位,所以用0x0000FFFFFFFFFFFF这个掩码做按位与操作,就能得到正确的返回地址:

programCounter = *(uint64_t*)(framePointer + 8) & 0x0000FFFFFFFFFFFF;

改完之后,你的回溯代码就能正确识别地址,符号化的结果也就正常了。

额外补充几个细节

  • 这个高16位的混淆标记是苹果Stack Pointer Guard安全机制的一部分,专门针对用户态主动发信号的场景
  • 系统自带的backtrace()函数内部已经处理了这个标记,所以如果你直接调用backtrace(bt, 100)而不是手动遍历帧指针,也能拿到正确的调用栈
  • 不管你触发的是SIGSEGV还是其他信号,只要是通过raise()这类用户态函数触发的,都会有这个地址篡改的行为,所以你才会看到所有信号都有同样的问题

内容的提问来源于stack exchange,提问作者Donpedro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 10:27:47