Maven工程下Java加载so包no suitable image found unknown file type排查与处理

问题产生背景

使用IDE为idea intellij,在SpringBoot项目中需要用到native方法,因此需要依赖so包(Shared Object Library)。考虑把so包放在项目的resource目录下,并通过System.loadLibrary方法加载包。加载时路径配置有两种方法。

  1. 启动系统通过-D参数指定绝对位置。
  2. 系统运行时内部代码通过相对位置定位。

2在运行时通过相对位置加载提高灵活性,因此采用2的方式,并将包放在resource目录下。

/xxx/src/main/resources/lib/
├── libXXX.dylib
└── libXXX.so

加载so包代码如下


public class XXXDecoder {
    public native String en(String input);
    public native String de(String input);

    static {
        try {
            initLibPath();
            System.loadLibrary("XXX");
        } catch (Exception e) {
            logger.error("loadLibraryException", e);
        }
    }

    private static void initLibPath() throws Exception {
        String path = XXXDecoder.class.getResource("/").getPath() + "lib";
        System.setProperty("java.library.path", path);
        final Field sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
        sysPathsField.setAccessible(true);
        sysPathsField.set(null, null);

    }
}

但启动项目后确暴出如下错误,导致无法正常使用。

java.lang.UnsatisfiedLinkError
no suitable image found.
unknown file type, first eight bytes

Caused by: java.lang.UnsatisfiedLinkError: /xxx/target/classes/lib/xxx.so: dlopen(/xxx/target/classes/lib/xxx.so, 1): no suitable image found.  Did find:
	/xxx/target/classes/lib/xxx.so: unknown file type, first eight bytes: 0xEF 0xBF 0xBD 0xEF 0xBF 0xBD 0xEF 0xBF
	/xxx/target/classes/lib/xxx.so: unknown file type, first eight bytes: 0xEF 0xBF 0xBD 0xEF 0xBF 0xBD 0xEF 0xBF

问题分析

前文提到加载有两种方式,其中一种可以通过-D参数指定绝对置设置。于是尝试指定绝对位置看是否会产生问题。通过两种方式对比来分析问题。

通过-D指定参数为resource目录下的位置。随后启动系统,发现一切正常。

-Djava.library.path=/xxx/src/main/resources/lib/xxx.so

随后替换参数为target目录的so包位置,并启动依然报 no suitable image found.unknown file type, first eight bytes:

-Djava.library.path=/xxx/target/classes/lib/xxx.so

通过第一次对比,可以复现出正常和异常两种情况。接着要搞清楚unknown file type, first eight bytes:到底什么含义。根据异常提示猜测可能和文件格式虽坏有关系。由于so采用的是ELF格式[1],通常查看ELF需要通过命令readelf命令。

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          200 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           40 (bytes)
  Number of section headers:         8
  Section header string table index: 5

接着通过readelf查看两个so到底有什么差异。

首先查看/xxx/src/main/resources/lib/xxx.so,发现正常显示ELF header,Section等信息。

接着查看target目录下的so包。
readelf -a /xxx/target/classes/lib/xxx.so,结果如下。输出提示中产生警告ELF header发生损坏。

这样结合IDE中报错大概就能够知道问题产生的环节。即项目启动,构建时生成target目录,resource目录下so文件拷贝到 target目录下时产生了这个文件损坏的问题。

ELF 头:
  Magic:  7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  类别:                              ELF64
  数据:                              2 补码,小端序 (little endian)
  版本:                              1 (current)
  OS/ABI:                            UNIX - System V
  ABI 版本:                          0
  类型:                              DYN (共享目标文件)
  系统架构:                          Advanced Micro Devices X86-64
  版本:                              0x1
  入口点地址:              0x18bdbfef
  程序头起点:              4194304 (bytes into file)
  Start of section headers:          -4773833481741467648 (bytes into file)
  标志:             0x0
  本头的大小:       0 (字节)
  程序头大小:       0 (字节)
  Number of program headers:         0
  节头大小:         64 (字节)
  节头数量:         56
  字符串表索引节头: 6
readelf:错误:Reading 3584 bytes extends past end of file for 节头
readelf:错误:Section headers are not available!

接着看一看build到底经历了哪些过程。查阅idea官网可知有4步[2]。

  1. 编译源码将结果输出到配置输出位置。
  2. 编译test中代码将结果输出到配置输出位置。
  3. 将resource文件拷贝到输出路径
  4. 报告产生的问题。

现在就确定了问题就出在步骤3上。由于我的项目是一个maven项目,resource文件拷贝到目标位置并进行相关操作会参照pom中的配置。

当时项目配置如下。查阅官网filtering含义[3]就是将directory目录下含有的占位符进行替换,然后拷贝到输出位置。这样看来一定是由于这条配置导致so包格式被破坏。

        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
            </resource>
        </resources>

在官网filtering[3]介绍页面下可以看到提示。就是说二进制文件如果在resource下并且使用了true配置,那么会导致文件格式被破坏。

As already mentioned filtering binary files like images,pdf`s etc.
could result in corrupted output. To prevent such problems you can
configure file extensions which will not being filtered.

问题解决

终于找到了问题,那么也很好解决。最终根据官方提示configure file extensions[4]说明来进行配置增加过滤项。这样最终就不会对so包进行处理。增加一项配置,重新启动系统,本地及测试环境均正常,问题解决。

                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.0.1</version>
                    <configuration>
                        <nonFilteredFileExtensions>
                            <nonFilteredFileExtension>so</nonFilteredFileExtension>
                            <nonFilteredFileExtension>dylib</nonFilteredFileExtension>
                        </nonFilteredFileExtensions>
                    </configuration>
                </plugin>

参考资料

[1]ELF文件http://akaedu.github.io/book/ch18s05.html
[2]build的涉及的过程https://www.jetbrains.com/help/idea/build-process.html
[3]Maven filter配置https://maven.apache.org/plugins/maven-resources-plugin/examples/filter.html
[4]Maven resource过滤二进制文件https://maven.apache.org/plugins/maven-resources-plugin/examples/binaries-filtering.html

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值