美文网首页
jdk无法在树莓派4b上运行

jdk无法在树莓派4b上运行

作者: 叶迎宪 | 来源:发表于2021-05-22 19:07 被阅读0次

一个之前在海思平台运行正常的jdk版本,在32位树莓派的系统上面总是没法正常运行。运行java时报错

java: error while loading shared libraries: libjli.so: cannot open shared object file: No such file or directory

这个错误网上一堆说要设置 LD_LIBRARY_PATH 之类的。但是之前在海思平台上面也不需要设置,而且在树莓派上面设置了也还是报错。后来换了另外一个版本的jdk就可以运行了。但是之前的版本为什么不行呢?

这个so库位于 jdk1.7.0_60/lib/arm/jli 。使用ldd查看不行的版本

-bash-5.0# ldd libjli.so
        not a dynamic executable

而可以的版本

-bash-5.0# ldd libjli.so
        linux-vdso.so.1 (0xbedbe000)
        /usr/lib/arm-linux-gnueabihf/libarmmem-${PLATFORM}.so => /usr/lib/arm-linux-gnueabihf/libarmmem-v7l.so (0xb6f0d000)
        libdl.so.2 => /lib/arm-linux-gnueabihf/libdl.so.2 (0xb6efa000)
        libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0xb6dac000)
        libpthread.so.0 => /lib/arm-linux-gnueabihf/libpthread.so.0 (0xb6d82000)
        libgcc_s.so.1 => /lib/arm-linux-gnueabihf/libgcc_s.so.1 (0xb6d55000)
        /lib/ld-linux-armhf.so.3 (0xb6f40000)

ldd都分析不了这个动态库的依赖关系,之前在amd64系统ldd 32位i686库的时候遇到过。但是树莓派也都是arm32位系统,应该不存在32位 64位不匹配的问题啊?

PS:假如ldd不好使,还可以用 readelf -d xxx 来查看某个程序或者动态库的依赖

google搜索中,有人提到可以用strace分析java加载时为什么报错。于是试了一下。在漫长的结果中,有这么一段

