这是一篇头一次做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
"
错误的地点
java
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`
c
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中测试通过,
完美解决 !