0

I am writing a native launcher in linux for a java program. The launcher should load libjvm.so statically and execute the function JNI_CreateJavaVM() via a function pointer, so I can launch the executable without having to first set LD_LIBRARY_PATH.

I have this so far and I've figured it how to compile and link it, but I'm struggling with the syntax for declaring the function pointer and then later executing the function:

JavaVM *jvm;
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption* options = new JavaVMOption[10];

...

std::string location = "./jre/lib/server/libjvm.so";
void *handle = dlopen ( location.c_str(), RTLD_LAZY );

if ( !handle ) {
   printf ( "Unable to load %s, exiting", location.c_str() );
   return 0;
}

?? = dlsym ( handle, "JNI_CreateJavaVM" ); //get the function pointer

//This is how I would execute the function if dynamically linking: 
//JNI_CreateJavaVM( &jvm, (void**)&env, &vm_args );

?? ( &jvm, (void**)&env, &vm_args ); //Execute the function pointer. 

What magic words do I put in place of the ??s to make this work? I've tried working through the dlsym documentation, but I'm too unfamiliar with C/C++ to translate it to my situation.

Thanks!

Grumblesaurus
  • 3,021
  • 3
  • 31
  • 61
  • 1
    Did you see the example in the `dlopen` man page? http://man7.org/linux/man-pages/man3/dlopen.3.html Also, doesn't `JNI` cover this? Also this is **dynamic** loading/linking not "static". – Galik Dec 04 '18 at 11:17
  • JNI covers this, but the examples provided by Oracle don't offer examples in this shape -- they require jvm.dll / libjvm.so be in path before executing. I'm not a C/C++ guy at all, so I don't have the lingo down or familiarity with the syntax, but I know this is the basic shape of the solution I need. I have it working in windows, but I can't quite get the syntax right on Linux. – Grumblesaurus Dec 04 '18 at 11:36
  • Why do you wan't to `dlopen` libjvm.so and not link to it normaly? `dlopen` is typicaly used for plugins. If the problem is that ibjvm.so is not in your default library path, you can fix this by setting `LD_LIBRARY_PATH` – HAL9000 Dec 04 '18 at 17:12

1 Answers1

2

First, declare a type (here p_JNI_CreateJavaVM) for the function you want to retrieve:

typedef jint (*p_JNI_CreateJavaVM)(JavaVM**, void**, void**);
p_JNI_CreateJavaVM JNI_CreateJavaVM = (p_JNI_CreateJavaVM)dlsym(handle, "JNI_CreateJavaVM");

And you can invoke it as usual:

jint ret = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
Botje
  • 26,269
  • 3
  • 31
  • 41
  • I had to change the typedef to get it to compile: `typedef jint (*p_JNI_CreateJavaVM)(JavaVM**, void**, JavaVMInitArgs* );` Otherwise this worked. Thank you! – Grumblesaurus Dec 04 '18 at 11:47
  • 1
    I was going off of [Oracle documentation](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html) that listed it as `void**` – Botje Dec 04 '18 at 11:48
  • I don't know enough C/C++ to make sense of it. The compiler told me `launch.cpp: In function ‘int main(int, char**)’: launch.cpp:51:60: error: cannot convert ‘JavaVMInitArgs*’ to ‘void**’ in argument passing jint ret = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);` so I tried changing the last argument to that `JavaVMInitArgs*` and it worked. – Grumblesaurus Dec 04 '18 at 11:50
  • @JoshuaD The JNI spec is technically written for C, and C allows conversion to/from `void *` without a cast. C++ requires an explicit cast. – Andrew Henle Dec 04 '18 at 11:52
  • 2
    Just because I was curious, if you have access to C++14 you can do `typedef decltype(&JNI_CreateJavaVM) p_JNI_CreateJavaVM;` to get the type without typing (or thinking) so much. – Botje Dec 04 '18 at 11:53