1.简介
基于Address Sanitizer实现Android NDK的内存错误检测Demo。
ps:适用于Android 13(API 级别 33)以下的设备,Android 14(API 级别 34)或更高版本的 ARM64设备推荐使用HWAddress Sanitizer配置更简单。
GitHub源码地址:https://github.com/shiyinghan/AddressSanitizerDemo
2.实现过程
参考Address Sanitizer(也称为 ASan)的官方教程,具体实现步骤如下:
2.1 配置CMake
对于 CMakeLists.txt 中的每个目标:
target_compile_options(${CMAKE_PROJECT_NAME} PUBLIC -fsanitize=address -fno-omit-frame-pointer)
target_link_options(${CMAKE_PROJECT_NAME} PUBLIC -fsanitize=address)
在模块的 build.gradle 中:
android {
......
defaultConfig {
......
externalNativeBuild {
cmake {
// Can also use system or none as ANDROID_STL.
arguments "-DANDROID_ARM_MODE=arm", "-DANDROID_STL=c++_shared", "-DSANITIZE=asan"
}
}
}
}
2.2 配置ndk-build(可选)
在 Application.mk 中:
APP_STL := c++_shared
APP_CFLAGS += -fsanitize=address -fno-omit-frame-pointer
APP_LDFLAGS += -fsanitize=address
APP_OPTIM := debug
对于 Android.mk 中的每个模块:
LOCAL_ARM_MODE := arm
设置“LOCAL_ARM_MODE := arm”不是必须的,因为通过将 Application.mk 文件中的 APP_OPTIM 设置为 debug,会强制构建系统生成 ARM 二进制文件,有同样的效果。
2.3 将 android:debuggable 和android:extractNativeLibs添加到应用清单中
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
.....
android:debuggable="true"
android:extractNativeLibs="true"
.....
>
</application>
</manifest>
2.4 将 ASan 运行时库添加到应用模块的 jniLibs 中
ASan运行库的文件夹:NDK目录/toolchains/llvm/prebuilt/host平台/lib64/clang/版本号/lib/linux/
我用的NDK版本是21.4.7075529,所以ASan运行库的文件夹如下:
复制运行库so文件到jniLibs对应的文件夹下面:
2.5 将wrap.sh 文件添加到 src/main/resources/lib 目录中的每个目录
wrap.sh脚本文件内容如下:
#!/system/bin/sh
HERE="$(cd "$(dirname "$0")" && pwd)"
export ASAN_OPTIONS=log_to_syslog=false,allow_user_segv_handler=1
ASAN_LIB=$(ls $HERE/libclang_rt.asan-*-android.so)
if [ -f "$HERE/libc++_shared.so" ]; then
# Workaround for https://github.com/android-ndk/ndk/issues/988.
export LD_PRELOAD="$ASAN_LIB $HERE/libc++_shared.so"
else
export LD_PRELOAD="$ASAN_LIB"
fi
"$@"
创建并复制wrap.sh文件到src/main/resources/lib对应的文件夹下面:
3.测试效果
模拟一个很常见的内存已经释放但还在使用的情况:
char *heap = static_cast<char *>(malloc(1024));
free(heap);
char *str = "str";
memcpy(heap, str, strlen(str));
运行应用,logcat会出现以下错误提示:
这就说明检测出了上面故意加入的内存错误操作!
4. 兼容性问题
ndk-build的兼容性可能比CMake好一点,使用CMake配置好之后,在一个armeabi-v7a的设备上无法正常加载asan工具,出现卡死的现象,但是使用ndk-build就是正常的。而在arm64-v8a设备上面,CMake和ndk-build的配置,都可以正常加载asan工具。
5. 修改ASAN_OPTIONS
一些asan的选项可以通过ASAN_OPTIONS进行配置,比如我使用libjpeg-turbo软件库,打开asan之后,会出现以下错误:
咱们可能不关心odr-violation问题,就可以添加detect_odr_violation=0配置,找到对应cpu架构wrap.sh文件,修改如下:
再运行就不会出现对应的错误提示了,asan会忽略odr-violation检测。
6. 其他
ASan的一些高级用法可以参考“安仔都有人用”的这篇文章:android 如何分析应用的内存(十一)——ASan