這是一篇頭一次做NDK開發,記錄了踩坑,雜亂無章的錯誤記錄,僅供參考.
待解決Android運行NDK程序無法加載庫,無法找到庫,缺失庫文件,找不到c庫,導致我的Android NDK程序直接崩潰退出,,經過一番偵查發現,通過file獲知,我們使用的
gcc
編譯,搞錯了編譯工具鏈,使用arm工具鏈編譯,OK,這個問題解決,然后由彈出找不到libgcc_s.so.1
,好嘛,C庫缺失,很顯然,我拷貝一個C庫過去就好了
首先從之前制作好的最小根文件系統中提取出libgcc_s.so.1
這個庫文件,然后使用ADB,
ADB shell
將文件推入
adb push ./libgcc_s.so.1 /system/lib/
``
這里,它提示我們這是只讀文件系統
A:\Users\defenion\Desktop\ARM練習\FASTBOOT>adb push ./libgcc_s.so.1 /system/lib/
failed to copy './libgcc_s.so.1' to '/system/lib//libgcc_s.so.1': Read-only file system
我們通過重新掛載解決它
1|root@android:/ # mount -o remount,rw /dev/null /system
[ 1499.316842] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null)
Ok 看樣子是掛載成功
然后重新將我們從linux根文件系統中提取出來的C庫push進去
A:\Users\defenion\Desktop\ARM練習\FASTBOOT>adb push ./libgcc_s.so.1 /system/lib/
6792 KB/s (69371 bytes in 0.009s)
然后再次啟動我們的Android APP,在Android Studio 的ADB信息中看到 錯誤依舊
Caused by: java.lang.UnsatisfiedLinkError: Cannot load library: link_image[1936]: 1295 could not load needed library 'libgcc_s.so.1' for 'libhardcontrol.so' (link_image[1936]: 1295 could not load needed library 'libc.so.6' for 'libgcc_s.so.1' (load_library[1091]: Library 'libc.so.6' not found))
注意,這句是重點,是因為libc.so.6
導致我們不能加載剛才導入的libgcc_s.so.1
庫文件,
could not load needed library 'libc.so.6' for 'libgcc_s.so.1'
看來還需要將 libc.so.6
導入 ,顯然啊 ,這是C系的庫都沒有包含,,,,並且shell中還有很多常用的sh命令被刪除了,
待解決~ ! 擱置 20200106 20:18
目前看來最簡單的解決辦法是將Linux最小根文件系統中的C庫全部copy進來
這個也很是詭異,之前使用eclipse
從未出現過這種問題,使用Android Studio.就遇見了缺少依賴的動態鏈接庫.暫不探究.
對比下他們的文件信息
#他們同為ARM交叉編譯工具鏈編譯出來的
root@ubuntu:/mnt/hgfs/ubuntu# file *.so
#之前使用eclipse時使用的NDK庫,沒有任何問題
libled.so: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, stripped
#這次出問題的NDK庫,使用Android Studio
libnative.so: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, not stripped
有區別,( ′◔ ‸◔`),,我們再次找到了線索
探究
stripped
與not stripped
,以及將Linux最小根文件系統中的C庫全部copy進來
這個方法.
正常版本是通過NDK編譯工具鏈編譯出來的,出錯的版本是通過GCC手動指定參數進行編譯的
NDK編譯工具鏈
root@ubuntu:/usr/local/ndk/android-ndk-r8b# vim ndk-build
這東西是一個腳本,本質上依然是調用ARM-GCC進行的交叉編譯.
關於stripped
與not stripped
用file命令查看文件信息的時候,顯示如下:
libcom_err.so.2: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, stripped
libcrypto.so.10: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, stripped
libcrypt.so.1: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
libc.so.6: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
libdl.so.2: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
第一個最后顯示的是stripped,第二個是not stripped。而且對於同樣名字的動態庫,帶not stripped庫會大很多。
參考elf文件格式與動態鏈接庫
博客
a.out和elf(Executable and Linking Format)。這兩種格式中都有符號表(symbol table),其中包括所有的符號(程序的入口點還有變量的地址等等)。在elf格式中符號表的內容會比a.out格式的豐富的多。但是這些符號表可以用 strip工具去除,這樣的話這個文件就無法讓debug程序跟蹤了,但是會生成比較小的可執行文件。a.out文件中的符號表可以被完全去除,但是 elf中的在加載運行時起着重要的作用,所以用strip永遠不可能完全去除elf格式文件中的符號表。但是用strip命令不是完全安全的,比如對未連接的目標文件來說如果用strip去掉符號表的話,會導致連接器無法連接。
回過頭來,我們接着嘗試解決剛才提到的問題
- 嘗試1
既然使用NDK編譯工具鏈編譯成功了,那我們也使用一下這個編譯工具鏈,康康是否可行
參考以前的NDK筆記
需要編譯的文件,在目錄“ledtest\jni”中
–Android.mk(編譯腳本)
–com_topeet_ledtest_led.c
–com_topeet_ledtest_led.h
•拷貝文件夾“jni”到Ubuntu系統,,,,(JNI文件夾為 調用底層驅動的接口)
進入jni目錄執行 ndk-build編譯
•編譯完成后,在Ubuntu目錄“../” -->“libs”-->“armeabi”中生成庫文件“libled.so”,該文件就是安卓應用程序中需要的“.so”文件。
•將該文件拷貝到工程ledtest的“\libs\armeabi”目錄下,庫文件的編譯就全部完成了。
注:這個NDK編譯工具鏈是Android文件系統中自帶的工具之一.
我們需要先建立Android.mk文件
在我們拷貝的jni文件中建立一個Andoird.mk文件 ,輸入如下內容
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hardcontrol
LOCAL_SRC_FILES := native.c
LOCAL_LDLIBS += -llog
LOCAL_LDLIBS +=-lm
include $(BUILD_SHARED_LIBRARY)
第三行是編譯出來的文件的名稱libxxxx.so
,第四行是我們源文件名稱
編譯,
root@ubuntu:~/yizhi/jni# ndk-build
Compile thumb : hardcontrol <= native.c
SharedLibrary : libhardcontrol.so
Install : libhardcontrol.so => libs/armeabi/libhardcontrol.so
生成在上層libs目錄中,../libs/armeabi/libhardcontrol.so
放入我們的工程中,然后用前面的方法push到android的system/lib
目錄下,我們在測試
libhardcontrol.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked, stripped
這回這個和以前未出過依賴gcc庫問題的庫文件一樣了都是剝離了符號表的stripped類型 測試!
在虛擬機上跑一下,不出所料,程序依舊崩潰退出.但是顯然我們解決了這個問題,錯誤信息中沒有提示找不到我們加載的庫文件
報錯信息:
Caused by: java.lang.NoClassDefFoundError: HardControl
Caused by: java.lang.ClassNotFoundException: HardControl
顯然,我們的庫文件成功的加載了,但是沒有找到我們在C文件中指定注冊的類.
使用其他的NDK庫加載成功,可能是JAVA類和C指定的某些地方不匹配,暫定
static {
Log.d("AAA","AAAAAAAAA");
System.loadLibrary("adc");
}
使用其他的NDK測試成功,成功,成功的原因是這個C庫中沒有嘗試使用
cls = (*env)->FindClass(env, "HardControl");
這句代碼
暫定 20200107 21:09
在自用的安卓手機,Android 7上測試 ,提示出了稍有區別的錯誤,但是本質上是一樣的問題,都是找不到類
01-08 13:45:05.447 29811-29811/? A/DEBUG: Abort message: 'art/runtime/java_vm_ext.cc:470] JNI DETECTED ERROR IN APPLICATION: JNI NewGlobalRef called with pending exception java.lang.ClassNotFoundException: Didn't find class "HardControl" on path: DexPathList[[zip file "/data/app/com.example.ratherdog.app_addons_0001_message-1/base.apk"],nativeLibraryDirectories=[/data/app/com.example.ratherdog.app_addons_0001_message-1/lib/arm, /system/fake-libs, /data/app/com.example.ratherdog.app_addons_0001_message-1/base.apk!/lib/armeabi, /system/lib, /vendor/
試了直接指定其他類也不行,加上包名,OKay,可以找到我們需要的類HardControl了
JNI注冊類是這么寫就OK
jclass cls = (*env)->FindClass(env, "com/thisway/hardlibrary/HardControl");
一定要指定包名 ,
接下來,繼續報錯
java.lang.NoSuchMethodError: no static or non-static method "Lcom/thisway/hardlibrary/HardControl;.`
ledCtrl(I)I`"
錯誤的地點
public class HardControl{
public static native int ledCtrl(int which,int status);
public static native int ledOpen();
public static native void ledClose();
//加載hardcontrol這個名字的C庫,這種靜態寫法是加載C庫的標准寫法,因為static內的代碼只能被執行一次,並且在類創建實例的時候最先執行
//static方法只執行一次,最先執行,適合用於加載庫,
static {
try {
System.loadLibrary("hardcontrol");
} catch (Exception e) {
e.printStackTrace();
}
}
}
找不到這樣的方法是把ledCtrl(I)I
static const JNINativeMethod methods[] = {
{"ledOpen", "()I", (void *)ledOpen},
{"ledClose", "()V", (void *)ledClose},
{"ledCtrl", "(I)I", (void *)ledCtrl},
};
顯然這里面寫的不匹配. {"ledCtrl", "(I)I", (void *)ledCtrl},
這里一個參數,java中兩個參數
改為
{"ledCtrl", "(II)I", (void *)ledCtrl},
測試
在KindleFire HD 10中測試,Android4.4.4,通過,在虛擬機中測試通過,在魅藍Note6,Andoird中測試通過,
完美解決 !