用于C ++库的JNI包装器和未定义的符号错误

时间:2014-10-30 18:55:43

标签: java c++ java-native-interface undefined-symbol

我想编写一个简单的JNI包装器,用于使用具有C ++ API的Festival text-to-speech(或任何其他)库。我有以下文件:

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选项。

2 个答案:

答案 0 :(得分:0)

我不确定您的代码是如何打包的,但包名对JNI非常重要。另外,我不建议使用默认包(在任何时候,尤其是在这种情况下:p)

假设您的包被调用 package groove.FestivaWrapper

然后你的JNI电话必须看起来像这样 groove_FestivalWrapper_FestivalSayHello

否则JNI将无法确定要拨打的电话。

答案 1 :(得分:0)

你有两个问题。第一个问题是,当您构建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编辑节日。是的,这会阻止链接,因为所有 .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

库不遵循标准配置约定 - 即在运行configure脚本之前尝试设置CFLAGS根本不起作用。

使用speech_toolsfestival个文件构建-fPIC.a

export CC_OTHER_FLAGS=-fPIC

在为每个包运行configure之前。这将确保将-fPIC标志传递到编译的所有阶段。