Android NDK Address Sanitizer

2023-11-13


此文章是基于官方文档 Address Sanitizer的基础上做了一些扩展说明。

从 API 级别 27 (Android O MR 1) 开始,Android NDK 可支持 Address Sanitizer(也称为 ASan)。为啥从27开始呢?因为wrap.sh 仅适用于 API 级别 27 及更高级别。
ASan 是一种基于编译器的快速检测工具,用于检测原生代码中的内存错误。ASan 可以检测以下问题:

  • 堆栈和堆缓冲区上溢/下溢
  • 释放之后的堆使用情况
  • 超出范围的堆栈使用情况
  • 重复释放/错误释放

ASan 可在 32 位和 64 位 ARM 以及 x86 和 x86-64 上运行。ASan 的 CPU 开销约为 2 倍,代码大小开销在 50% 到 2 倍之间,并且内存开销很大(具体取决于您的分配模式,但约为 2 倍)。
对于 64 位 ARM,强烈建议使用HWAddress Sanitizer

构建

如需使用 Address Sanitizer 构建应用的原生 (JNI) 代码,请执行以下操作:

# 在 Application.mk 中:
APP_PLATFORM  := android-17
APP_STL := c++_shared # Or system, or none.
APP_CFLAGS := -fsanitize=address -fno-omit-frame-pointer
APP_LDFLAGS := -fsanitize=address
# 对于 Android.mk 中的每个模块:
LOCAL_ARM_MODE := arm

如果APP_PLATFORM版本小于17,那么编译的时候就会出现如下错误:

/Users/stone/Library/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/5.0.300080/lib/linux/libclang_rt.asan-arm-android.so: error: undefined reference to '__vsnprintf_chk', version 'LIBC'
clang++: error: linker command failed with exit code 1 (use -v to see invocation)

在链接选项中添加-fsanitize=address(属于llvm的特性),那么它在链接的时候会到llvm的库目录寻找这个库,库的位置在如下位置:

./toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/5.0/lib/linux/libclang_rt.asan-arm-android.so
./toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/5.0/lib/linux/libclang_rt.asan-mips64-android.so
./toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/5.0/lib/linux/libclang_rt.asan-i686-android.so
./toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/5.0/lib/linux/libclang_rt.asan-x86_64-android.so
./toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/5.0/lib/linux/libclang_rt.asan-aarch64-android.so
./toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/5.0/lib/linux/libclang_rt.asan-mips-android.so

查看编译出来的库依赖关系:readelf -d libs/armeabi-v7a/santest,可以看到它确实依赖了libclang_rt.asan-arm-android.so

Dynamic section at offset 0xe98 contains 34 entries:
  Tag        Type                         Name/Value
 0x00000003 (PLTGOT)                     0x1fe4
 0x00000002 (PLTRELSZ)                   32 (bytes)
 0x00000017 (JMPREL)                     0x4a4
 0x00000014 (PLTREL)                     REL
 0x00000011 (REL)                        0x474
 0x00000012 (RELSZ)                      48 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffa (RELCOUNT)                   6
 0x00000015 (DEBUG)                      0x0
 0x00000006 (SYMTAB)                     0x224
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000005 (STRTAB)                     0x2b4
 0x0000000a (STRSZ)                      340 (bytes)
 0x00000004 (HASH)                       0x408
 0x00000001 (NEEDED)                     Shared library: [libclang_rt.asan-arm-android.so]
 0x00000001 (NEEDED)                     Shared library: [libc++_shared.so]
 0x00000001 (NEEDED)                     Shared library: [liblog.so]
 0x00000001 (NEEDED)                     Shared library: [libc.so]
 0x00000001 (NEEDED)                     Shared library: [libm.so]
 0x00000001 (NEEDED)                     Shared library: [libstdc++.so]
 0x00000001 (NEEDED)                     Shared library: [libdl.so]
 0x0000001a (FINI_ARRAY)                 0x1e74
 0x0000001c (FINI_ARRAYSZ)               8 (bytes)
 0x00000019 (INIT_ARRAY)                 0x1e7c
 0x0000001b (INIT_ARRAYSZ)               20 (bytes)
 0x00000020 (PREINIT_ARRAY)              0x1e90
 0x00000021 (PREINIT_ARRAYSZ)            0x8
 0x0000000f (RPATH)                      Library rpath: [/Users/stone/Library/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/5.0.300080/lib/linux/arm]
 0x0000001e (FLAGS)                      BIND_NOW
 0x6ffffffb (FLAGS_1)                    Flags: NOW
 0x6ffffff0 (VERSYM)                     0x440
 0x6ffffffe (VERNEED)                    0x454
 0x6fffffff (VERNEEDNUM)                 1
 0x00000000 (NULL)                       0x0

