一個之前在海思平臺運(yùn)行正常的jdk版本,在32位樹莓派的系統(tǒng)上面總是沒法正常運(yùn)行。運(yùn)行java時報錯
java: error while loading shared libraries: libjli.so: cannot open shared object file: No such file or directory
這個錯誤網(wǎng)上一堆說要設(shè)置 LD_LIBRARY_PATH 之類的狸演。但是之前在海思平臺上面也不需要設(shè)置弦聂,而且在樹莓派上面設(shè)置了也還是報錯。后來換了另外一個版本的jdk就可以運(yùn)行了倍奢。但是之前的版本為什么不行呢庶溶?
這個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都分析不了這個動態(tài)庫的依賴關(guān)系懂鸵,之前在amd64系統(tǒng)ldd 32位i686庫的時候遇到過。但是樹莓派也都是arm32位系統(tǒng)行疏,應(yīng)該不存在32位 64位不匹配的問題按夜狻?
PS:假如ldd不好使酿联,還可以用 readelf -d xxx 來查看某個程序或者動態(tài)庫的依賴
google搜索中终息,有人提到可以用strace分析java加載時為什么報錯。于是試了一下贞让。在漫長的結(jié)果中周崭,有這么一段
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已經(jīng)可以根據(jù)相對路徑喳张,找到 libjli.so了续镇,但是在read aeabi一段后,就把它關(guān)閉了销部,繼續(xù)從其他路徑查找可用的 libjli.so磨取。說明這個庫的aeabi有不兼容的地方(aeabi是ARM Embedded Application Binary Interface的縮寫)。繼續(xù)google柴墩,發(fā)現(xiàn)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
而可以運(yùn)行的那個版本
-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"這個導(dǎo)致了程序在樹莓派上面不能正常執(zhí)行凫岖。于是想辦法編譯一個相同Tag_CPU_name的執(zhí)行程序來試驗一下江咳。apt search了一下,發(fā)現(xiàn)ubuntu自帶的arm-linux gcc版本真多啊哥放,從gcc 5到gcc 8歼指,有multilib和非multilib版本之分,還有g(shù)nueabi和gnueabihf版本之分甥雕。先挑了一個低版本的
apt install gcc-5-arm-linux-gnueabi
結(jié)果發(fā)現(xiàn)踩身,無論我怎么指定-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的瞒瘸,就先不管吧坷备。在臺式機(jī)用 arm-linux-gnueabi-gcc-5 交叉編譯出來的程序,加上-march=armv6情臭,里面只包含一句printf省撑,拿到樹莓派上面赌蔑,運(yùn)行正常,但是使用ldd分析的時候會報錯“not a dynamic executable”竟秫!交叉編譯程序的aeabi和樹莓派gcc編譯出來的已經(jīng)非常接近了娃惯!
-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
仔細(xì)一對比,差異之處只有 Tag_ABI_VFP_args: VFP registers鸿摇。樹莓派gcc編譯出來的帶有這個石景,而交叉編譯的沒有。google一下可以知道拙吉,這個代表著不同的浮點數(shù)函數(shù)傳參規(guī)則潮孽。沒有VFP registers的,使用的是軟浮點筷黔,浮點數(shù)都是通過整形的通用寄存器進(jìn)行傳參的往史;而帶有VFP registers的,使用的是硬浮點佛舱,浮點數(shù)直接使用浮點寄存器進(jìn)行傳參椎例。
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订歪,結(jié)果直接報錯
/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
這個編譯出來默認(rèn)是硬浮點刷晋,而且arch已經(jīng)是v7-a的了,也無法將arch改為armv6慎陵,會報錯“ sorry, unimplemented: Thumb-1 hard-float VFP ABI”眼虱。不過現(xiàn)在普遍都是Cortex-A架構(gòu)的cpu了,問題不大席纽。這套gcc編譯出來的程序在樹莓派上面既可以正常運(yùn)行捏悬,也可以被ldd識別。
不過之前的情況還是有些疑問润梯,有些程序雖然在樹莓派上面不能被ldd識別过牙,但是是可以正確執(zhí)行的。armel和armhf的兼容性情況到底是怎樣的纺铭?寫個帶浮點運(yùn)算的測試程序
#include <stdio.h>
#include <math.h>
int main()
{
double i = 4.0f;
printf("%f\n", sqrt(i));
return 0;
}
用armel工具鏈編譯出來的程序抒和,居然也能運(yùn)行!只不過輸出是 4.000錯誤的結(jié)果彤蔽!這就是一個巨坑了摧莽,可以執(zhí)行,但是運(yùn)行可能是錯的顿痪。
反過來镊辕,在armel的平臺上油够,armhf的程序是不能被執(zhí)行的,直接報 not found征懈,還好石咬。
為了進(jìn)一步模擬原來jdk那種so無法加載的情況,寫了一個動態(tài)庫
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;
}
編譯執(zhí)行程序:arm-hisiv400-linux-gcc -L./ -Wl,-rpath,./ 1.c -ltest -o test
在樹莓派上面一執(zhí)行卖哎,果然就報錯了
./test: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory
一開始這個add我是寫成double類型的鬼悠。但后來發(fā)現(xiàn)不需要浮點,只要浮點EABI不對亏娜,就不能加載so庫焕窝。但是如果libtest.so是用armhf工具鏈編譯的,執(zhí)行程序用armel編譯维贺,還是能跑出正確的結(jié)果它掂。