android N : UnsatisfiedLinkError 只能訪問設定為公用庫的so庫

來源:互聯網
上載者:User

標籤:uid   example   run   ali   VID   his   nsa   process   bsp   

在android N上使用 .so作為apk的第三方庫的時候,會發生java.lang.UnsatisfiedLinkError:

09-27 12:17:01.280 D/ListenSoundModel( 3635): Load libxxxjni 
09-27 12:17:01.292 D/AndroidRuntime( 3635): Shutting down VM 
——— beginning of crash 
09-27 12:17:01.293 E/AndroidRuntime( 3635): FATAL EXCEPTION: main 
09-27 12:17:01.293 E/AndroidRuntime( 3635): Process: com.qualcomm.xxx, PID: 3635 
09-27 12:17:01.293 E/AndroidRuntime( 3635): java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file “/data/app/xxxApp.apk”],nativeLibraryDirectories=[/data/app-lib/xxxApp, /system/lib64, /vendor/lib64]]] couldn’t find “libxxxjni.so” 
09-27 12:17:01.293 E/AndroidRuntime( 3635): at java.lang.Runtime.loadLibrary0(Runtime.java:972) 
09-27 12:17:01.293 E/AndroidRuntime( 3635): at java.lang.System.loadLibrary(System.java:1530) 
… 
09-27 12:17:01.293 E/AndroidRuntime( 3635): at android.os.Handler.dispatchMessage(Handler.java:102) 
09-27 12:17:01.293 E/AndroidRuntime( 3635): at android.os.Looper.loop(Looper.java:154)

這在google的變更說明有 介紹https://developer.android.com/preview/behavior-changes.html#ndk,

具體是什麼原因呢,怎麼解決這種問題呢,google給出了一個官方的解決辦法(如上連結),這裡也給出另外一種方法。

首先,主要原因是google在N上對.so庫的載入進行了限制,限制了so庫指從部分指定的路徑進行載入,不在這個路徑的so提示 
java.lang.UnsatisfiedLinkError: dlopen failed: library “xxx.so” not found 或 
java.lang.UnsatisfiedLinkError: dlopen failed: library “/vendor/lib64/xxx.so” needed or dlopened by “/system/lib64/libnativeloader.so” is not accessible for the namespace “classloader-namespace” 或 其他異常錯誤提示。

N上對so庫載入的搜尋路徑方式為ld_library_path, runtime path, permit path,不在這個搜尋路徑下則載入失敗。

從代碼層面看,主要是類載入器ClassLoader的相關處理, 
code1: (loadedApk.java getClassLoader()) check sdk version 
// DO NOT SHIP: this is a workaround for apps loading native libraries 
// provided by 3rd party apps using absolute path instead of corresponding 
// classloader; see http://b/26954419 for example. 
if (mApplicationInfo.targetSdkVersion <= 23) { 
libraryPermittedPath += File.pathSeparator + “/data/app”; 
}

Code2: (loadedApk.java getClassLoader()) N add a new PermittedPath 
String libraryPermittedPath = mDataDir;

Code3: (native_loader.cpp) use the new namespace rule with search path: ld_library_path, runtime path, permit path.

在明白原因之後, 
解決辦法則是將自己的so加入到允許路徑的白名單裡面,具體操作為,如果不改代碼實現,則匯出裝置的/vendor/etc/public.libraries.txt 或/etc/public.libraries.txt檔案,將so名字添加進去,在push到裝置,重啟即可。

 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Android N 版本有個新feature,就是普通應用不能直接引用系統的一些so庫了,只能直接引用public.libraries.txt檔案中過濾的so庫。這個網址有介紹怎麼處理。
source.android.com/devices/tec…
具體情況是這樣的:我有一個系統許可權的apk,這個apk會編譯出一些so庫放在system/lib目錄下面,剛刷機這個apk是可以引用到這些so庫的,但我調試的時候直接install這個apk,啟動並執行時候居然直接掛了!!我也是醉了。沒想到這個新feature居然影響到我的系統許可權的apk。真蛋疼,我可不想每次push這個apk然後重啟才生效,一般我都是直接install的,但現在好日子貌似到頭了。

一.看看報錯資訊羅

具體報錯的資訊如下:

01-01 02:17:24.222  7475  7475 E linker  : library "/system/lib64/libhaha_utils.so" ("/system/lib64/libhaha_utils.so") needed or dlopened by "/system/lib64/libnativeloader.so" is not accessible for the namespace: [name="classloader-namespace", ld_library_paths="", default_library_paths="/system/fake-libs64:/data/app/com.example.haha-1/base.apk!/lib/arm64-v8a", permitted_paths="/data:/mnt/expand:/data/data/com.example.haha"]01-01 02:17:24.223  7475  7475 E System  : java.lang.UnsatisfiedLinkError: dlopen failed: library "/system/lib64/libhaha_utils.so" needed or dlopened by "/system/lib64/libnativeloader.so" is not accessible for the namespace "classloader-namespace"