注意:在使用 libc++_static 时,ASan 目前不兼容 C++ 异常处理。使用 libc++_shared 或不使用异常处理的应用或者不受影响,或者有相应解决方法。如需了解详情,请参阅问题 988

运行

从 Android O MR1(API 级别 27)开始,应用可以提供可封装或替换应用进程的封装 Shell 脚本。这样一来,可调试的应用就可对其应用启动过程进行自定义,以便在生产设备上使用 ASan。

注意:以下说明将介绍如何将 ASan 与 Android Studio 项目结合使用。对于非 Android Studio 项目,请参阅封装 Shell 脚本文档。

  1. android:debuggableandroid:extractNativeLibs=true 添加到应用清单。请注意,后者是某些配置的默认设置。如需了解详情,请参阅封装 Shell 脚本
  2. 将 ASan 运行时库添加到应用模块的 jniLibs 中。
  3. 将包含以下内容的 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
"$@"

假设您项目的应用模块的名称为 app,您的最终目录结构应包含以下内容:

<project root>
└── app
    └── src
        └── main
            ├── jniLibs
            │   ├── arm64-v8a
            │   │   └── libclang_rt.asan-aarch64-android.so
            │   ├── armeabi-v7a
            │   │   └── libclang_rt.asan-arm-android.so
            │   ├── x86
            │   │   └── libclang_rt.asan-i686-android.so
            │   └── x86_64
            │       └── libclang_rt.asan-x86_64-android.so
            └── resources
                └── lib
                    ├── arm64-v8a
                    │   └── wrap.sh
                    ├── armeabi-v7a
                    │   └── wrap.sh
                    ├── x86
                    │   └── wrap.sh
                    └── x86_64
                        └── wrap.sh

堆栈轨迹

Address Sanitizer 需要在每次调用 malloc/realloc/free 时都展开堆栈。这里介绍两个选项:

  • 基于帧指针的“快速”展开程序。请按照构建部分中的说明使用此展开程序。
  • “慢速”CFI 展开程序。在此模式下,ASan 会使用 _Unwind_Backtrace。它只需要使用 -funwind-tables(通常默认处于启用状态)。

注意:“慢速”展开程序速度缓慢(速度差距达 10 倍或更多,具体取决于您调用 malloc/free 的频率)。

快速展开程序是 malloc/realloc/free 的默认选项。慢速展开程序是严重异常所对应堆栈轨迹的默认选项。通过将 fast_unwind_on_malloc=0 添加到 wrap.sh 的 ASAN_OPTIONS 变量中,即可为所有堆栈轨迹启用慢速展开程序。

二进制测试

由于是二进制程序测试,所以API版本只要高于17就可以了。

  • 测试代码
#include <iostream>

int main()
{
    std::cout << "Android NDK Address Sanitizer." << std::endl;
    char *p = new char[5];
    p[5] = 5;
    delete (p+1);
    return 0;
}
  • wrap.sh
#!/system/bin/sh
HERE="$(cd "$(dirname "$0")" && pwd)"
export ASAN_OPTIONS="log_to_syslog=true,allow_user_segv_handler=1,fast_unwind_on_malloc=0"
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和编译的可执行程序santest到手机
$ adb push libs/armeabi-v7a/santest /data/local/tmp
$ adb push wrap.sh /data/local/tmp
  • 执行
