Android NDK Clang遷移
原因:Android NDK GCC deprecated
Android NDK從r11開始建議大家切換到clang。並且把GCC標記為deprecated,將GCC版本鎖定在GCC 4.9不再更新。android-ndk/ndk
- PSA: Everyone should be switching to Clang.
- Clang has been updated to 3.8svn (r243773, build 2481030).
- Note that this is now a nearly pure upstream clang.
- Also note that Clang packaged in the Windows 64 NDK is actually 32-bit.
- GCC in the NDK is now deprecated.
- Time to start using Clang if you haven』t already. If you have problems with Clang, please file bugs!
- The NDK will not be upgrading to 5.x, nor will we be accepting non-critical backports.
- Maintenance for miscompiles and internal compiler errors in 4.9 will be handled on a case by case basis.
- GCC 4.8 has been removed. All targets now use GCC 4.9.
- Synchronized with google/gcc-4_9 to r224707 (from r214835).
Android NDK從r13起,默認使用Clang進行編譯。但是暫時也沒有把GCC刪掉,Google會一直等到libc++(Clang的c++標準庫實現)足夠穩定後刪掉GCC。。。
- GCC is no longer supported. It will not be removed from the NDK just yet, but is no longer receiving backports. It cannot be removed until after libc++ has become stable enough to be the default, as some parts of gnustl are still incompatible with Clang. It will likely be removed after that point.
- Added simpleperf, a CPU profiler for Android:https://android.googlesource.com/platform/system/extras/+/master/simpleperf/README.md
- NDK_TOOLCHAIN_VERSION now defaults to Clang.
遷移方法
1. 如果使用ndk-build的話,只要不在Application.mk中指定NDK_TOOLCHAIN_VERSION,並且使用NDK >=r13,默認使用Clang進行編譯。
2. 如果使用cmake的話,並採用standalone工具鏈進行編譯,則直接使用設置CMAKE_C_COMPILER和CMAKE_CXX_COMPILER即可。
set(CMAKE_C_COMPILER "clang")set(CMAKE_CXX_COMPILER "clang++")
其中standalone的生成方法為:
# 生成${ANDROID_NDK_HOME}/build/tools/make_standalone_toolchain.py --arch arm|arm64|x86|x64 --api 23 --stl gnustl|libc++ --install-dir=xx_dir# 使用export PATH=xx_dir/bin:$PATH
而所謂的clang,其實是一個腳本,
#!/bin/bashif [ "$1" != "-cc1" ]; then `dirname $0`/clang50 -target i686-none-linux-android --sysroot `dirname $0`/../sysroot -D__ANDROID_API__=23 "$@"else # target/triple already spelled out. `dirname $0`/clang50 "$@"fi
clang++也是一個腳本,只是把上面腳本中clang50->clang50++
從這個腳本可以看出,所謂clang的多平台,是通過-target實現的。
#armv7a armv7-none-linux-androideabi#armv8 aarch64-none-linux-android#x86 i686-none-linux-android#x86_64 x86_64-none-linux-android
3. 如果不用standalone工具鏈,直接使用cmake進行編譯,建議使用${ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake來作為工具鏈文件進行配置。目前沒有去深入研究。
4. 如果跟gradle進行集成,大概方法:
(1)在子工程目錄下build.gradle的android/defaultConfig下添加externalNativeBuild/cmake分支,如果有用到rtti最好顯式聲明,如果只編譯armv7a則指定abiFilters。並在android下添加externalNativeBuild/cmake分支,指定CMakeLists.txt路徑。
android { ... defaultConfig { ... externalNativeBuild { cmake { cppFlags "-fexceptions -frtti" //顯式聲明,以支持dynamic_cast abiFilters armeabi-v7a } } } ... externalNativeBuild { cmake { path "CMakeLists.txt" } }
(2)CMakeLists.txt
指定CMake版本:
cmake_minimum_required(VERSION 3.4.1)
如果需要從外部導入庫,需要(注意路徑相對的是CMakeLists.txt所在目錄)
add_library( pugixml SHARED IMPORTED ) set_target_properties( # Specifies the target library. pugixml # Specifies the parameter you want to define. PROPERTIES IMPORTED_LOCATION # Provides the path to the library you want to import. ../../../../src/main/jniLibs/armeabi-v7a/libpugixml.so )
注意:由於Imported library doesn't build in Android Studio 2.2提到的Android Studio的bug,還需要使用
jniLibs.srcDirs = [../../../../src/main/jniLibs]
來保證外部導入庫可以被包含到apk中。
如果需要編譯庫,需要:
# Creates and names a library, sets it as either STATIC# or SHARED, and provides the relative paths to its source code.# You can define multiple libraries, and CMake builds them for you.# Gradle automatically packages shared libraries with your APK.add_library( # Sets the name of the library. native-lib # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). src/main/cpp/native-lib.cpp )# Searches for a specified prebuilt library and stores the path as a# variable. Because CMake includes system libraries in the search path by# default, you only need to specify the name of the public NDK library# you want to add. CMake verifies that the library exists before# completing its build.find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log )# Specifies libraries CMake should link to your target library. You# can link multiple libraries, such as libraries you define in this# build script, prebuilt third-party libraries, or system libraries.target_link_libraries( # Specifies the target library. native-lib # Links the target library to the log library # included in the NDK. ${log-lib} )
寫習慣後注釋非必須哈。
遷移問題:std::thread crash
現象:
backtrace: #00 pc 0003d53c /system/lib/libc.so (tgkill+12) #01 pc 00016db5 /system/lib/libc.so (pthread_kill+52) #02 pc 000179c7 /system/lib/libc.so (raise+10) #03 pc 0001416d /system/lib/libc.so (__libc_android_abort+36) #04 pc 000124f4 /system/lib/libc.so (abort+4) #05 pc 00050c70 /data/app/xxx/lib/arm/libgnustl_shared.so (__gnu_cxx::__verbose_terminate_handler()+348) #06 pc 0004e548 /data/app/xxx/lib/arm/libgnustl_shared.so (__cxxabiv1::__terminate(void (*)())+8) #07 pc 0004e6c8 /data/app/xxx/lib/arm/libgnustl_shared.so (std::terminate()+12) #08 pc 0004f790 /data/app/xxx/lib/arm/libgnustl_shared.so (__cxa_pure_virtual+24) #09 pc 000af0bc /data/app/xxx/lib/arm/libgnustl_shared.so (execute_native_thread_routine+28) #10 pc 000165a3 /system/lib/libc.so (__pthread_start(void*)+30) #11 pc 000144cb /system/lib/libc.so (__start_thread+6)
找了一下,發現是個NDK的bug。"pure virtual method called" when using std::thread with clang in NDK r12b · Issue #151 · android-ndk/ndk
奇怪,我使用NDK r11c和r13b的GCC編譯沒問題,使用NDK r13b的Clang編譯有問題,使用NDK r15的Clang有問題。
經確認,NDK r11c->r15的GCC默認arch是armv5te,Clang從r11c開始默認arch是armv7a,使用std::thread的話,要求編譯出來的庫和gnustl的arch必須匹配。
NDK r13b的-fno-exceptions -fno-rtti問題
android-ndk/ndk NDK r14 changelog提到:
- RTTI and exceptions are now on by default. This was done for improved compatibility with existing CMake projects. See Why NDK r13 android.toolchain.cmake add -fno-exceptions and -fno-rtti? · Issue #212 · android-ndk/ndk.
由於NDK r13b中build/cmake/android.toolchain.cmake存在
list(APPEND ANDROID_COMPILER_FLAGS_CXX -fno-exceptions -fno-rtti)
導致NDK代碼中使用dynamic_cast的無法通過NDK內置的cmake插件編譯。
解決辦法:需要用到rtti的,進行顯式聲明。
externalNativeBuild { cmake { cppFlags "-fexceptions -frtti" //顯式聲明,以支持dynamic_cast abiFilters armeabi-v7a //只是為了過濾平台,與本問題無關 }}
NDK r15移除了android_support的iconv功能
android_support庫在${NDK}/sources/android/support目錄,相比NDK r14b,NDK r15b拿掉了src/musl-locale整個目錄。
src/musl-locale/catclose.c src/musl-locale/catgets.c src/musl-locale/catopen.c src/musl-locale/iconv.c src/musl-locale/intl.c src/musl-locale/isalnum_l.c src/musl-locale/isalpha_l.c src/musl-locale/isblank_l.c src/musl-locale/iscntrl_l.c src/musl-locale/isdigit_l.c src/musl-locale/isgraph_l.c src/musl-locale/islower_l.c src/musl-locale/isprint_l.c src/musl-locale/ispunct_l.c src/musl-locale/isspace_l.c src/musl-locale/isupper_l.c src/musl-locale/iswalnum_l.c src/musl-locale/iswalpha_l.c src/musl-locale/iswblank_l.c src/musl-locale/iswcntrl_l.c src/musl-locale/iswctype_l.c src/musl-locale/iswdigit_l.c src/musl-locale/iswgraph_l.c src/musl-locale/iswlower_l.c src/musl-locale/iswprint_l.c src/musl-locale/iswpunct_l.c src/musl-locale/iswspace_l.c src/musl-locale/iswupper_l.c src/musl-locale/iswxdigit_l.c src/musl-locale/isxdigit_l.c src/musl-locale/langinfo.c src/musl-locale/strcasecmp_l.c src/musl-locale/strcoll.c src/musl-locale/strerror_l.c src/musl-locale/strfmon.c src/musl-locale/strftime_l.c src/musl-locale/strncasecmp_l.c src/musl-locale/strxfrm.c src/musl-locale/tolower_l.c src/musl-locale/toupper_l.c src/musl-locale/towctrans_l.c src/musl-locale/towlower_l.c src/musl-locale/towupper_l.c src/musl-locale/wcscoll.c src/musl-locale/wcsxfrm.c src/musl-locale/wctrans_l.c src/musl-locale/wctype_l.c
NDK >=r14越界檢測不能用問題
https://android.googlesource.com/platform/bionic/+/0a284f5c05f36c7be570f1a2750eba79fa883291/android-changes-for-ndk-developers.md
GNU hashes (Availible in API level >= 23)
The GNU hash style available with --hash-stylex=gnu allows faster symbol lookup and is now supported by the dynamic linker in API 23 and above. (Use --hash-stylex=both if you want to build code that uses this feature >= Android M but still works on older releases.)
問題是libclang_rt.asan-arm-android.so從NDK r14開始改為--hash-stylex=gnu(有空再研究這段設置在哪裡),導致低版本的安卓動態鏈接器出問題。調試信息提示「Caused by: java.lang.UnsatisfiedLinkError: dlopen failed: empty/missing DT_HASH in "libclang_rt.asan-arm-android.so" (built with --hash-stylex=gnu?)」。
解決方法:目前NDK r13b的libclang_rt.asan-arm-android.so還能用。
補充:原因是NDK r14b中elf中沒有.hash段導致。原因如下:
NDK r13b libclang_rt.asan-arm-android.so調用命令arm-linux-androideabi-readelf -S libclang_rt.asan-arm-android.so
Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .interp PROGBITS 00000154 000154 000013 00 A 0 0 1 [ 2] .note.gnu.build-i NOTE 00000168 000168 000020 00 A 0 0 4 [ 3] .dynsym DYNSYM 00000188 000188 005710 10 A 4 1 4 [ 4] .dynstr STRTAB 00005898 005898 009b28 00 A 0 0 1 [ 5] .gnu.hash GNU_HASH 0000f3c0 00f3c0 002924 04 A 3 0 4 [ 6] .hash HASH 00011ce4 011ce4 0025e8 04 A 3 0 4 [ 7] .rel.dyn REL 000142cc 0142cc 001ca0 08 A 3 0 4 [ 8] .rel.plt REL 00015f6c 015f6c 000470 08 AI 3 9 4 [ 9] .plt PROGBITS 000163dc 0163dc 0006bc 00 AX 0 0 4 [10] .text PROGBITS 00016a98 016a98 07f3d8 00 AX 0 0 4 [11] .ARM.exidx ARM_EXIDX 00095e70 095e70 001340 08 AL 10 0 4 [12] .rodata PROGBITS 000971b0 0971b0 00dd24 00 A 0 0 4 [13] .ARM.extab PROGBITS 000a4ed4 0a4ed4 000288 00 A 0 0 4 [14] .fini_array FINI_ARRAY 000a6e20 0a5e20 000008 00 WA 0 0 4 [15] .data.rel.ro PROGBITS 000a6e40 0a5e40 00018c 00 WA 0 0 32 [16] .init_array INIT_ARRAY 000a6fcc 0a5fcc 000008 00 WA 0 0 4 [17] .dynamic DYNAMIC 000a6fd4 0a5fd4 0000f8 08 WA 4 0 4 [18] .got PROGBITS 000a70e8 0a60e8 000f18 00 WA 0 0 4 [19] .data PROGBITS 000a8000 0a7000 002600 00 WA 0 0 4 [20] .bss NOBITS 000aa600 0a9600 457178 00 WA 0 0 64 [21] .comment PROGBITS 00000000 0a9600 00008a 01 MS 0 0 1 [22] .note.gnu.gold-ve NOTE 00000000 0a968c 00001c 00 0 0 4 [23] .ARM.attributes ARM_ATTRIBUTES 00000000 0a96a8 000038 00 0 0 1 [24] .gnu_debuglink PROGBITS 00000000 0a96e0 000024 00 0 0 1 [25] .shstrtab STRTAB 00000000 0a9704 0000f3 00 0 0 1
NDK r14b libclang_rt.asan-arm-android.so調用命令arm-linux-androideabi-readelf -S libclang_rt.asan-arm-android.so
Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .note.gnu.build-i NOTE 00000134 000134 000020 00 A 0 0 4 [ 2] .dynsym DYNSYM 00000154 000154 0058b0 10 A 3 1 4 [ 3] .dynstr STRTAB 00005a04 005a04 009d6e 00 A 0 0 1 [ 4] .gnu.hash GNU_HASH 0000f774 00f774 002984 04 A 2 0 4 [ 5] .gnu.version VERSYM 000120f8 0120f8 000b16 02 A 2 0 2 [ 6] .gnu.version_d VERDEF 00012c10 012c10 00001c 00 A 3 1 4 [ 7] .gnu.version_r VERNEED 00012c2c 012c2c 000080 00 A 3 4 4 [ 8] .rel.dyn REL 00012cac 012cac 001d68 08 A 2 0 4 [ 9] .rel.plt REL 00014a14 014a14 000468 08 A 2 0 4 [10] .plt PROGBITS 00014e7c 014e7c 0006b0 00 AX 0 0 4 [11] .text PROGBITS 0001552c 01552c 08424c 00 AX 0 0 4 [12] .ARM.exidx ARM_EXIDX 00099778 099778 001570 08 AL 11 0 4 [13] .rodata PROGBITS 0009ace8 09ace8 00e730 00 A 0 0 4 [14] .ARM.extab PROGBITS 000a9418 0a9418 000228 00 A 0 0 4 [15] .fini_array FINI_ARRAY 000aada0 0a9da0 000008 00 WA 0 0 4 [16] .data.rel.ro PROGBITS 000aadc0 0a9dc0 00018c 00 WA 0 0 32 [17] .init_array INIT_ARRAY 000aaf4c 0a9f4c 000008 00 WA 0 0 4 [18] .dynamic DYNAMIC 000aaf54 0a9f54 000118 08 WA 3 0 4 [19] .got PROGBITS 000ab088 0aa088 000f78 00 WA 0 0 4 [20] .data PROGBITS 000ac000 0ab000 002608 00 WA 0 0 4 [21] .bss NOBITS 000ae640 0ad640 4571b8 00 WA 0 0 64 [22] .comment PROGBITS 00000000 0ad608 000065 01 MS 0 0 1 [23] .debug_str PROGBITS 00000000 0ad66d 03da93 01 MS 0 0 1 [24] .debug_loc PROGBITS 00000000 0eb100 083802 00 0 0 1 [25] .debug_abbrev PROGBITS 00000000 16e902 00e968 00 0 0 1 [26] .debug_info PROGBITS 00000000 17d26a 0df94c 00 0 0 1 [27] .debug_ranges PROGBITS 00000000 25cbb6 0251b8 00 0 0 1 [28] .debug_macinfo PROGBITS 00000000 281d6e 000038 00 0 0 1 [29] .debug_pubnames PROGBITS 00000000 281da6 02b64b 00 0 0 1 [30] .debug_pubtypes PROGBITS 00000000 2ad3f1 00dd1b 00 0 0 1 [31] .debug_frame PROGBITS 00000000 2bb10c 00cedc 00 0 0 4 [32] .debug_line PROGBITS 00000000 2c7fe8 061a61 00 0 0 1 [33] .debug_aranges PROGBITS 00000000 329a50 000160 00 0 0 8 [34] .note.gnu.gold-ve NOTE 00000000 329bb0 00001c 00 0 0 4 [35] .ARM.attributes ARM_ATTRIBUTES 00000000 329bcc 00003b 00 0 0 1 [36] .symtab SYMTAB 00000000 329c08 06d990 10 37 26639 4 [37] .strtab STRTAB 00000000 397598 01ec7a 00 0 0 1 [38] .shstrtab STRTAB 00000000 3b6212 0001ac 00 0 0 1
__bswapsi2——Clang和gcc兼容性問題
如果代碼中使用了htonl或ntohl,使用gcc編譯時會依賴符號:__bswapsi2。而使用Clang編譯則不會依賴__bswapsi2。
我們編譯時依賴了自己編譯的OpenSSL,而我們的OpenSSL使用gcc來編譯,從而導致引入了__bswapsi2的依賴。這樣,依賴OpenSSL的庫如果使用htonl時,就會在符號中增加U __bswapsi2。而Android 5.0使用的是Clang來編譯,載入系統的openssl庫(使用Clang)時則沒有__bswapsi2導致動態庫打開失敗。
推薦閱讀:
※如何評價Clang with Microsoft CodeGen?
※Clang Static Analyzer - Warning Suppress
※LLVM每日談之十四 如何給Clang添加一個屬性
※在Mac上編C/C++不用Xcode而藉助terminal調用gcc真的好嗎?
※Clang Static Analyzer - BodyFarm
TAG:AndroidNDK | Clang | GCC |