目录

Kotlin/Native 在 OHOS 上开启 ASAN

本文记录如何在适配鸿蒙的 Kotlin/Native(基于 Kotlin 2.0.21)上开启 ASAN(Address Sanitizer)

一、ASAN 简介

ASAN 是面向 C/C++ 的内存错误检测工具,运行时能发现例如:

  • heap-buffer-overflow / stack-buffer-underflow
  • stack-use-after-scope
  • attempt-free-nonallocated-memory / double-free / heap-use-after-free
  • 以及其他常见内存错误。

二、Kotlin/Native 的 ASAN 支持现状

在 Kotlin 2.0.21 里,虽然留了 ASAN/TSAN 的配置口子,但实际几乎没支持

// Sanitizer.kt
enum class SanitizerKind { ADDRESS, THREAD }

// KonanConfig.kt:只有 THREAD 且部分产物类型才 takeIf true,其余都 not supported
// KonanTargetExtensions:仅 LINUX_X64 支持 ADDRESS,MACOS_X64 支持 THREAD,其余空列表

所以要在 OHOS 上用 ASAN,需要自己接一套。


三、OHOS 上开 ASAN 的思路

鸿蒙的 clang 单独编一个带 ASAN 的 binary 可以这样:

clang --target=aarch64-linux-ohos -shared-libasan -fsanitize=address main.cpp -o libhello.out

clang -v 能看到完整编译和链接步骤(-cc1 的各类参数、链接时 libclang_rt.asan.so 等)。

Kotlin/Native 不是标准 C++ 流程:是 IR → .o → 链接器,没有「clang 一步到 binary」那一步,所以要把 ASAN 的编译/链接参数等价地塞进 K/N 的 pipeline。


四、遇到的问题与处理

4.1 -disable-llvm-optzns

K/N 默认带 -disable-llvm-optzns,走自己的 optimizePipeline。ASAN 插桩依赖优化阶段,开着这个选项时,编出的 so 里没有 ASAN 相关符号。需要去掉 -disable-llvm-optzns,让优化和插桩正常进行。

4.2 从 Bitcode 编译无法触发 Sanitizer 插桩

去掉 4.1 后,so 里开始有 ASAN 符号,但不全(只有 init 之类,没有 report 等)。原因见:

Sanitizers won’t trigger when compiling from llvm’s bitcode #1476

LLVM IR/bitcode 编译时,不会自动跑 Sanitizer 的插桩(插桩依赖 ASAN 的 LLVM attribute,完整 clang 流程才会打上)。K/N 正好是「IR → .o → 链接」拆开来的,所以要在 LLVM 层 自己加 ASAN pass,用 LLVM API 处理 IR。

4.3 用 LLVM API 做 ASAN pass

ASAN 是基础功能,低版本 LLVM(如 12.x)里已有封装好的 utils,可直接复用:

  • llvm-project/clang/lib/CodeGen/BackendUtil.cpp 里的 addAddressSanitizerPasses

把类似逻辑 copy 到自己的链路里即可。

4.4 接到 Kotlin/Native

  • 修改 K/N 里 Sanitizer/KonanConfig/KonanTargetExtensions 等,放开 OHOS 的 ASAN 支持(让 OHOS target 出现在 supportedSanitizers 等逻辑里)。
  • CAPIExtension.cpp(或你们接 LLVM 的地方)里 include ASAN 相关头文件,按 BackendUtil 的方式在 IR 上插入 ASAN passes,再走后续编译和链接;链接时确保带上 -shared-libasan、libclang_rt.asan 等(和上面 clang -v 看到的保持一致)。

4.5 行号不准

按上面步骤后能检测到内存问题,但堆栈里的行号有时不对,目前还没找到稳定修复办法,只能先接受。


五、小结

  • OHOS 的 clang 本身支持 -fsanitize=address + -shared-libasan,但 K/N 走的是 IR → .o → 链接,需要自己在 LLVM IR 阶段 加 ASAN pass,并放开 K/N 对 OHOS + ASAN 的配置。
  • 关键点:去掉 -disable-llvm-optzns;用 LLVM API(如 addAddressSanitizerPasses)在 IR 上插桩;链接参数与鸿蒙 clang 单独编 ASAN binary 时一致。