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

Mac arm64下AddressSanitizer仅显示同文件堆栈跟踪问题咨询

问题现象

从Intel架构Mac更换为M1(arm64)设备后,使用Homebrew安装的clang开启AddressSanitizer(ASAN)时,整体功能正常,但存在异常:堆栈跟踪仅显示与触发信号的调用位于同一源文件内的栈上函数,该问题在Intel架构Mac或Linux平台均未出现。

示例1:所有函数位于同一源文件

a.c代码:

void bar(void) {
  *(volatile char *)0 = 0;
}

void foo(void) {
  bar();
}

int main(void) {
  foo();
}

编译、链接与符号化命令:

$ /opt/homebrew/opt/llvm/bin/clang -c -fsanitize=address -O1 -g -fno-omit-frame-pointer a.c -o a.o
$ /opt/homebrew/opt/llvm/bin/clang -fsanitize=address a.o -o a.out
$ dsymutil a.out

运行程序输出:

$ ./a.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==31187==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x00010050bf5c bp 0x00010054d08c sp 0x00016f8f7660 T0)
==31187==The signal is caused by a UNKNOWN memory access.
==31187==Hint: address points to the zero page.
    #0 0x10050bf5c in bar /Users/jpc/src/asantest/a.c:2:23
    #1 0x10050bf5c in foo /Users/jpc/src/asantest/a.c:6:3
    #2 0x10050bf5c in main /Users/jpc/src/asantest/a.c:10:3

==31187==Register values:
 x[0] = 0x0000000000000001   x[1] = 0x000000016f8f77d0   x[2] = 0x000000016f8f77e0   x[3] = 0x000000016f8f78f8  
 x[4] = 0x0000000000000000   x[5] = 0x0000000000000000   x[6] = 0x0000000000000000   x[7] = 0x0000000000000000  
 x[8] = 0x0000000000000000   x[9] = 0x0000000000000002  x[10] = 0x0000000000000000  x[11] = 0x0000000000000002  
x[12] = 0x0000000000000002  x[13] = 0x0000000000000000  x[14] = 0x0000000000000020  x[15] = 0x0000000000000000  
x[16] = 0x0000000300fd7088  x[17] = 0x6ae100016f8f6a70  x[18] = 0x0000000000000000  x[19] = 0x00000001005fc060  
x[20] = 0x000000010050bf34  x[21] = 0x00000001005a8070  x[22] = 0x0000000000000000  x[23] = 0x0000000000000000  
x[24] = 0x0000000000000000  x[25] = 0x0000000000000000  x[26] = 0x0000000000000000  x[27] = 0x0000000000000000  
x[28] = 0x0000000000000000     fp = 0x000000016f8f7660     lr = 0x000000010054d08c     sp = 0x000000016f8f7660  
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /Users/jpc/src/asantest/a.c:2:23 in bar
==31187==ABORTING
zsh: abort      ./a.out

栈上三个函数均正常显示,符合预期。

示例2:触发违规的函数位于其他源文件

b1.c代码:

void bar(void);

void foo(void) {
  bar();
}

int main(void) {
  foo();
}

b2.c代码:

void bar(void) {
  *(volatile char *)0 = 0;
}

编译、链接与符号化命令:

$ /opt/homebrew/opt/llvm/bin/clang -c -fsanitize=address -O1 -g -fno-omit-frame-pointer b1.c -o b1.o
$ /opt/homebrew/opt/llvm/bin/clang -c -fsanitize=address -O1 -g -fno-omit-frame-pointer b2.c -o b2.o
$ /opt/homebrew/opt/llvm/bin/clang -fsanitize=address b1.o b2.o -o b.out
$ dsymutil b.out

运行程序输出:

$ ./b.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==31297==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x000102afff5c bp 0x000102afff10 sp 0x00016d303650 T0)
==31297==The signal is caused by a UNKNOWN memory access.
==31297==Hint: address points to the zero page.
    #0 0x102afff5c in bar /Users/jpc/src/asantest/b2.c:2:23

==31297==Register values:
 x[0] = 0x0000000000000001   x[1] = 0x000000016d3037d0   x[2] = 0x000000016d3037e0   x[3] = 0x000000016d3038f8  
 x[4] = 0x0000000000000000   x[5] = 0x0000000000000000   x[6] = 0x0000000000000000   x[7] = 0x0000000000000000  
 x[8] = 0x0000000000000000   x[9] = 0x0000000000000002  x[10] = 0x0000000000000000  x[11] = 0x0000000000000002  
x[12] = 0x0000000000000002  x[13] = 0x0000000000000000  x[14] = 0x0000000000000020  x[15] = 0x0000000000000000  
x[16] = 0x00000003074e7088  x[17] = 0x6ae100016d302a70  x[18] = 0x0000000000000000  x[19] = 0x0000000102b08060  
x[20] = 0x0000000102afff04  x[21] = 0x0000000102cb8070  x[22] = 0x0000000000000000  x[23] = 0x0000000000000000  
x[24] = 0x0000000000000000  x[25] = 0x0000000000000000  x[26] = 0x0000000000000000  x[27] = 0x0000000000000000  
x[28] = 0x0000000000000000     fp = 0x000000016d303650     lr = 0x0000000102afff10     sp = 0x000000016d303650  
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /Users/jpc/src/asantest/b2.c:2:23 in bar
==31297==ABORTING
zsh: abort      ./b.out

堆栈跟踪仅显示b2.c中的违规函数bar,b1.c中的foo、main两个调用方均未显示,和Intel Mac、Linux平台的表现不一致。


原因与解决方案

这是Homebrew分发的LLVM版本在Apple Silicon(arm64)平台的已知适配问题:ASAN默认开启的快速栈回溯器依赖帧指针链遍历栈帧,但arm64 Darwin ABI下,单独编译的叶子函数(比如示例2里的bar)即便加了-fno-omit-frame-pointer,默认也不会生成完整的栈帧结构,快速回溯器读取到错误的帧指针地址就会提前终止遍历,只能读到当前源文件内的栈帧。同文件编译时优化会将多个函数合并内联,不存在跨编译单元的栈帧跳转,所以回溯正常。

可任选以下一种方案修复:

  • 推荐方案:所有编译、链接命令追加参数-mllvm -asan-use-fast-unwind=false,强制ASAN使用基于DWARF调试信息的慢速回溯器,不依赖帧指针链即可正确解析所有跨源文件栈帧,仅回溯速度略慢,日常调试完全无感知。
  • 若需要保留快速回溯的性能,给所有编译命令追加两个参数-fno-omit-frame-pointer -mno-omit-leaf-frame-pointer,后者强制所有叶子函数也生成标准的栈帧结构,保证快速回溯器能正常遍历整条调用链。
  • 小项目可临时跳过单独编译目标文件的步骤,直接将所有源文件传入clang一次完成编译链接,避免跨编译单元的栈帧问题,不适合中大型项目使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 23:18:29