I'm trying to convert a valid C JNI binding to C++ because I want to link to C++ source (open source implementation of an image processing algorithm) from some Java code. I got everything working in C, this includes the Java class, the C version of the JNI binding and the build system and excludes using the C++ source.
I am now trying to convert the JNI binding to C++ so I can add the C++ source to the build system and call it from the binding, but I'm running into an undefined symbol.
My Makefile is pretty straightforward:
GCC = gcc -Wall -g -std=gnu99
CXX = g++ -Wall -g
LIBOBJS = parallelsurf_jni.o
LIB=libparallelsurf.so
DESTLIB=../../lib/$(LIB)
all: $(DESTLIB)
$(DESTLIB): $(LIB)
cp $(LIB) $(DESTLIB)
libparallelsurf.so: parallelsurf_ParallelSURF.h $(LIBOBJS)
ld --shared $(LIBOBJS) -o libparallelsurf.so
parallelsurf_ParallelSURF.h:
echo "Rebuilding JNI headers. Ensure java file has been recently built."
javah -classpath ../../parallelsurf.jar -jni parallelsurf.ParallelSURF
clean:
rm -f $(LIBOBJS) *~ parallelsurf_ParallelSURF.h $(LIB) $(DESTLIB) $(LIBOBJS)
JNI_INCLUDES = -I/usr/lib/jvm/java-6-sun/include/ -I/usr/lib/jvm/java-6-sun/include/linux -I/usr/lib/jvm/java-6-openjdk/include/
%.o: %.cpp
$(CXX) -shared -O2 -c -fPIC -fno-omit-frame-pointer -fno-stack-protector -D_REENTRANT $< $(JNI_INCLUDES)
My header file is thus automatically generated with javah
and I don't appear to have mangled it when copying from .h
to .cpp
. I do not have extern "C"
blocks in the .cpp file, as guided by this post.
I also included a pragma as guided by this post: Undefined Symbol _gxx_personality_v0 on link. The pragma is #pragma GCC java_exceptions
Now, that undefined symbol (gxx_personality) seems to be well referenced/documented on the web. A number of posts point to this comment on C++/Java exception handling, which suggests using that pragma. However, after including that pragma, I get another undefined symbol that is not well covered (I get exactly 1 Google hit): _gcj_personality_v0
Exception in thread "main" java.lang.UnsatisfiedLinkError: /home/chardson/chardson/parallelsurf/lib/libparallelsurf.so: /home/chardson/chardson/parallelsurf/lib/libparallelsurf.so: undefined symbol: __gcj_personality_v0
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1750)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1675)
at java.lang.Runtime.loadLibrary0(Runtime.java:840)
at java.lang.System.loadLibrary(System.java:1047)
at parallelsurf.ParallelSURF.<clinit>(ParallelSURF.java:17)
at testpsurf.<init>(testpsurf.java:11)
at testpsurf.main(testpsurf.java:23)
I'm not sure what to make of this undefined symbol, and I have no idea where else to turn. Perhaps there's just a mistake in my JNI code that could be fixed to get rid of this problem? My JNI C++ code is included below for completeness.
#include <jni.h>
#include <stdio.h>
#include <assert.h>
#pragma GCC java_exceptions
/*
* Class: parallelsurf_ParallelSURF
* Method: parallelsurf_get_keypoints
* Signature: (II[FZ)Ljava/util/ArrayList;
*/
JNIEXPORT jobject JNICALL Java_parallelsurf_ParallelSURF_parallelsurf_1get_1keypoints
(JNIEnv *jenv, jclass jcls, jint width, jint height, jfloatArray gray, jboolean rotInvariant)
{
// init arraylist class
jclass arrayListClass = jenv->FindClass("java/util/ArrayList");
assert(arrayListClass != NULL);
jmethodID alInitMethodId = jenv->GetMethodID(arrayListClass, "<init>", "()V");
assert(alInitMethodId != NULL);
jobject arrayList = jenv->NewObject(arrayListClass, alInitMethodId);
jmethodID alAddMethodId = jenv->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
assert(alAddMethodId != NULL);
// init keypoint class
jclass keypointClass = jenv->FindClass("parallelsurf/Keypoint");
assert(keypointClass != NULL);
jmethodID kpInitMethodId = jenv->GetMethodID(keypointClass, "<init>", "(DDDDID[D)V");
assert(kpInitMethodId != NULL);
jdoubleArray descriptor = jenv->NewDoubleArray(10);
assert(descriptor != NULL);
jdouble vals[10];
for (int i=0; i < 10; i++) {
vals[i] = (jdouble) i;
}
jenv->SetDoubleArrayRegion(descriptor, 0, 10, vals);
// create keypoint
jobject kp = jenv->NewObject(keypointClass, kpInitMethodId,
(jdouble) 1, (jdouble) 2, (jdouble) 3,
(jdouble) 4, (jint) 5, (jdouble) 6,
descriptor);
// add to arraylist
jenv->CallBooleanMethod(arrayList, alAddMethodId, kp);
jenv->DeleteLocalRef(kp);
return arrayList;
}
Thanks for looking. Your help is much appreciated.
Some fine gentleman on the gcc-help mailing list figured this one out:
#pragma GCC java_exceptions
to deal with __gxx_personality_v0, make ld
link in -l gcc_s
ld
link in -l gcj
ld
link -l stdc++
If you're linking with g++ (I'm not), some of these may happen automatically (stdc++?)
Also, make sure you didn't forget (or lose) the #import
line for the JNI header -- you'll need the extern C {}
bits, or you'll get an UnsatisfiedLinkError