天天看點

Android NDK下STD庫share static方式加載 (so庫混亂調用問題)

結論

-DANDROID_STL=c++_shared

  • 有該選項 share方式加載STL,打包的so庫不含有STD代碼
  • 沒該選項 static方式加載STL。
// 打so庫的build.gradle

android {
    compileSdkVersion rootProject.android.compileSdkVersion

    defaultConfig {
        externalNativeBuild {
            cmake {
                arguments "-DANDROID_STL=c++_shared"
            }
        }
           

Google相關文檔

If your application includes multiple shared libraries, you should use libc++_shared.so.

On Android, the libc++ used by the NDK is not part of the OS. This gives NDK users access to the latest libc++ features and bug fixes even when targeting old versions of Android. The trade-off is that if you use libc++_shared.so, you must include it in your APK. If you’re building your application with Gradle this is handled automatically.

Old versions of Android had bugs in PackageManager and the dynamic linker that caused installation, update, and loading of native libraries to be unreliable. In particular, if your app targets a version of Android earlier than Android 4.3 (Android API level 18), and you use libc++_shared.so, you must load the shared library before any other library that depends on it.

The ReLinker project offers workarounds for all known native library loading problems, and is usually a better choice than writing your own workarounds.

相關概念

  • STD:C++的标準庫(類比于JDK)
    • 18 之前用GNU (linux) 标準的STL
    • 18及以後用的 LLVM(clang指令行)的libc++

本文讨論 static、share的加載STD庫,兩個so庫如果一個static、一個share,就可能導緻異常崩潰。

情景

  • APP使用sdk1.so,sdk2.so
  • sdk1.so并沒有調用sdk2.so
  • 但異常崩潰,調用棧 sdk1.so --> libc++_shared.so -->sdk2.so

很詭異,因為libc++_shared.so的原因,是以檢視打包指令是否有問題

Tips

  • 使CMake輸出 clang++指令的方法
    • cmake腳本link不存在的so庫,讓它主動報錯,會在AndroidStudio的底部欄run 内輸出指令

例如CMakeLists.txt連結正常庫 zf, 改成z

target_link_libraries( # Specifies the target library.
        docDetect
        # Links the target library to the log library
        # included in the NDK.
        opencv_java3
        tensorflow-lite
        z
           

clang++指令中std相關參數

sdk1.so

aarch64-linux-android21-clang++ 
 -stdlib=libc++  
 -std=c++11
           

sdk2.so

aarch64-linux-android21-clang++ 
 -stdlib=libc++
 -std=c++11 
 -static-libstdc++
           

驗證

  • 對比兩個指令,多了個

    -static-libstdc++

    • 導緻sdk2.so内含有std c++_shared這個so庫 (static形式加載、share不包含)

項目異常原因

  • sdk1一年前的sdk,sdk2是新的sdk。
  • sdk1工程以share形式引入 STL,SDK1内有:sdk1.so, libc++_shared.so 兩個庫
  • sdk2 工程以static形式引入 STL,SDK2内有:sdk2.so(裡面含有libc++_shared.so) 1個庫
  • 兩個工程的libc++_shared.so還不一樣(md5不同)
  • sdk1.so在運作時要link libc++_shared.so,就連結到sdk2.so裡的libc++_shared.so的相關代碼,libc++_shared.so 又因為某些原因調用了sdk2.so的代碼,然後崩潰了
  • 嗯應該就是這麼回事。是以最後 sdk1、sdk2,都用share的形式加載libc++_shared.so,然後随便帶一個libc++_shared.so就行了(應該都是以符号連結引用的,運作時會link)

更改後

android {
    compileSdkVersion rootProject.android.compileSdkVersion

    defaultConfig {
        externalNativeBuild {
            cmake {
                arguments "-DANDROID_STL=c++_shared"
            }
        }
           

改正後 sdk2.so的打包腳本

aarch64-linux-android21-clang++ 
 -stdlib=libc++  
 -std=c++11