xmake-io / xmake

🔥 A cross-platform build utility based on Lua
https://xmake.io
Apache License 2.0
9.57k stars 762 forks source link

java.lang.System.loadLibrary加载xmake编译的Android so库崩溃 #3743

Closed CaffreySun closed 1 year ago

CaffreySun commented 1 year ago

Xmake 版本

v2.7.8+20230406

操作系统版本和架构

Ubuntu 22.04.2 LTS

描述问题

我在编译一个名为RbtBezier的C++库,

  1. 给xmake配置好全局ndk,然后使用xmake编译为libRbtBezier.so:xmake f -p android -> xmake b
  2. 将libRbtBezier.so放入Android Studio项目中,被一个名为RbtBezierAdapter的C++ JNI库引用,CMakeList.txt如下:
    
    cmake_minimum_required(VERSION 3.4.1)
    project(RbtBezierAdapter)
    add_library(
        RbtBezierAdapter
        SHARED
        src/RbtBezierAdapter.cpp
        src/cn_robotpen_views_adapter_RbtBezierAdapter.h)

target_include_directories(RbtBezierAdapter PRIVATE ${CMAKE_SOURCE_DIR}/includes)

add_library(RbtBezier SHARED IMPORTED) set_target_properties(RbtBezier PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}/libRbtBezier.so)

find_library( log-lib log)

target_link_libraries( RbtBezierAdapter RbtBezier ${log-lib})

3. RbtBezierAdapter会被编译成libRbtBezierAdapter.so,并和相关Java代码以及libRbtBezier.so一起打包成aar
4. aar放入Android app项目中进行使用,App启动时崩溃, 崩溃信息如下:

Caused by: java.lang.UnsatisfiedLinkError: dlopen failed: library "/Volumes/SHW_APFS/WorkHome/Robot/Robot_Android/SDK/RobotPen_Android/robotpenmodel/src/main/cpp/../jniLibs/arm64-v8a/libRbtBezier.so" not found: needed by /data/app/~~fRYfb56I_uFC1H8fCeCE_w==/cn.robotdemo.d7.chanxian-ajzOiR8gFraQkGviBzSqVA==/lib/arm64/libRbtBezierAdapter.so in namespace classloader-namespace at java.lang.Runtime.loadLibrary0(Runtime.java:1077) at java.lang.Runtime.loadLibrary0(Runtime.java:998) at java.lang.System.loadLibrary(System.java:1661) at cn.robotpen.views.adapter.RbtBezierAdapter.(SourceFile:14)


看到报错信息中libRbtBezier.so路径是错的,错误的路径为第2步 CMakeList.txt中的绝对路径。

但是我是用CMake编译libRbtBezier是没有问题的,然后我就对比CMake和XMake编译的详细输出,发现最后链接时CMake多了`-Wl,-soname,libRbtBezier.so`,我就在xmake.lua增加`add_shflags("-Wl,-soname,libRbtBezier.so")`,然后就可以用了。

### 期待的结果

虽然我通过`add_shflags("-Wl,-soname,libRbtBezier.so")`解决了这个问题,但我感觉我是误打误撞弄好的,并不清楚底层到底什么问题导致崩溃。如果是我使用错误导致的,在此表示抱歉,打扰了

### 工程配置

_No response_

### 附加信息和错误日志

_No response_
waruqi commented 1 year ago

看到报错信息中libRbtBezier.so路径是错的,错误的路径为第2步 CMakeList.txt中的绝对路径。

不要指定绝对路径,走 links linkdirs 。

或者全用 xmake.lua ,jni 也是。。xmake 有提供 gradle 插件,自动编译 jni 和打包

https://github.com/xmake-io/xmake-gradle

CaffreySun commented 1 year ago

我在网上查了一下-Wl,-soname

-Wl选项告诉编译器将后面的参数传递给链接器。 -soname则指定了动态库的soname(短名,Short for shared object name) -Wl 表示后面的参数也就是-soname,libhello.so.1直接传给连接器ld进行处理。每一个库都有一个soname,当连接器发现程序库(如:libhello_v1.so)中有soname (libhello.so),连接器便会将soname (libhello.so)嵌入要连接的二进制文件内,而不是库文件的实际文件名(libhello_v1.so)。在程序执行期间,程序会查找拥有 soname名字(libhello.so)的文件,而不是库的文件名,换句话说,soname是库的区分标志。

我这两天又查了一下如何解决我遇到问题:

CMake 用 add_library(XXX SHARED IMPORTED) 并通过 IMPORTED_LOCATION 指定库的本地路径导入so编译。会先看这个 so 有没有 SONAME,如果有的话就把路径截断并通过 -L/path/libtest.so -ltest来链接,如果没有的话就会用全路径 -l/path/libtest.so 来链接。这会导致在链接生成的文件里面 NEEDED 字段一个是 so,一个是全路径so。全路径的 libtest.so 显然是没法在我们设置的 LD_LIBRARY_PATH 里面找到的,所以会报个 not found 的错误,这就是加载不到so的原因了。

之前提到的add_shflags("-Wl,-soname,libRbtBezier.so")是一种方案,这样CMake就可以使用soname正确的链接。 另一种方案时CMakeList.txt中增加set_property(TARGET test PROPERTY IMPORTED_NO_SONAME 1),设置 IMPORTED_NO_SONAME 变量,显式的告诉 CMake 始终都把 lib full path 截断。

xmake是否需要同CMake一样在编译jni 动态库时添加动态库链接选项-Wl,-soname,,这样就方便编译出来的so库使用。

waruqi commented 1 year ago

不考虑,你可以自己设置。。add_shflags("-Wl,-soname,libRbtBezier.so") 。。另外,我刚说了。你 cmakelists 不去设置 绝对路径来 link 不走 IMPORTED_LOCATION 设置,也就没这个问题。。

我这也经常用于 android so库,从没有这种问题。