Java 加载带有JNI:未定义符号的C插件系统
我想在Linux的Java程序中运行我的C插件系统,我已经在Java程序中编写了绑定和库加载 插件系统提供了一个加载插件的命令,该命令被包装成Java,这样我就可以在我的Java shell中调用I,它就可以工作了 当插件试图从插件系统运行命令时,出现了一个未定义的符号错误。然而,我已经仔细地将插件使用的所有函数链接到库中,系统是使用Java 加载带有JNI:未定义符号的C插件系统,java,c,dll,Java,C,Dll,我想在Linux的Java程序中运行我的C插件系统,我已经在Java程序中编写了绑定和库加载 插件系统提供了一个加载插件的命令,该命令被包装成Java,这样我就可以在我的Java shell中调用I,它就可以工作了 当插件试图从插件系统运行命令时,出现了一个未定义的符号错误。然而,我已经仔细地将插件使用的所有函数链接到库中,系统是使用-fPIC和-shared编译的。我还使用objdump-T查看了符号表,并列出了函数 我觉得这个过程有点疯狂,我加载了C代码,用Java加载了C代码。。。可能吗
-fPIC
和-shared
编译的。我还使用objdump-T
查看了符号表,并列出了函数
我觉得这个过程有点疯狂,我加载了C代码,用Java加载了C代码。。。可能吗
如果有人曾经遇到过这个问题,或者想分享关于可能的解决方案的想法,我将非常感谢
多谢各位
编辑:在这个例子中,我尽可能地模仿我的程序。我也得到了一个错误,但它是完全不同的
// plugin_system.c
#include <dlfcn.h>
#include <stdio.h>
int load_and_run_plugin() {
void *lib = dlopen("/home/kowa/code/c/test_dir/jni/plug/plugin.so", RTLD_LAZY);
if (lib == NULL) {
fprintf(stderr, "Cannot open library.\n");
return -1;
}
void (*plug_func)(void) = dlsym(lib, "plug_function");
if (plug_func == NULL) {
dlclose(lib);
fprintf(stderr, "Cannot find plugin function.\n");
return -1;
}
plug_func();
return 0;
}
// PluginSystem.c
#include "PluginSystem.h"
#include "plugin_system.h"
/*
* Class: PluginSystem
* Method: cmdPlug
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_PluginSystem_cmdPlug
(JNIEnv *env, jclass this)
{
printf("Calling load_and_run function...\n");
load_and_run_plugin();
printf("End of call to load_and_run function\n");
}
// plugin_user_api.c
#include <stdio.h>
static void verbose_stdout(char *str)
{
printf("%s", str);
}
void (*verbose) (char *str) = verbose_stdout;
// plugin.c
#include "plugin_user_api.h"
int plug_function() {
verbose("I'm a plugin and I use external functions.\n");
return 0;
}
// PluginSystem.java
public class PluginSystem
{
static {
System.load("/home/kowa/code/c/test_dir/jni/plug/plugin_system.so");
}
public static native void cmdPlug();
public static void main(String args[]) {
PluginSystem.cmdPlug();
}
}
// Makefile
JAVA_HOME = /usr/lib/jvm/java-8-openjdk
C_INCLUDE_PATH = $(JAVA_HOME)/include $(JAVA_HOME)/include/linux
INCLUDE = $(foreach i, $(C_INCLUDE_PATH), -I$i)
CFLAGS = -Wall -O2 -std=gnu99
all:
gcc $(CFLAGS) -fPIC -c plugin.c
gcc $(CFLAGS) -Wl,-soname,plugin.so -shared -o plugin.so plugin.o
gcc $(CFLAGS) $(INCLUDE) -fPIC -c plugin_system.c
gcc $(CFLAGS) $(INCLUDE) -fPIC -c plugin_user_api.c
gcc $(CFLAGS) $(INCLUDE) -fPIC -c PluginSystem.c
gcc $(CFLAGS) -Wl,-soname,plugin_system.so -shared -o plugin_system.so plugin_system.o plugin_user_api.o PluginSystem.o
sed -i "s:System.load(.*):System.load(\"$(shell echo `pwd`)/plugin_system.so\"):" PluginSystem.java
javac PluginSystem.java
clean:
rm *.o *.so *.class
日志文件很长(654行!),所以不要怪我没有发布它 您的示例代码适用于我,但有以下注意事项:
- 我运行了
来生成自己版本的javah
,并编写了自己的PluginSystem.h
,因为这些都没有发布plugin\u system.h
- 我将硬编码的共享库路径更改为适合本地系统的路径
- 为了方便起见,我将
合并到plugin\u-user\u-api.c
中,并删除了plugin.c
(反正没有提供)plugin\u-user\u-api.h
- 我用更惯用的风格重写了你的
Makefile
$ /usr/lib/jvm/java-1.8.0/bin/java PluginSystem
Calling load_and_run function...
I'm a plugin and I use external functions.
End of call to load_and_run function
更新: 如果不是将
plugin\u user\u api.c
合并到plugin.c
中,而是将其合并到plugin\u system.c
,我就能够复制您的问题。在这种情况下,我可以实现一个C驱动程序能够加载插件的结果,但Java无法这样做。这表明Java正在加载带有RTLD_LOCAL
标志的库,这样它的符号就不会暴露给随后加载的库,比如插件
至少有三种可能的解决方案:
- 在Java和
使您能够控制plugin\u系统之间插入另一个加载程序。因此
的符号plugin\u系统的范围。因此
- 将插件所需的所有支持功能移出plugin_system.so,并放入一个单独的动态库中,所有插件都可以链接到该库
- 通过让插件系统使用指向所需函数和数据的指针来初始化插件,从而使插件不再需要与插件系统动态链接
#包括
#包括
#包括“plugin_system.h”
typedef int(*init_函数)(结构插件_上下文*);
静态无效详细数据(char*str)
{
printf(“%s”,str);
}
静态结构插件上下文={.verbose=verbose\u stdout};
int load_和_run_插件(){
void*lib=dlopen(“/home/kowa/code/c/test\u dir/jni/plug/plugin.so”,
RTLD_LAZY | RTLD_LOCAL);
if(lib==NULL){
fprintf(stderr,“无法打开库。\n”);
返回-1;
}
init_函数plug_init=dlsym(lib,“plug_init”);
if(plug_init==NULL){
dlclose(lib);
fprintf(stderr,“找不到插件初始化函数。\n”);
返回-1;
}
plug_init(&context);//省略错误检查
void(*plug_func)(void)=dlsym(lib,“plug_函数”);
if(plug_func==NULL){
dlclose(lib);
fprintf(stderr,“找不到插件函数。\n”);
返回-1;
}
plug_func();//忽略错误检查
返回0;
}
plugin.c
包括
#包括“plugin_system.h”
静态无效(*详细)(字符*str);
int plug_init(结构插件_上下文*上下文){
详细=上下文->详细;
返回0;
}
int plug_函数(){
如果(详细){
详细(“我是一个插件,使用外部函数。\n”);
返回0;
}否则{
返回-1;
}
}
我看不出有任何理由说明像您正在尝试做的事情是不可能的。至于你的案子到底出了什么问题,你还没有给我们提供任何线索。如果你准备了a,你获得帮助(或帮助自己)的机会会大大增加。我试图在帖子中添加的示例中吸取系统的精华,但我得到了不同的错误,因此我可能在该示例中犯了错误。我发布的示例不太准确,它可能适用于较小的项目,我不知道,但在我的项目中,链接器无法解析插件系统库中函数的链接。一种可能的解决方案是将函数指针发送到插件,其缺点是,如果一个函数从同一阶段调用另一个函数,链接器仍然会失败。似乎System.load
加载库时,符号仅出现在同一命名空间和插件中
$ /usr/lib/jvm/java-1.8.0/bin/java PluginSystem
Calling load_and_run function...
I'm a plugin and I use external functions.
End of call to load_and_run function
#ifndef PLUGIN_SYSTEM_H
#define PLUGIN_SYSTEM_H
struct plugin_context {
void (*verbose) (char *str);
};
#endif
#include <dlfcn.h>
#include <stdio.h>
#include "plugin_system.h"
typedef int (*init_function)(struct plugin_context *);
static void verbose_stdout(char *str)
{
printf("%s", str);
}
static struct plugin_context context = { .verbose = verbose_stdout };
int load_and_run_plugin() {
void *lib = dlopen("/home/kowa/code/c/test_dir/jni/plug/plugin.so",
RTLD_LAZY | RTLD_LOCAL);
if (lib == NULL) {
fprintf(stderr, "Cannot open library.\n");
return -1;
}
init_function plug_init = dlsym(lib, "plug_init");
if (plug_init == NULL) {
dlclose(lib);
fprintf(stderr, "Cannot find plugin initialization function.\n");
return -1;
}
plug_init(&context); // error checking omitted
void (*plug_func)(void) = dlsym(lib, "plug_function");
if (plug_func == NULL) {
dlclose(lib);
fprintf(stderr, "Cannot find plugin function.\n");
return -1;
}
plug_func(); // error checking omitted
return 0;
}
include <stdio.h>
#include "plugin_system.h"
static void (*verbose) (char *str);
int plug_init(struct plugin_context *context) {
verbose = context->verbose;
return 0;
}
int plug_function() {
if (verbose) {
verbose("I'm a plugin and I use external functions.\n");
return 0;
} else {
return -1;
}
}