大概的意思就是應用nativeloader打不開libhaha_utils.so這個so庫了,就崩潰了!!好殘忍。libhaha_utils.so這個庫是用我用Android.mk編譯後放在system/lib64下面的。但現在打不開了。
為啥呢?
因為/system/lib64/不在APK尋找so庫的合法路徑啊,合法路徑有啥呢?
上面log就有說明啦。下面三個路徑都沒有找到libhaha_utils.so庫,所以就掛了。
ld_library_paths="",
default_library_paths="/system/fake-libs64:/data/app/com.example.haha-1/base.apk!/lib/arm64-v8a", permitted_paths="/data:/mnt/expand:/data/data/com.example.haha

二.那為啥剛刷機時APK可以用,Install這個apk後就不能用了呢?

這個apk可是系統許可權的喲,就是apk的清單AndroidManifest中有下面一句
android:sharedUserId="android.uid.system"

正常來說,這種高端apk的permitted_paths是包含system/lib64的,從源碼可以知道
/frameworks/base/ core/java/android/app/LoadedApk.java

 1 //如果是系統apk並且沒有升級過 2         final boolean isBundledApp = mApplicationInfo.isSystemApp() 3                 && !mApplicationInfo.isUpdatedSystemApp(); 4  5         String libraryPermittedPath = mDataDir; 6         if (isBundledApp) {   //permitted_paths就增加system/lib64 7             // This is necessary to grant bundled apps access to 8             // libraries located in subdirectories of /system/lib 9             libraryPermittedPath += File.pathSeparator +10                                     System.getProperty("java.library.path");11         }

 

看上面的注釋就知道啦,如果是系統apk並且沒有升級過的話,so庫的搜尋路徑就會增加一個system/lib64。我去,google搞啥呢,為什麼還要限定不能升級。
因為install -r來安裝apk就相當於升級,所以刷機時apk可以用,install升級後不能用。

三.那如何解決這個鬼問題呢?

我純粹是為了調試方便,所以參考google的連結
source.android.com/devices/tec…


google.png


應用可以調用/vendor/etc/public.libraries.txt和/system/etc/public.libraries.txt裡面的所有so庫,所以哥往這個檔案寫入libhaha_utils.so,這個庫就變成共用的了,任意應用就可以找到這個so庫了,終於可以歡快地使用install apk的方式調試啦!!再也不用重啟了!!
下面是原生google的圖


Paste_Image.png


大概有這麼些個so庫是共用的。
libandroid.so
libc.so
libdl.so
libEGL.so
libGLESv1_CM.so
......
可以看看原生的這筆提交修改的。
android-review.googlesource.com/#/c/209029/

四.看看源碼這個public.libraries.txt檔案是咋玩的

在這個源碼裡面用到這個txt檔案
/system/core/libnativeloader/native_loader.cpp
在LibraryNamespaces類的Initialize()會讀取這個檔案,將so庫設定為公用so庫,所謂公用so庫,就是這個so庫誰都能用啦。

 1 static constexpr const char* kPublicNativeLibrariesSystemConfigPathFromRoot = "/etc/public.libraries.txt"; 2 static constexpr const char* kPublicNativeLibrariesVendorConfig = "/vendor/etc/public.libraries.txt"; 3   void Initialize() { 4     .................. 5     std::vector<std::string> sonames; 6     ReadConfig(public_native_libraries_system_config, &sonames, &error_msg), 7     ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames); 8     public_libraries_ = base::Join(sonames, ‘:‘); 9     .............10   }

 

這個方法時什麼時候調用的呢?大概過下流程羅。
首先在建立一個虛擬機器的時候,初始化NativeLoader,這個NativeLoader,顧名思義,就是用來裝載so庫的。
/art/runtime/ java_vm_ext.cc

1 extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {2   ................3   // Initialize native loader. This step makes sure we have4   // everything set up before we start using JNI.5   android::InitializeNativeLoader();6   ..............7 }

 

然後進入native_loader,進行初始化
/system/core/libnativeloader/native_loader.cpp

1 static LibraryNamespaces* g_namespaces = new LibraryNamespaces;2 3 void InitializeNativeLoader() {4   g_namespaces->Initialize();5 }

 

初始化是調用LibraryNamespaces類的Initialize完成公用so庫的賦值,哈哈哈,搞定!!

1 void Initialize() {2     ..................3     std::vector<std::string> sonames;4     ReadConfig(public_native_libraries_system_config, &sonames, &error_msg),5     ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames);6     public_libraries_ = base::Join(sonames, ‘:‘);7     .............8   }

 

總結:
1.Android N 不能直接調用系統的一些私人庫了,公用的庫都定義在public.libraries.txt裡面。
2.系統應用剛刷機是能夠調用system/lib64下的庫,但通過install升級該應用時,應用開啟會掛。因為升級後permitted_paths就不再包含system/lib64了。所以我們可以將apk要用到的庫名稱寫到public.libraries.txt中去解決快速調試問題。

 

android N : UnsatisfiedLinkError 只能訪問設定為公用庫的so庫

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.