繁体   English   中英

在Linux上运行Java调用本机.so时发生永久性UnsatisfiedLinkError

[英]Persistent UnsatisfiedLinkError while running Java invoking native .so on Linux

我正在尝试获取一个小型/示例Java应用程序,该应用程序使用在Linux上运行的JNI调用来调用本机C ++代码。

我使用Eclipse构建,配置环境并运行该应用程序。

我已将“总体”项目分为2个单独的Eclipse项目:1个Java项目和1个C ++项目,其中包含本机代码。

Eclipse项目视图

Java部分包括:1:一个“主”类,从中调用“ adapter”类以加载.so库并调用本机接口/ C ++方法2:一个“ adapter”类,其中包含本机方法声明

public class Java_Main_For_So_Kickoff {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println("Hello from Java main!");

        Native_Cpp_Adapter adapter = new Native_Cpp_Adapter();

        adapter.locSetProperty();
        adapter.locLoadLib();

        adapter.sayHello();

        adapter.test_Kickoff_So_For_Print();

    }

}


public class Native_Cpp_Adapter {   
    public native void test_Kickoff_So_For_Print();

    public void locSetProperty() {
        "/home/adminuser/workspace_Unit_Test_Java_Cpp/Unit_Test_Cpp/Debug");
        String libPathProp = System.getProperty("java.library.path");

        System.out.println("lib path set:" + libPathProp);
    }

    public void locLoadLib() {
        System.setProperty("java.library.path", 
        "//home//adminuser//workspace_Unit_Test_Java_Cpp//Unit_Test_Cpp//Debug//");
        String libPathProp = System.getProperty("java.library.path");
        String soLibName = "libUnit_Test_Cpp.so";
        String soLibWithPath = libPathProp.concat(soLibName);

        System.out.println("lib path set for loading:" + libPathProp);
        System.load(soLibWithPath);

        System.out.println("library loaded");
    }

    public void sayHello() {
        System.out.println("Hello from JNI Adapter (Java part)");
    }
}

C ++部分包括:1:一个用javah生成的* .h文件,包含本机方法定义2:一个* .cpp文件,包含本机方法的C ++实现

两者都非常初级,仅意味着对JNI环境设置进行完整性测试。 添加了此内容,在此问题的原始帖子中省略了此内容。

.h文件:Native_Cpp_adapter.h

     /*
 * Native_Cpp_Adapter.h
 *
 *  Created on: Aug 23, 2017
 *      Author: adminuser
 */

#ifndef SRC_NATIVE_CPP_ADAPTER_H_
#define SRC_NATIVE_CPP_ADAPTER_H_

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Native_Cpp_Adapter */

#ifndef _Included_Native_Cpp_Adapter
#define _Included_Native_Cpp_Adapter
#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT void JNICALL Java_Native_Cpp_Adapter_test_Kickoff_So_For_Print
  (JNIEnv *, jobject);


#ifdef __cplusplus
}
#endif
#endif





#endif /* SRC_NATIVE_CPP_ADAPTER_H_ */

.cpp文件:Native_Cpp_Adapter.cpp

#include "jni.h"
#include <iostream>

using namespace std;

JNIEXPORT void JNICALL Java_Native_Cpp_Adapter_test_Kickoff_So_For_Print
 (JNIEnv *, jobject)
{
    cout << "Correct kickoff of Native JNI method nr. 1";
    return;
}

从C ++项目中构建了一个.so,包括jni.h和jni_md.h。 对于Java项目,生成的.so作为外部库包含在Java构建路径中,并作为VM参数“ java.library.path”添加到运行配置中(适用于Java项目/主类)。

首先,.so是从C ++项目构建的:这会在公共工作空间内的C ++项目文件夹中生成一个.so二进制文件。 Java构建已执行。 此后,将运行Java程序并调用项目的主类。

结果:.so似乎已加载,但是此后该过程中止,并出现(非常持久)“ UnsatisfiedLinkError”。

据我了解,如果Java虚拟机找不到声明为native的方法的适当的本地语言定义,则会引发此错误。

这听起来像本机方法定义(C ++端)与Java端的本机方法声明不兼容。 但是据我所知/知道,C ++定义完全符合Java中的本机方法声明以及适用的本机方法命名约定。 对我而言,似乎所有的构建/配置选项都已用尽-是否有人建议可能是什么原因,并且要解决此问题?

看来您的本机方法名称无效。 确保正确逃脱_ _用于在本机方法中分隔软件包。 您需要遵循命名约定:

https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names

如果方法名称中包含“ _”,则需要在本机代码中使用“ _1”对其进行转义。

例。 对于Java中的方法:

public static native void display_Message();

你需要:

JNIEXPORT void JNICALL Java_recipeNo001_HelloWorld_display_1Message
  (JNIEnv *, jclass);

注意在“ 显示 ”和“ 消息 ”之间的“ _1 ”。

从此处获取(并稍作修改)的来源: http//jnicookbook.owsiak.org/recipe-No-001/

更新资料

您应该注意的地方:

  1. 如果其他所有操作均失败,请确保在运行Eclipse之前设置LD_LIBRARY_PATH 这将为您提供一些有关Eclipse是否讨厌或其他问题的见解。
  2. 启动代码时,请确保使用“ -D”传递java.library.path 您可以在Debug配置中将其设置为JVM参数
  3. 有时,这可能很棘手,并且可能导致您的lib根本不包含符号。 您可以使用nm检查它

     nm libSomeLibFile.so 
  4. 您还可以在Eclipse中的项目的Properties配置中设置本机代码位置

    在此处输入图片说明

更新。 我稍微简化了您的代码,使检查问题更容易。 我建议您从类名中删除“ _”,并将它们再次混入本机代码中。

在这里看看:

public class Main {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println("Hello from Java main!");
        NativeCppAdapter adapter = new NativeCppAdapter();
        adapter.locLoadLib();
        adapter.testKickoffSoForPrint();
    }
}

本机适配器类

public class NativeCppAdapter {
    public native void testKickoffSoForPrint();

    public void locLoadLib() {
        String soLibName = "/tmp/libNativeCppAdapter.so";
        System.load(soLibName);
    }
}

C ++代码(注意C导出-它会影响函数名称!)

#include "jni.h"
#include <iostream>

using namespace std;

#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     NativeCppAdapter
 * Method:    testKickoffSoForPrint
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_NativeCppAdapter_testKickoffSoForPrint
  (JNIEnv *, jobject) {

    cout << "Correct kickoff of Native JNI method nr. 1";
    return;

}

#ifdef __cplusplus
}
#endif

编译代码和Java

> javac *.java
> c++ -g -shared -fpic -I${JAVA_HOME}/include \
-I${JAVA_HOME}/include/darwin \
NativeCppAdapter.cpp \
-o /tmp/libNativeCppAdapter.so
> java -cp . Main
Hello from Java main!
Correct kickoff of Native JNI method nr. 1

再说几句话

如果编译代码时没有

#ifdef __cplusplus
extern "C" {
#endif
...
#ifdef __cplusplus
}
#endif

您的库中的符号将不正确(不是JVM期望的)。

另一件事是。 如果使用“ loadLibrary”,则必须确保文件名的格式为:lib SomeName .so,然后通过System.loadLibrary("SomeName");加载文件System.loadLibrary("SomeName"); -并且您必须确保java.library.pathLD_LIBRARY_PATH指向该文件。 另一方面,如果使用System.load ,则无需对名称进行任何假设。 只要确保您提供文件的完整路径即可。 例如: /tmp/someFileWithMyLib

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM