本机库加载,但调用本机函数会抛出 UnsatisfiedLinkError

发布于 2025-01-17 22:59:37 字数 2687 浏览 4 评论 0原文

我构建了一个本机库 libmynative.so,它公开了一个简单的函数:

public class MyWrapper {
    public static native double MyCalculation(double a, double b);

    static {
        System.loadLibrary("mynative");
    }
}

当我编译 MyWrapper.java、将 .so 和 .class 文件放在一起并运行简单的测试时,该函数运行良好与 java -Djava.library.path=。 MyWrapper(当此类具有 main 方法时)。

但我想将包装器和本机库打包到 .jar 文件中,并通过让包装器提取 .so 文件并在运行时从临时目录加载它来使用它。我的代码基本上是:

public class MyWrapper {
    public static native double MyCalculation(double a, double b);

    private static File tempDir = null;

    static {
        try {
           tempDir = Files.createTempDirectory("foo").toFile();
           tempDir.deleteOnExit();
        } catch (IOException e) {
           System.out.println("Couldn't create temporary directory");
        }

        try {
            InputStream inStream = OpenDP.class.getResourceAsStream("/libmynative.so");

            // Create the temp file in the filesystem
            File temp = new File(tempDir.getPath() + "/libmynative.so");
            temp.createNewFile();
            temp.deleteOnExit();
            FileOutputStream outStream = new FileOutputStream(temp);

            // Copy from the .jar to the filesystem
            try {
                byte[] buffer = new byte[256 * 1024];
                int bytesRead;
                while ((bytesRead = inStream.read(buffer)) >= 0) {
                    outStream.write(buffer, 0, bytesRead);
                }
            } finally {
                outStream.close();
                inStream.close();
            }

            // Try to load library from extracted native resources
            String p = tempDir.getAbsolutePath() + "/libmynative.so";
            System.load(p);
        } catch (Exception e) {
            throw new UnsatisfiedLinkError(e.getMessage());
        }
    }

我可以在独立应用程序中调用我的本机函数,因此函数签名很好。

我构建了一个 jar 文件,其包目录中包含 MyWrapper.class,根目录中包含 libmynative.so。然后我运行一个简单的测试项目(使用 java -cp ".:MyJar.jar" MyTestProject):

import com.myorganization.MyWrapper;
public static void main(String[] args) {
    System.out.println(MyWrapper.MyCalculation(1.0, 2.0));
}

这失败了:

Exception in thread "main" java.lang.UnsatisfiedLinkError: com.myorganization.MyWrapper.MyCalculation(DD)D
        at com.myorganization.MyWrapper.MyCalculation(NativeMethod)
        ...

我已经验证了 System.load code> 调用不会抛出异常;调用函数时会出现问题。我不是这里的专家,我最好的猜测是 java.library.path 未设置有问题?我怎样才能调试这里发生的事情?或者有一个简单的答案吗?

I built a native library, libmynative.so, that exposes a single, straightforward function:

public class MyWrapper {
    public static native double MyCalculation(double a, double b);

    static {
        System.loadLibrary("mynative");
    }
}

This works fine when I compile MyWrapper.java, have the .so and .class files together, and run a simple test with java -Djava.library.path=. MyWrapper (when this class has a main method).

But I want to package both the wrapper and native library into a .jar file and use it by having the wrapper extract the .so file and load it from a temp directory at runtime. My code is basically:

public class MyWrapper {
    public static native double MyCalculation(double a, double b);

    private static File tempDir = null;

    static {
        try {
           tempDir = Files.createTempDirectory("foo").toFile();
           tempDir.deleteOnExit();
        } catch (IOException e) {
           System.out.println("Couldn't create temporary directory");
        }

        try {
            InputStream inStream = OpenDP.class.getResourceAsStream("/libmynative.so");

            // Create the temp file in the filesystem
            File temp = new File(tempDir.getPath() + "/libmynative.so");
            temp.createNewFile();
            temp.deleteOnExit();
            FileOutputStream outStream = new FileOutputStream(temp);

            // Copy from the .jar to the filesystem
            try {
                byte[] buffer = new byte[256 * 1024];
                int bytesRead;
                while ((bytesRead = inStream.read(buffer)) >= 0) {
                    outStream.write(buffer, 0, bytesRead);
                }
            } finally {
                outStream.close();
                inStream.close();
            }

            // Try to load library from extracted native resources
            String p = tempDir.getAbsolutePath() + "/libmynative.so";
            System.load(p);
        } catch (Exception e) {
            throw new UnsatisfiedLinkError(e.getMessage());
        }
    }

I can call my native function in a standalone app, so the function signature is fine.

I build a jar file with MyWrapper.class in its package directory and libmynative.so in the root. Then I have a simple test project that I run (using java -cp ".:MyJar.jar" MyTestProject):

import com.myorganization.MyWrapper;
public static void main(String[] args) {
    System.out.println(MyWrapper.MyCalculation(1.0, 2.0));
}

This fails with:

Exception in thread "main" java.lang.UnsatisfiedLinkError: com.myorganization.MyWrapper.MyCalculation(DD)D
        at com.myorganization.MyWrapper.MyCalculation(NativeMethod)
        ...

I've verified that the System.load call doesn't throw an exception; the problem occurs when the function is invoked. My best guess, not being an expert here, is that there's an issue with the java.library.path which isn't set? How can I debug what's going on here? Or is there a simple answer?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

苍景流年 2025-01-24 22:59:37

Java期望的功能的名称可能不是库中函数的名称。

您可以使用-Xlog:library = info vm flag(JDK 15+)来查看JVM实际上正在尝试加载哪个符号。由于您获得了noteStiedlinkError它应该在最后打印出类似的内容:

[0.622s][info][library] Failed to find Java_my_package_MyWrapper_MyCalculation in library with handle 0x00007ff8a24f0000

然后,您可以使用nm来确保该符号实际上在库中:

nm libmynative.so | grep Java_my_package_MyWrapper_MyCalculation

和我猜你会发现不是。

在这种情况下,解决方案是将库中的函数名称更改为Java的期望。一种方法是用javahjavac -h再生标头文件,该文件将具有正确的名称,然后从该标头文件实现该函数。

It is likely that the name of the function that Java expects is not the name of the function inside the library.

You can use the -Xlog:library=info VM flag (JDK 15+) to see which symbol the JVM is actually trying to load. Since you're getting an UnsatisfiedLinkError it should print something like this at the end:

[0.622s][info][library] Failed to find Java_my_package_MyWrapper_MyCalculation in library with handle 0x00007ff8a24f0000

Then, you can use nm to make sure that that symbol is actually in the library:

nm libmynative.so | grep Java_my_package_MyWrapper_MyCalculation

And I'm guessing you'll find that it isn't.

The solution in that case is to change the name of the function in the library to be what Java expects. One way to do that is to regenerate the header file with javah or javac -h, which will have the right name, and then implement the function from that header file.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文