$ adb shell
$ cd /data/local/tmp
$ ./wrap.sh ./santest
  • 奔溃信息
WARNING: linker: /data/local/tmp/santest: unused DT entry: type 0xf arg 0x331
ASAN:DEADLYSIGNAL
=================================================================
==8874==ERROR: AddressSanitizer: SEGV on unknown address 0x0000001f (pc 0xe98ffa54 bp 0xffadf268 sp 0xffadeb00 T0)
==8874==The signal is caused by a READ memory access.
==8874==Hint: address points to the zero page.
    <empty stack>

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV
==8874==ABORTING
Aborted
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android NDK Address Sanitizer 的相关文章

  • 查看寻呼机 - 使用静态变量以编程方式滑动到下一页

    我想在我的 ViewPager 中以编程方式制作幻灯片 我的问题是 滑动事件是由放置在 ViewPager 保存的片段内部的按钮调用的 我知道代码 viewpager setCurrentItem int index 现在我的想法是使 Vi
  • 为什么 cordova.file.documentsDirectory 为空?

    我正在尝试使用 cordova plugin file transfer 在http ngcordova com docs plugins fileTransfer http ngcordova com docs plugins fileT
  • 如何使用retrofit2进行GET请求?

    我有一个在本地主机上运行的安静的 Web 服务 我想在该剩余 URL 上发出 Retrofit2 GET 请求 MainActivity java private void requestData public static final S
  • OPENGL ES 不工作:无当前上下文

    我尝试了 OpenGL ES2 for Android 一书中所示的程序 但它不起作用 我已经在Odroid E 三星s3 三星y 三星star上进行了测试 the gl version suported returns 2 but i g
  • 毕加索磁盘缓存

    我正在使用 Picasso 从 URL 加载图像 Picasso with getApplicationContext load product getImageUrl into imageView 据我所知 每次都会访问该网址 而不是缓存
  • BluetoothAdapter.getDefaultAdapter() 不返回 null

    这是我的第一篇文章 所以如果我做了一些愚蠢的事情 请告诉我 这个问题可能看起来与其他帖子类似 但或多或 少与我所看到的所有内容相反 关于该项目的事情 我正在开发 android 4 0 4 4 应用程序 我正在使用蓝牙 我正在运行 andr
  • 如何将 Android Instrumentation 测试推送到模拟器/设备?

    我正在尝试使用 Ubuntu 9 04 中的命令行 shell 在 Android 模拟器上运行 Webkit 布局测试 adb s emulator 5554 shell am instrument w com android dumpr
  • ListView:防止视图回收

    我有一个使用回收视图的 ListView 我试图阻止视图被回收 所以我使用 setHasTransientState android support v4 view ViewCompatJB setHasTransientState Vie
  • 使用 RxJava 限制吞吐量

    我现在遇到的情况很难解释 所以我会写一个更简单的版本来解释这个问题 我有一个Observable from 它发出一系列由ArrayList文件数量 所有这些文件都应上传到服务器 为此 我有一个函数可以完成这项工作并返回一个Observab
  • 如何使用 Swipe 视图实现 Android TabLayout 设计支持库

    我将使用 android TabLayout 设计支持库 但我不知道如何使用滑动视图 这是我的代码 XML
  • Android Drawable 绘图性能?

    在我看来 我有一个简单的 ARGB 可绘制对象 大约需要 2 毫秒才能绘制 但我可以在 0 5 毫秒内绘制与位图相同的文件 只是一些快速代码 我真的不能认为它是一个选项 优化可绘制对象的绘制速度的最佳方法是什么 这取决于可绘制的数量以及每个
  • 截图显示黑色

    我正在拍摄快照并创建缩略图 然后共享此图像 但缩略图显示全黑 我使用了以下代码 Bitmap bitmap View v1 v getRootView v1 setDrawingCacheEnabled true bitmap Bitmap
  • 在 android 版本 7.0 上膨胀类 android.widget.DatePicker 时出错

    我想显示弹出日期选择器并且我使用此代码 Calendar mcurrentDate Calendar getInstance int mYear mcurrentDate get Calendar YEAR int mMonth mcurr
  • 在 android 中,第一次单击时按钮侦听器未注册

    因为我是 Android 新手 所以我遇到了按钮监听器的问题 我正在使用 OnClickListener 来处理胸像 但它第一次点击后不执行一旦我单击多个 它就会表现良好 但如何使其在第一次单击时成为可能 这是我的代码 public cla
  • 如何在 kotlin 中检查 lambda 空值

    在 Kotlin 中如何检查 lambda 是否为空 例如 我有这样的签名 onError Throwable gt Unit 我如何区分它的默认值是应用于主体还是应用于此函数的值 您无法测试 lambda 的主体是否为空 因此它不包含源代
  • 如何在 onDraw() 方法中定义与像素无关的高度

    我扩展了 View 来构建自定义小部件 我想用独立的像素单位定义小部件的高度 我认为可以通过将像素密度乘以所需的高度来完成 但我不知道该怎么做 到目前为止我所拥有的 最小化 public class Timeline extends Vie
  • 动态创建 JSON 对象

    我正在尝试使用以下格式创建 JSON 对象 tableID 1 price 53 payment cash quantity 3 products ID 1 quantity 1 ID 3 quantity 2 我知道如何使用 JSONOb
  • 制作弹跳动画

    我想做图层的弹跳动画 我已经完成了该图层从右到中心的操作 现在我想将其向后移动一点 然后回到中心 这会产生反弹效果 我想我可以用这样的翻译来做到这一点
  • 改造方法调用可能会产生“java.lang.NullPointerException”

    使用 Retrofit 2 3 0 我在 Android Studio 中收到以下消息 有关如何删除此 IDE 错误消息的任何建议 谢谢 来自Response文档 http square github io retrofit 2 x ret
  • 在android中使用BaseActivity的不同活动中的通用标头

    我想编写一次代码并在不同的活动中使用 我创建了一个Base Activity class为了那个原因 此外 不同活动中所有布局的标题都是相同的 我在以下人员的帮助下做到了这一点