openat(AT_FDCWD, "/usr/local/jdk1.7.0_60/bin/../lib/arm/jli/libjli.so", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\330\27\0\0004\0\0\0"..., 512) = 512
_llseek(3, 79528, [79528], SEEK_SET)    = 0
read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1200) = 1200
_llseek(3, 78572, [78572], SEEK_SET)    = 0
read(3, "A2\0\0\0aeabi\0\1(\0\0\0\5ARM10TDMI\0\6\3\10\1\t"..., 51) = 51
close(3)                                = 0
openat(AT_FDCWD, "/usr/lib/libjli.so", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/local/ffmpeg/lib/libjli.so", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = -1 ENOENT (No such file or directory)
...

很明显,java已经可以根据相对路径,找到 libjli.so了,但是在read aeabi一段后,就把它关闭了,继续从其他路径查找可用的 libjli.so。说明这个库的aeabi有不兼容的地方(aeabi是ARM Embedded Application Binary Interface的缩写)。继续google,发现readelf可以查看一个so的aeabi信息。

-bash-5.0# readelf -A libjli.so
Attribute Section: aeabi
File Attributes
  Tag_CPU_name: "ARM10TDMI"
  Tag_CPU_arch: v5T
  Tag_ARM_ISA_use: Yes
  Tag_THUMB_ISA_use: Thumb-1
  Tag_FP_arch: VFPv2
  Tag_ABI_PCS_wchar_t: 4
  Tag_ABI_FP_denormal: Needed
  Tag_ABI_FP_exceptions: Needed
  Tag_ABI_FP_number_model: IEEE 754
  Tag_ABI_align_needed: 8-byte
  Tag_ABI_enum_size: int
  Tag_ABI_HardFP_use: Deprecated
  Tag_ABI_optimization_goals: Aggressive Speed

而可以运行的那个版本

-bash-5.0# readelf -A libjli.so
Attribute Section: aeabi
File Attributes
  Tag_CPU_name: "6"
  Tag_CPU_arch: v6
  Tag_ARM_ISA_use: Yes
  Tag_THUMB_ISA_use: Thumb-1
  Tag_FP_arch: VFPv2
  Tag_ABI_PCS_wchar_t: 4
  Tag_ABI_FP_denormal: Needed
  Tag_ABI_FP_exceptions: Needed
  Tag_ABI_FP_number_model: IEEE 754
  Tag_ABI_align_needed: 8-byte
  Tag_ABI_align_preserved: 8-byte, except leaf SP
  Tag_ABI_enum_size: int
  Tag_ABI_HardFP_use: Deprecated
  Tag_ABI_VFP_args: VFP registers
  Tag_ABI_optimization_goals: Aggressive Speed
  Tag_CPU_unaligned_access: v6
  Tag_DIV_use: Not allowed

开始以为是Tag_CPU_name: "ARM10TDMI"这个导致了程序在树莓派上面不能正常执行。于是想办法编译一个相同Tag_CPU_name的执行程序来试验一下。apt search了一下,发现ubuntu自带的arm-linux gcc版本真多啊,从gcc 5到gcc 8,有multilib和非multilib版本之分,还有gnueabi和gnueabihf版本之分。先挑了一个低版本的
apt install gcc-5-arm-linux-gnueabi

结果发现,无论我怎么指定-mcpu,编译出来的Tag_CPU_name总是没变化。反而是指定-march=armv6或者armv7-a,Tag_CPU_name、Tag_CPU_arch都一起变了。后面又试了一下,用树莓派上面自己带的gcc,编译出来的aeabi信息为

-bash-5.0# readelf -A a.out
Attribute Section: aeabi
File Attributes
  Tag_CPU_name: "6"
  Tag_CPU_arch: v6
  Tag_ARM_ISA_use: Yes
  Tag_THUMB_ISA_use: Thumb-1
  Tag_FP_arch: VFPv2
  Tag_ABI_PCS_wchar_t: 4
  Tag_ABI_FP_rounding: Needed
  Tag_ABI_FP_denormal: Needed
  Tag_ABI_FP_exceptions: Needed
  Tag_ABI_FP_number_model: IEEE 754
  Tag_ABI_align_needed: 8-byte
  Tag_ABI_align_preserved: 8-byte, except leaf SP
  Tag_ABI_enum_size: int
  Tag_ABI_VFP_args: VFP registers
  Tag_CPU_unaligned_access: v6

暂时搞不出来arm10tdmi的,就先不管吧。在台式机用 arm-linux-gnueabi-gcc-5 交叉编译出来的程序,加上-march=armv6,里面只包含一句printf,拿到树莓派上面,运行正常,但是使用ldd分析的时候会报错“not a dynamic executable”!交叉编译程序的aeabi和树莓派gcc编译出来的已经非常接近了!

-bash-5.0# readelf -A test
Attribute Section: aeabi
File Attributes
  Tag_CPU_name: "6"
  Tag_CPU_arch: v6
  Tag_ARM_ISA_use: Yes
  Tag_THUMB_ISA_use: Thumb-1
  Tag_ABI_PCS_wchar_t: 4
  Tag_ABI_FP_rounding: Needed
  Tag_ABI_FP_denormal: Needed
  Tag_ABI_FP_exceptions: Needed
  Tag_ABI_FP_number_model: IEEE 754
  Tag_ABI_align_needed: 8-byte
  Tag_ABI_align_preserved: 8-byte, except leaf SP
  Tag_ABI_enum_size: int
  Tag_CPU_unaligned_access: v6
-bash-5.0# ldd test
        not a dynamic executable
-bash-5.0# ./test
0

仔细一对比,差异之处只有 Tag_ABI_VFP_args: VFP registers。树莓派gcc编译出来的带有这个,而交叉编译的没有。google一下可以知道,这个代表着不同的浮点数函数传参规则。没有VFP registers的,使用的是软浮点,浮点数都是通过整形的通用寄存器进行传参的;而带有VFP registers的,使用的是硬浮点,浮点数直接使用浮点寄存器进行传参。
https://developer.toradex.com/knowledge-base/linux-floating-point-calling-convention-co-processor-engine

另外,使用软浮点EABI的称为armel,使用硬浮点的EABI被称为armhf
https://blogs.oracle.com/jtc/is-it-armhf-or-armel
https://www.cnblogs.com/hustdc/p/7224980.html

试一下在交叉编译的时候加上-mfloat-abi=hard,结果直接报错
/usr/lib/gcc-cross/arm-linux-gnueabi/5/../../../../arm-linux-gnueabi/bin/ld: error: /tmp/ccR9XYmB.o uses VFP register arguments, test does not
/usr/lib/gcc-cross/arm-linux-gnueabi/5/../../../../arm-linux-gnueabi/bin/ld: failed to merge target specific data of file /tmp/ccR9XYmB.o

回想起来ubuntu自带的gcc都是分gnueabi和gnueabihf的,之前装的是gnueabi,换成gnueabihf的
apt install gcc-5-arm-linux-gnueabihf

这个编译出来默认是硬浮点,而且arch已经是v7-a的了,也无法将arch改为armv6,会报错“ sorry, unimplemented: Thumb-1 hard-float VFP ABI”。不过现在普遍都是Cortex-A架构的cpu了,问题不大。这套gcc编译出来的程序在树莓派上面既可以正常运行,也可以被ldd识别。

不过之前的情况还是有些疑问,有些程序虽然在树莓派上面不能被ldd识别,但是是可以正确执行的。armel和armhf的兼容性情况到底是怎样的?写个带浮点运算的测试程序

#include <stdio.h>
#include <math.h>
int main()
{
        double i = 4.0f;
        printf("%f\n", sqrt(i));
        return 0;
}

用armel工具链编译出来的程序,居然也能运行!只不过输出是 4.000错误的结果!这就是一个巨坑了,可以执行,但是运行可能是错的。
反过来,在armel的平台上,armhf的程序是不能被执行的,直接报 not found,还好。

为了进一步模拟原来jdk那种so无法加载的情况,写了一个动态库

int add(int a, int b)
{
        return a+b;
}

编译成so: arm-hisiv400-linux-gcc -shared -o libtest.so 2.c

#include <stdio.h>
extern int add(int a, int b);
int main()
{
        int a=4, b=2;
        printf("%d\n", add(a, b));
        return 0;
}

编译执行程序:arm-hisiv400-linux-gcc -L./ -Wl,-rpath,./ 1.c -ltest -o test
在树莓派上面一执行,果然就报错了

./test: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory

一开始这个add我是写成double类型的。但后来发现不需要浮点,只要浮点EABI不对,就不能加载so库。但是如果libtest.so是用armhf工具链编译的,执行程序用armel编译,还是能跑出正确的结果。

重要结论:armhf上面不会阻止armel执行程序的执行,但是会阻止armel动态库的加载。armel执行程序调用armhf的动态库,只要不涉及浮点传参,结果还是正常的。

相关文章

网友评论

      本文标题:jdk无法在树莓派4b上运行

      本文链接:https://www.haomeiwen.com/subject/agugjltx.html