资讯动态

高通8155p Android9 ASAN编译与运行时避坑指南

发布时间:2026/10/19 9:03:07 来源:尧图企业网站定制
简介这份资源面向在高通8155p平台上进行QNXAndroid9双系统开发的工程师聚焦于开启ASANAddressSanitizer以定位内存越界、泄漏等疑难问题的完整配置方法。包内同时提供Android.mk与Android.bp两套构建脚本示例覆盖libAsanDebug、asan_debugd及libclang_rt.asan等模块帮助开发者解决ASAN无法使能、编译报错等常见障碍。资源共18个文件包含4个mk、4个bp、4个cpp源码、2个so动态库、2个头文件及2个mk备份压缩包约1.21MB结构清晰可直接对照参考。已有1995人学习下载适合不熟悉Android.mk与Android.bp写法、或反复遭遇编译问题的中高级开发者能快速掌握ASAN在QNXAndroid9环境下的集成思路与排错方法。1. 高通8155p 上开 ASAN 就翻车为什么你的 Android9 编译一开就挂在高通8155p 这类车机平台上做 Android9 开发内存问题是最难缠的一类故障。堆越界、use-after-free、双重释放这些 bug 在正常编译下往往表现得很“玄学”——今天跑得好好的明天换个场景就 native crashtombstone 里只有一串地址根本看不出是谁踩了谁。这时候大家都会想到 ASANAddressSanitizer但真正动手开的时候十有八九第一步就卡住要么编译直接报错要么链接缺符号要么刷进去开机就起不来。更麻烦的是8155p 的工程里 Android.mk 和 Android.bp 两套构建文件同时存在ASAN 的开关在两边写法完全不同改错一个地方就白忙活。这篇笔记就是把这套流程里我踩过的坑一个个拆开从报错现象到根因再到能直接抄的配置帮你把 ASAN 在 QNXAndroid9 双系统环境里真正跑起来。2. 编译期就报错Android.mk 与 Android.bp 的 ASAN 开关到底写在哪很多人第一次开 ASAN是在BoardConfig.mk里加了一句SANITIZE_TARGET : address然后make一跑编译到一半就炸了。报错五花八门但根因基本都指向同一个ASAN 的开启粒度是模块级的不是全局一句话就能搞定。Android9 的构建系统里SANITIZE_TARGET只对 SoongAndroid.bp体系生效而大量高通私有模块、HAL、vendor 库还是走 Android.mk两边必须分别处理。2.1 Android.bp 模块sanitize 属性怎么写才不报错先看 Android.bp 这边。Android9 的 Soong 支持在模块里直接声明 sanitize写法是cc_binary { name: vendor.demo.memtest, srcs: [memtest.cpp], vendor: true, // 关键开启 address sanitizer仅对本模块生效 sanitize: { address: true, }, shared_libs: [ liblog, libbase, ], }这段配置的逻辑是sanitize.address: true告诉 Soong 在编译和链接这个模块时插入 ASAN 的运行时插桩并在链接阶段自动带上libclang_rt.asan相关库。参数上要注意两点一是vendor: true的模块如果开了 ASAN链接器会去找 vendor 分区里的 ASAN 运行时而 Android9 默认的 vendor 镜像里不一定带这个库所以后面还要处理运行时依赖二是sanitize块里不要同时写address和hwaddress两者互斥同时写会直接报conflicting sanitizers。如果你在 Android.bp 里写完之后编译报unknown property sanitize那说明你的 Soong 版本对这个属性的支持被裁剪过常见做法是改用cflags手动加-fsanitizeaddress但这样链接阶段还得自己补-fsanitizeaddress的 ldflags否则会出现“编译过了、链接找不到符号”的经典翻车。2.2 Android.mk 模块LOCAL_SANITIZE 的正确姿势Android.mk 这边走的是另一套变量。Android9 的 make 体系里ASAN 开关是LOCAL_SANITIZEinclude $(CLEAR_VARS) LOCAL_MODULE : demo_memtest LOCAL_SRC_FILES : memtest.cpp LOCAL_VENDOR_MODULE : true # 关键声明本模块使用 address sanitizer LOCAL_SANITIZE : address # 如果模块同时依赖静态库静态库也要带 ASAN 插桩 LOCAL_STATIC_LIBRARIES : libdemo_utils LOCAL_SHARED_LIBRARIES : liblog libbase include $(BUILD_EXECUTABLE)这里最容易踩的坑是LOCAL_SANITIZE : address只对当前模块生效它依赖的静态库如果没开 ASAN链接时会出现undefined reference to __asan_report_load4这类符号缺失。解决办法是把被依赖的静态库模块也加上LOCAL_SANITIZE : address或者干脆把工具代码直接编进同一个模块。另一个坑是LOCAL_SANITIZE和LOCAL_SANITIZE_RECOVER的区别前者遇到错误直接 abort后者会尝试恢复继续跑定位内存泄漏时用 recover 模式更合适但定位越界写建议用默认的 abort 模式能第一时间拿到完整调用栈。2.3 全局开关 SANITIZE_TARGET 的边界在哪SANITIZE_TARGET : address写在BoardConfig.mk里作用是让整个 Soong 构建体系默认带 ASAN。但它的边界很明确只影响 Android.bp 模块不影响 Android.mk 模块。而且一旦全局打开system 分区体积会暴涨8155p 的 system 分区本来就紧张很容易刷机时提示partition size exceeded。我一般只在定位阶段对特定模块开定位完立刻关掉避免影响正常出包。如果你确实需要全局开记得同步在BoardConfig.mk里加SANITIZE_TARGET_DIAG : address否则有些诊断路径不会带插桩栈信息会缺帧。3. 链接期符号缺失undefined reference to __asan 系列报错怎么解编译过了链接挂了报错长这样undefined reference to __asan_init、undefined reference to __asan_report_load8。这类报错在 8155p 的 Android9 工程里特别常见因为高通把很多库拆得很碎ASAN 运行时库的传递链容易断。3.1 根因ASAN 运行时库没有正确传递到 vendor 分区ASAN 插桩后的模块在链接时需要libclang_rt.asan系列库。在 Android9 里system 分区的模块能自动找到libclang_rt.asan-aarch64-android.so但vendor 分区的模块默认找不到因为 vendor 和 system 的库搜索路径是隔离的。报错里出现__asan符号缺失九成是这个原因。解决办法是在模块的链接参数里显式指定 ASAN 运行时或者把运行时库复制到 vendor 分区。常见做法是在BoardConfig.mk里加# 让 vendor 模块也能链接到 ASAN 运行时 BOARD_VENDOR_ASAN_LIBS : libclang_rt.asan-aarch64-android然后在模块的Android.mk里LOCAL_SHARED_LIBRARIES libclang_rt.asan-aarch64-android注意这个库名要和你实际的架构匹配8155p 是 aarch64所以是-aarch64-android写错架构会报library not found。3.2 静态库依赖链断裂的排查顺序如果加了运行时库还是报符号缺失那问题多半在静态库依赖链上。排查顺序我一般是这样第一步用nm -u看报错的.o文件里到底缺哪些符号# 在 out 目录下找到报错的中间目标文件 nm -u out/target/product/xxx/obj/STATIC_LIBRARIES/libdemo_utils_intermediates/demo_utils.o | grep asan如果输出里有一堆__asan_开头的未定义符号说明这个静态库本身没被 ASAN 插桩但调用它的模块被插桩了链接时对不上。第二步确认静态库模块的Android.mk里有没有LOCAL_SANITIZE : address。没有就补上然后make clean重新编这个库因为增量编译不会重新插桩已经编好的.o。第三步如果静态库是预编译的.a文件高通经常这么干那基本无解——预编译库没法重新插桩。这时候只能把预编译库换成源码编译或者在链接时用--allow-multiple-definition绕过但后者只是掩盖问题运行时照样会崩。3.3 用 readelf 验证插桩是否真的生效链接过了不代表插桩生效了。我习惯用readelf确认一下# 检查最终可执行文件是否依赖 ASAN 运行时 readelf -d out/target/product/xxx/vendor/bin/demo_memtest | grep -i asan如果输出里有libclang_rt.asan-aarch64-android.so说明链接阶段带上了。再看符号表readelf -s out/target/product/xxx/vendor/bin/demo_memtest | grep __asan_init有__asan_init符号才说明插桩真正生效。这两个命令能帮你快速区分“编译链接过了但没插桩”和“真正开了 ASAN”两种情况避免刷进去发现没效果又回头重查。4. 运行时崩溃ASAN 报错日志看不懂、进程起不来怎么办编译链接都过了刷进 8155p 开机结果进程直接 abortlogcat 里一堆 ASAN 输出但关键信息被截断或者看不懂。这一章专门解决运行时的问题。4.1 ASAN 日志被截断logcat 缓冲区与 abort 时机ASAN 报错时默认往 stderr 输出Android 上 stderr 会重定向到 logcat。但 logcat 的缓冲区有限ASAN 的完整报告动辄几百行很容易被冲掉。解决办法有两个一是设置 ASAN 选项把报告写到文件# 在 init.rc 或启动脚本里设置环境变量 export ASAN_OPTIONSlog_path/data/local/tmp/asan_log:abort_on_error1:detect_leaks0log_path会让 ASAN 把报告写到/data/local/tmp/asan_log.pid不会被 logcat 冲掉。abort_on_error1保证出错立即 abort方便抓 tombstone。detect_leaks0是因为 Android9 上 LeakSanitizer 经常误报定位越界问题时先关掉。二是调大 logcat 缓冲区# 临时调大 logcat 缓冲区重启失效 adb logcat -G 16M但这个方法治标不治本ASAN 报告太长还是可能丢所以优先用log_path。4.2 进程起不来ASAN 运行时库没进 vendor 镜像刷机后进程直接起不来logcat里报CANNOT LINK EXECUTABLE ... library libclang_rt.asan-aarch64-android.so not found。这是最典型的运行时依赖缺失。根因是 ASAN 运行时库只编进了 system 分区但你的模块在 vendor 分区vendor 的 linker 搜索路径里没有 system 的库。解决办法是在BoardConfig.mk里把 ASAN 运行时库加到 vendor 镜像# 把 ASAN 运行时库复制到 vendor/lib64 BOARD_VENDOR_ASAN_RUNTIME : true如果这个变量在你的工程里不生效那就手动在device.mk里加PRODUCT_COPY_FILES \ prebuilts/clang/host/linux-x86/clang-4691093/lib64/clang/6.0.2/lib/linux/libclang_rt.asan-aarch64-android.so:$(TARGET_COPY_OUT_VENDOR)/lib64/libclang_rt.asan-aarch64-android.so路径里的 clang 版本号要和你实际用的编译器版本一致写错会报file not found。复制完之后make vendorimage重新刷 vendor 分区进程就能起来了。4.3 QNXAndroid9 双系统下 ASAN 的额外注意点8155p 是 QNX 和 Android9 双系统ASAN 只对 Android 侧的进程生效QNX 侧的进程不能用这套方法。另外双系统共享内存区域如果被 Android 侧 ASAN 插桩的进程访问ASAN 会把共享内存的合法访问误判为越界因为 ASAN 的 shadow memory 不覆盖 QNX 管理的那段物理内存。常见做法是在 ASAN 选项里加export ASAN_OPTIONSlog_path/data/local/tmp/asan_log:abort_on_error1:detect_leaks0:intercept_tls_get_addr0intercept_tls_get_addr0能减少跨系统 TLS 访问的误报。如果还是误报那就只能对访问共享内存的代码段单独关掉插桩用__attribute__((no_sanitize_address))标注相关函数。5. 避坑清单开 ASAN 时最容易翻车的五个操作这一章是我自己踩过的坑每条都按“现象 → 原因 → 解决”写你对照着排查能省不少时间。现象一make报SANITIZE_TARGET未知变量。原因是你把它写在了Android.mk或device.mk里这个变量只认BoardConfig.mk。解决挪到BoardConfig.mk并且确认你的 Soong 版本支持这个变量不支持就改用模块级sanitize。现象二编译过了刷机后进程一启动就 abort但 ASAN 报告里全是unknown module。原因是符号表被 strip 了ASAN 拿不到函数名。解决在模块的Android.mk里加LOCAL_STRIP_MODULE : false或者在BoardConfig.mk里加TARGET_STRIP_MODULE : false保留符号表再刷。现象三ASAN 报告里调用栈只有地址没有行号。原因是编译时没加-g。解决在模块的cflags里加-gAndroid.bp 里加cflags: [-g]Android.mk 里加LOCAL_CFLAGS -g。注意-g和-O2可以共存不会影响性能太多。现象四开了 ASAN 之后系统卡顿严重开机时间翻倍。原因是全局SANITIZE_TARGET把所有模块都插桩了CPU 和内存开销巨大。解决只对目标模块开 ASAN全局开关定位完立刻关掉。如果必须全局开把detect_leaks关掉能省不少开销。现象五Android.mk 和 Android.bp 同时存在时改了 bp 没生效。原因是同名模块在 make 体系里优先级更高Soong 的配置被覆盖了。解决确认模块到底走哪套构建用make的-d参数看模块解析路径或者直接看out目录下模块的Android.mk和Android.bp哪个被实际使用。6. 快速定位 ASAN 问题的排查路径与验证习惯定位 ASAN 问题我总结了一条从快到慢的路径按这个顺序走基本不会绕远路。第一步确认插桩是否生效。用readelf -s看目标文件里有没有__asan_init没有就回头查编译配置别急着刷机。这一步能过滤掉一半的“假 ASAN”问题。第二步确认运行时库是否到位。用adb shell进设备ls /vendor/lib64/libclang_rt.asan*看库在不在不在就查BoardConfig.mk和device.mk的复制规则。第三步抓完整 ASAN 报告。设置ASAN_OPTIONSlog_path/data/local/tmp/asan_log:abort_on_error1复现问题后从/data/local/tmp/拉报告别依赖 logcat。第四步解析调用栈。报告里的地址用addr2line转成行号# 用带符号的库解析地址 aarch64-linux-android-addr2line -e out/target/product/xxx/vendor/bin/demo_memtest -f -C 0x123456-f输出函数名-C做符号 demangle两个参数一起用才能看到可读的 C 函数名。第五步如果栈信息还是不全检查是不是跨模块调用。ASAN 只能看到插桩模块内部的调用栈跨到没插桩的模块就断了。这时候要么把相关模块都插桩要么用no_sanitize_address标注边界函数缩小排查范围。下面这张表是我常用的 ASAN 选项速查按场景选场景ASAN_OPTIONS 配置说明定位越界写abort_on_error1:detect_leaks0出错立即 abort抓 tombstone定位内存泄漏detect_leaks1:abort_on_error0不 abort跑完输出泄漏报告报告太长被截断log_path/data/local/tmp/asan_log写文件不依赖 logcat跨系统共享内存误报intercept_tls_get_addr0减少 QNX 共享内存误报性能开销太大detect_leaks0:quarantine_size_mb16减小隔离区降开销从那以后我每次在 8155p 上开 ASAN都强制走一遍“readelf 验证插桩 → 确认运行时库 → 设 log_path → addr2line 解析”这四步不再凭感觉刷机。这套流程帮我省下了大量反复刷机的时间也希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