随机推荐

  • cpu矿工cpuminer-multi编译与使用

    文章目录 编译步骤 cpuminer multi 矿工运行 cpuminer multi有很多不同前辈开发 这里选用star最多且最流行的 lucasjones cpuminer multi 在编译中遇到了很多坑 这里全部整合到流程中 如果
  • nimg 文件服务器,NIMG-45. DEEP LEARNING-BASED PERITUMORAL MICROSTRUCTURE MAPPING IN GLIOBLASTOMAS USING FR...

    摘要 PURPOSE Characterization of the peritumoral microenvironment is a widely researched but as yet unsolved problem Deter
  • K-近邻算法预测电影类型

    K 近邻算法预测电影类型 k 近邻算法是一种比较简单 但是在一些方面又有很多作用的算法 比较常用的就是推荐入住位置 或者推荐入住酒店等等 K 近邻算法的原理 就是根据特征值 计算出离自己最近的那个分类 自己也属于那个类别 K 近邻是一种分类
  • 吴恩达机器学习(三) 无监督学习

    Unsupervised Learning Unsupervised learning allows us to approach problems with little or no idea what our results shoul
  • Django框架

    目录 目录 一 虚拟环境 1 什么是虚拟环境 2 作用 3 wondows下安装使用 二 Django框架 1 安装Django 2 拓展 虚拟机和虚拟环境问题 2 1虚拟机的三种网络模式 3 创建Django项目 3 1完整创建Djang
  • Python中Print()函数的用法___实例详解(全,例多)

    Python中Print 函数的用法 实例详解 全 例多 目 录 一 print 函数的语法 二 print 打印输出文本 三 print 中空格的使用方法 四 Print 换行 五 区隔符 sep 六 制表符 t 七 输出数学表达式 八
  • Qt:可视化UI设计

    1 创建项目 修改组件的对象名字和显示文本内容 创建一个 Widget Application 项目类 QDialog 在创建窗体时选择基类 QDialog 生成的类命名为 QWDialog 并选择生成窗体 在界面设计时 对需要访问的组件修
  • AES 配合mybaties 实现指定字段自动加解密

    1 加密工具类 Slf4j public class AESUtil 密钥长度 128 192 or 256 private static final int KEY SIZE 256 加密 解密算法名称 private static fi
  • C/C++从字符串中提取出数字的方法回顾

    在对格式化的数据进行处理的时候 很多时候需要在字符串中进行数据的提取 如果使用Oracle数据库 可以用里面的非常强大的sqlldr功能进行数据的提取和导入 在C C 中 可以自定义个方法提取出数字字符串 再使用atoi atof之类的方法
  • 颜色空间之RGB与YUV

    此篇是我在学习中做的归纳与总结 其中如果存在版权或知识错误或问题请直接联系我 欢迎留言 PS 本着知识共享的原则 此篇博客可以转载 但请标明出处 RGB CIE1931 RGB系统选择了700nm R 546 1nm G 435 8nm B
  • VGGNet实现CIFAR-100图像识别-1(数据预处理,one-hot)

    VGGNet CIFAR 100 导入数据 数据预处理 方法1 方法2 可能会遇到的问题 解决办法 Normalization和拆分训练集 验证集 One hot编码 未完待续 接下来请看另一篇博文 VGGNet实现CIFAR 100图像识
  • js复制功能插件

    JavaScript内容复制插件Clipboard js
  • 《C语言编程魔法书:基于C11标准》——1.3 主流C语言编译器介绍

    本节书摘来自华章计算机 C语言编程魔法书 基于C11标准 一书中的第1章 第1 3节 作者 陈轶 更多章节内容可以访问云栖社区 华章计算机 公众号查看 1 3 主流C语言编译器介绍 对于当前主流桌面操作系统而言 可使用Visual C GC
  • ARMV8体系结构简介:AArch64系统级体系结构之存储模型

    1 前言 关于存储系统体系架构 可以概述如下 存储系统体系结构的形式 VMSA 存储属性 2 存储系统体系结构 2 1 地址空间 指令地址空间溢出 指令地址计算 address of current instruction size of
  • Xcode14 终于放弃了bitcode和armv7架构,还有iOS 9、iOS 10

    相信大家已经了解到了不少关于Xcode 14的新消息 什么精简安装包 按需下载功能模块 提升编译速度 更快的xib storyBoard和SwiftUI app icon 1024像素图片 Xcode 14还放弃了一些东西 1 放弃了bit
  • openssl md5

    关于 16位和32位 md5得到的是一个16字节的散列值 每个字节用16进制 0x 格式成两个字符 连起来得到一个32个字符的串这就是所说的32位 16位就是取的32位的中间段 md5 aabbccdd 32位 bf3b2290e229da
  • (海伦公式)已知三角形三条边长,求面积

    海伦公式 已知三角形三条边长 求面积 海伦公式 S p p a p b p c 其中p是三角形的周长的一半p a b c 2 以下转自百度百科 海伦公式海又译作希伦公式 海龙公式 希罗公式 海伦 秦九韶公式 传说是古代的叙拉古国王 希伦 H
  • jQuery与原生JS相互转化

    前端发展很快 现代浏览器原生 API 已经足够好用 我们并不需要为了操作 DOM Event 等再学习一下 jQuery 的 API 同时由于 React Angular Vue 等框架的流行 直接操作 DOM 不再是好的模式 jQuery
  • WSL 2(Ubuntu18.04)编译Linux内核(5.7.9)并替换掉WSL 2原有内核

    准备工作 配置库 由于编译过程中需要很多库 因此需要提前进行配置 如果编译过程中遇到的报错均在下文的报错信息中记录 准备安装的库的命令为 sudo apt get install libncurses5 dev libncursesw5 d
  • Android NDK Address Sanitizer

    文章目录 构建 运行 堆栈轨迹 二进制测试 此文章是基于官方文档 Address Sanitizer的基础上做了一些扩展说明 从 API 级别 27 Android O MR 1 开始 Android NDK 可支持 Address San