I've already asked this on comp.lang.java, but no luck, and I'm on a
deadline, so I hope you'll forgive if you had to read this twice. Anyway,
can anybody help with this?
I am trying to make a C wrapper for my Java code which would then call my
Java application. So for the first try I mostly copied the Sun's tutorial
example on JNI JVM invocation. The code is below. I compile it with
gcc -shared -o
testjni -I/usr/lib/jdk1.3/include -I/usr/lib/jdk1.3/include/linux -L/us
r/lib/jdk1.3/jre/lib/i386 -ljava testjni.c
(the directories are correct AFAICS), and when I run the resulting
executable, I get Segmentation fault. I have tried to put a printf at the
top of the code, before any of the JNI stuff, and it doesn't even reach
that, so I guess it's a compilation/linking problem. I'm running JDK 1.3 and
SuSE 7.1.
What am I doing wrong?
Thanks, people!
Goran
----------
#include <jni.h>
#define USER_CLASSPATH "."
main() {
JNIEnv *env;
JavaVM *jvm;
JDK1_1InitArgs vm_args;
jint res;
jclass cls;
jstring jstr;
jmethodID mid;
jobjectArray args;
char classpath[1024];
vm_args.version = 0x00010200;
JNI_GetDefaultJavaVMInitArgs(&vm_args);
sprintf(classpath, "%s:%s", vm_args.classpath,
USER_CLASSPATH);
vm_args.classpath = classpath;
res = JNI_CreateJavaVM(&jvm, (void *) &env, &vm_args);
if(res < 0) {
fprintf(stderr, "Can't create Java VM\n");
exit(1);
}
cls = (*env)->FindClass(env, "TestJNI");
if(cls == 0) {
fprintf(stderr, "Can't find TestJNI.main\n");
exit(1);
}
mid = (*env)->GetStaticMethodID(env, cls, "main",
"([Ljava/lang/String;)V");
if(mid == 0) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
jstr = (*env)->NewStringUTF(env, "/l:ap.log");
if(jstr == 0) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
args = (*env)->NewObjectArray(env, 1,
(*env)->FindClass(env, "java/lang/String"), jstr);
if(args == 0) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
(*env)->CallStaticVoidMethod(env, cls, mid, args);
(*jvm)->DestroyJavaVM(jvm);
}
Don't specify -shared when you compile. What does "file testjni" say?
Also, you have specified 1.2 but are using a 1.1 style argument
struct. Use the 1.2 struct instead, described here:
http://java.sun.com/j2se/1.3/docs/guide/jni/jni-12.html#JNI_CreateJavaVM
/gordon
--
[ do not send me private copies of your followups ]
g o r d o n . b e a t o n @ e r i c s s o n . c o m
>Don't specify -shared when you compile.
I have to, because otherwise it doesn't compile at all: I get these
messages:
$ gcc -g -o
testjni -I/usr/lib/jdk1.3/include -I/usr/lib/jdk1.3/include/linux -L/usr/li
b/jdk1.3/jre/lib/i386 -ljava testjni.c
/usr/i486-suse-linux/bin/ld: warning: libjvm.so, needed by
/usr/lib/jdk1.3/jre/lib/i386/libjava.so, not found (try using -rpath
or -rpath-link)
/usr/i486-suse-linux/bin/ld: warning: libverify.so, needed by
/usr/lib/jdk1.3/jre/lib/i386/libjava.so, not found (try using -rpath
or -rpath-link)
And then a bunch of "undefined reference" errors from libjava.so.
> What does "file testjni" say?
$ file testjni
testjni: ELF 32-bit LSB shared object, Intel 80386, version 1, not stripped
>Also, you have specified 1.2 but are using a 1.1 style argument
>struct. Use the 1.2 struct instead, described here:
>
> http://java.sun.com/j2se/1.3/docs/guide/jni/jni-12.html#JNI_CreateJavaVM
Did that, didn't help - still getting Segmentation fault. The code crashes
before it runs the first line.
Thanks,
Goran
-------
#include <jni.h>
#define USER_CLASSPATH "."
main() {
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args;
JavaVMOption opts[1];
jint res;
jclass cls;
jstring jstr;
jmethodID mid;
jobjectArray args;
char classpath[1024];
sprintf(classpath, "-Djava.class.path=%s", USER_CLASSPATH);
opts[0].optionString = classpath;
vm_args.options = opts;
vm_args.nOptions = 1;
vm_args.version = JNI_VERSION_1_2;
res = JNI_CreateJavaVM(&jvm, (void *) &env, &vm_args);
if(res < 0) {
fprintf(stderr, "Can't create Java VM\n");
exit(1);
}
cls = (*env)->FindClass(env, "AutoProcessor");
if(cls == 0) {
fprintf(stderr, "Can't find AutoProcessor.main\n");
It's not the right solution - you've told it to build a shared object,
not an executable.
> $ file testjni
> testjni: ELF 32-bit LSB shared object, Intel 80386, version 1, not stripped
There you go.
This works for me:
export JDK=...
gcc -I $JDK/include -I $JDK/include/linux -c test.c
gcc -L $JDK/jre/lib/i386/client test.o -o test -ljvm
You will need to set LD_LIBRARY_PATH when you run the program, or use
some additional link flags (-R) to store the library locations in the
executable itself:
export LD_LIBRARY_PATH=$JDK/jre/lib/i386:$JDK/jre/lib/i386/client
Just a tip...
When any of these functions fail, you can use
if ((*env)->ExceptionOccurred(env)) {
(*env)-> ExceptionDescribe(env);
}
to see the real reason for the failure, instead of just blindly
assuming the problem is "out of memory" every time.
Thanks! This is better... But I still have problems :(
> gcc -I $JDK/include -I $JDK/include/linux -c test.c
> gcc -L $JDK/jre/lib/i386/client test.o -o test -ljvm
This compiles just fine. The Sun's tutorial says to include java library,
but here we use jvm? Why is that? (except for the rather obvious reason that
with -ljava it does not work)?
I exported LD_LIBRARY_PATH, and then run my program, and it broke on
JNI_CreateJavaVM(&jvm, (void *) &env, &vm_args);
saying
Error occurred during initialization of VM
Unable to load native library: shared object not open
What now?
Goran
Well that's a good enough reason for me (I don't consider the tutorial
to be an authoritative source).
Actually -ljava works too if you set the library path before
compiling, but I don't like the compile time dependency on the
environment.
> I exported LD_LIBRARY_PATH, and then run my program, and it broke on
>
> JNI_CreateJavaVM(&jvm, (void *) &env, &vm_args);
>
> saying
>
> Error occurred during initialization of VM
> Unable to load native library: shared object not open
>
> What now?
Recheck the LD_LIBRARY_PATH. You need $JDK/jre/lib/i386, one of the
client or server subdirectories, and one of the threads
subdirectories.
You can use ldd to examine the executable (with the path set) to see
what libraries it depends on and whether the loader can find them. You
shouldn't get any "not found". You can also use ldd on each of those
libraries to see what libraries *they* depend on, and again you
shouldn't get any "not found".
> This works for me:
>
> export JDK=...
>
> gcc -I $JDK/include -I $JDK/include/linux -c test.c
> gcc -L $JDK/jre/lib/i386/client test.o -o test -ljvm
Additionally you should define _REENTRANT and link with libpthread on
Linux.
BTW, our J2SDK contains "j2sdk-config" which helps to get the right values,
e.g.:
,----
| % j2sdk-config --cflags
| -D_REENTRANT -D_GNU_SOURCE -I/usr/lib/j2se/1.3/include -I/usr/lib/j2se/1.3/include/linux
|
| % j2sdk-config --libs
| -L/usr/lib/j2se/1.3/jre/lib/i386/client -ljvm -lpthread
|
| % j2sdk-config --ld-library-path
| /usr/lib/j2se/1.3/jre/lib/i386:/usr/lib/j2se/1.3/jre/lib/i386/native_threads:/usr/lib/j2se/1.3/jre/lib/i386/client:/usr/lib/j2se/1.3/lib/i386${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
|
| % j2sdk-config -server --ld-library-path
| /usr/lib/j2se/1.3/jre/lib/i386:/usr/lib/j2se/1.3/jre/lib/i386/native_threads:/usr/lib/j2se/1.3/jre/lib/i386/server:/usr/lib/j2se/1.3/lib/i386${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
`----
Juergen
--
Juergen Kreileder, Blackdown Java-Linux Team
http://www.blackdown.org/java-linux.html
Run Java 2 SE v1.3.1 on your iPAQ:
http://www.handhelds.org/pipermail/ipaq/2001-June/007221.html
Thank you very much, you have been *so* helpful. The code works now, and I
can actually implement something useful.
Goran