Android NDK از Address Sanitizer (همچنین به عنوان ASan شناخته می شود) پشتیبانی می کند که با سطح API 27 (Android O MR 1) شروع می شود.
ASan یک ابزار سریع مبتنی بر کامپایلر برای تشخیص اشکالات حافظه در کدهای بومی است. ASan تشخیص می دهد:
- سرریز/زیر جریان بافر پشته و پشته
- استفاده از پشته پس از رایگان
- پشته استفاده خارج از محدوده
- دو برابر رایگان / وحشی رایگان
سربار CPU ASan تقریباً 2 برابر است، سربار اندازه کد بین 50٪ تا 2x است، و سربار حافظه بزرگ است (بسته به الگوهای تخصیص شما، اما به ترتیب 2x).
نمونه برنامه
یک برنامه نمونه نحوه پیکربندی یک نوع ساخت برای asan را نشان می دهد.
ساخت
برای ساخت کد بومی (JNI) برنامه خود با Address Sanitizer ، موارد زیر را انجام دهید:
ndk-build
در Application.mk شما:
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
CMake
در build.gradle ماژول شما:
android {
defaultConfig {
externalNativeBuild {
cmake {
// Can also use system or none as ANDROID_STL.
arguments "-DANDROID_ARM_MODE=arm", "-DANDROID_STL=c++_shared"
}
}
}
}
برای هر هدف در CMakeLists.txt شما:
target_compile_options(${TARGET} PUBLIC -fsanitize=address -fno-omit-frame-pointer)
set_target_properties(${TARGET} PROPERTIES LINK_FLAGS -fsanitize=address)
اجرا کنید
با شروع Android O MR1 (سطح API 27)، یک برنامه کاربردی میتواند یک اسکریپت Wrap Shell ارائه کند که میتواند فرآیند برنامه را بپیچد یا جایگزین کند. این به یک برنامه قابل اشکال زدایی اجازه می دهد تا راه اندازی برنامه خود را سفارشی کند، که استفاده از ASan را در دستگاه های تولیدی امکان پذیر می کند.
-
android:debuggable
به مانیفست برنامه اضافه کنید. -
useLegacyPackaging
را در فایلbuild.gradle
برنامه خود رویtrue
تنظیم کنید. برای اطلاعات بیشتر به راهنمای اسکریپت wrap shell مراجعه کنید. - کتابخانه زمان اجرا ASan را به
jniLibs
ماژول برنامه خود اضافه کنید. فایل های
wrap.sh
را با محتویات زیر به هر دایرکتوری در دایرکتوریsrc/main/resources/lib
خود اضافه کنید.#!/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
نیاز دارد که معمولاً به طور پیش فرض فعال است.
باز کردن سریع پیش فرض malloc/realloc/free است. باز کردن آهسته پیش فرض برای ردیابی پشته های مرگبار است. با افزودن fast_unwind_on_malloc=0
به متغیر ASAN_OPTIONS
در wrap.sh میتوان بازگشایی کند را برای همه ردیابیهای پشته فعال کرد.