C++ 库的 JNI 包装器和未定义的符号错误
Posted
技术标签:
【中文标题】C++ 库的 JNI 包装器和未定义的符号错误【英文标题】:JNI wrapper for C++ libraries and undefined symbol error 【发布时间】:2014-10-30 18:55:43 【问题描述】:我想编写一个简单的 JNI 包装器,用于使用具有 C++ API 的 Festival 文本到语音(或任何其他)库。我有以下文件:
Main.java:
public class Main
static
System.loadLibrary("TTSWrapper");
private native void FestivalSayHello();
public static void main(String[] args)
new Main().FestivalSayHello();
生成的 Main.h:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Main */
#ifndef _Included_Main
#define _Included_Main
#ifdef __cplusplus
extern "C"
#endif
/*
* Class: Main
* Method: FestivalSayHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_Main_FestivalSayHello
(JNIEnv *, jobject);
#ifdef __cplusplus
#endif
#endif
FestivalWrapper.cpp:
#include <iostream>
#include <jni.h>
#include "Main.h"
#include "include/festival.h"
JNIEXPORT void JNICALL Java_Main_FestivalSayHello(JNIEnv *env, jobject thisObj)
EST_Wave wave;
int heap_size = 210000; // default scheme heap size
int load_init_files = 1; // we want the festival init files loaded
festival_initialize(load_init_files,heap_size);
festival_say_file("/etc/motd");
festival_eval_command("(voice_ked_diphone)");
festival_say_text("hello world");
festival_text_to_wave("hello world",wave);
wave.save("output.wav","riff");
festival_wait_for_spooler();
return;
制作文件:
CLASS_PATH = ../bin
all : libTTSWrapper.so
libTTSWrapper.so : FestivalWrapper.o Main.h
g++ -m64 -fPIC -shared -o $@ $<
FestivalWrapper.o : FestivalWrapper.cpp Main.h
g++ -fPIC \
-I"/usr/lib/jvm/java-7-openjdk-amd64/include" \
-I"/home/TTS_tools/Festival/festival/src" \
-I"/home/TTS_tools/Festival/speech_tools/include" \
-L"/home/TTS_tools/Festival/festival/src/lib" \
-L"/home/TTS_tools/Festival/speech_tools/lib" \
-lFestival \
-lestools \
-lestbase \
-leststring \
-lncurses \
-c $< \
-o $@
Main.h : Main.class
javah -classpath $(CLASS_PATH) $*
运行时出现以下错误:
/usr/lib/jvm/java-7-openjdk-amd64/bin/java: symbol lookup error: /home/workspace/TTS/jni/libTTSWrapper.so: undefined symbol: _ZN8EST_WaveC1Ev
您能帮我找出问题所在吗?而且我不确定是否需要 -m64 和 -fPIC。
注意 1:nm libTTSWrapper.so | grep EST_Wave
列出了导致错误的符号,即使我删除了 -L 和 -l 行。
注 2:Festival 是静态库的组合。
注 3:这个问题不是特定于库的,我在另一个库 (Ekho) 上也遇到了同样的问题。问题是我不知道如何链接第三方库。
更新:我仍然不确定,但我想问题是因为 Festival 没有与 -fPIC
选项相关联。尽管这些符号出现在生成的共享库中,但我认为它们无法正确引用。当我试图制作一个使用链接到静态库的共享库的简单可执行文件时,我达到了这一点,在纯 C 中。我必须从构建的目标文件创建 .a 文件-fPIC
选项,共享库也需要-fPIC
选项。
【问题讨论】:
问题对外部库来说太具体了..您需要提供代码..错误是什么?是代码造成的吗?只是您的makefile找不到文件吗?等等。仅靠 makefile 是不够的。 确切的错误总是相关的 @Brandon:我已经编辑了问题,并添加了源代码。 您可以使用JavaCPP 之类的工具让这一切变得更简单 - 而不会牺牲性能。 【参考方案1】:我不确定你的代码是如何打包的,但是包名对 JNI 来说非常重要。另外我不建议使用默认包(在任何时候,但尤其是在这种情况下:p)
假设你的包被调用
package groove.FestivaWrapper
那么您的 JNI 调用必须如下所示
groove_FestivalWrapper_FestivalSayHello
否则 JNI 将不知道调用什么。
【讨论】:
如果出现你所说的包问题,还能编译吗? 如果我没记错的话是的。如果 .h 与 .cpp 匹配,则编译器不会抱怨。然后在运行时 JNI 运行时将找不到任何东西并导致难以调试的痛苦。 但是,您收到的错误消息可能不是问题。【参考方案2】:你有两个问题。第一个问题是,当您构建FestivalWrapper.o
时,它忽略 -L
和-l
选项——它们仅由链接器使用,而不是由编译器使用。这些选项需要传递给链接器。你的 makefile 应该看起来更像:
CLASS_PATH = ../bin
all : libTTSWrapper.so
libTTSWrapper.so : FestivalWrapper.o Main.h
g++ -m64 -fPIC -shared -o $@ $< \
-L"/home/TTS_tools/Festival/festival/src/lib" \
-L"/home/TTS_tools/Festival/speech_tools/lib" \
-lFestival \
-lestools \
-lestbase \
-leststring \
-lncurses
FestivalWrapper.o : FestivalWrapper.cpp Main.h
g++ -fPIC \
-I"/usr/lib/jvm/java-7-openjdk-amd64/include" \
-I"/home/TTS_tools/Festival/festival/src" \
-I"/home/TTS_tools/Festival/speech_tools/include" \
-c $< \
-o $@
Main.h : Main.class
javah -classpath $(CLASS_PATH) $*
即您必须将-L
和-l
选项移动到生成.so
文件的项目,即执行链接时。
你的第二个问题是你没有用-fPIC
编译节日。是的,这将阻止链接,因为 all .so
64 位文件必须使用 -fPIC
编译,如果它们是 ABI 编译器。您会收到如下错误:
/usr/bin/ld: ../festival/src/lib/libFestival.a(festival.o): relocation R_X86_64_32 against `.rodata.str1.1' can not be used when making a shared object; recompile with -fPIC
../festival/src/lib/libFestival.a: error adding symbols: Bad value
这些库不遵循标准配置约定 - 即在运行配置脚本之前尝试设置 CFLAGS
根本不起作用。
使用-fPIC
构建speech_tools
和festival
用于您执行的.a
文件:
export CC_OTHER_FLAGS=-fPIC
在为每个包运行 configure
之前。这将确保-fPIC
标志被传递到编译的所有阶段。
【讨论】:
感谢您的详细回答。不幸的是,“export...”命令不适用于 Ekho TTS 库。因此,如果我没有做错任何事情,恐怕此过程不适用于所有图书馆。 speech_tools 和节日包不遵循configure
/autoconf
规范,这就是为什么在构建它们时最终必须使用 CC_OTHER_FLAGS
以获得 -fPIC
标志建造。 Ekho TTS 使用标准的 autoconf 机制,因此设置 export CXXFLAGS="-O -fPIC"
和 export CFLAGS="-O -fPIC"
应该对该库具有预期的效果。以上是关于C++ 库的 JNI 包装器和未定义的符号错误的主要内容,如果未能解决你的问题,请参考以下文章