Search code examples
javalinuxjava-native-interfacefreepascal

Using JNI in FPC/Lazarus on Linux


I found a good implementation of JNI: https://sites.google.com/site/aminer68/jni-wrapper-for-delphi-and-freepascal

It has version 2.85, and the according text has been written in March 2016, but it is written for Windows.

But I had no problems skipping/removing all the Windows related stuff (what Lazarus did not remove), but my work still fails

I did it like this:

procedure TJavaRuntime.Initialize;
begin
   if libHandle <> 0 then
      exit; // already initialized.
   FRuntimeLib := '/usr/lib/jvm/java-8-oracle/jre/lib/amd64/server/libjvm.so';
   libHandle := LoadLibrary(PChar(FRuntimeLib));

   if libHandle = 0 then
      raise EJavaRuntimeCreation.Create('Could not load library ' + FRuntimeLib);
   @CreateVM := getProcAddress(libHandle, 'JNI_CreateJavaVM');
   @GetDefaultArgs := getProcAddress(libHandle, 'JNI_GetDefaultJavaVMInitArgs');
   @GetCreatedVMs := getProcAddress(libHandle, 'JNI_GetCreatedJavaVMs');
   if (@CreateVM = Nil) or (@GetDefaultArgs = Nil) or (@GetCreatedVMs = Nil) then
  raise EJavaRuntimeCreation.Create('Library ' + FRuntimeLib + ' is not valid.');
   vmargs2.version := $00010008;
   GetDefaultArgs(@vmargs2);
end;

You see, I load the jvm-library as needed in Linux. libHandle becomes non-zero, so I think it loads it well (if I create a spelling-error in the library-name, libHandle remains zero.)

The finding of the procadresses seems to fail. @CreateVM: >

Some background on CreateVM:

CreateVM : TCreateVM; 
TCreateVM = function (vm : PPJavaVM ; penv : PPJNIEnv ; p : Pointer) : jint; {$IFDEF WIN32} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}

(by the way, cdecl is active, stdcall is grayed out in the Lazarus editor)

I try to call one, like this in function TJavaRuntime.GetVM

Like here

if CreateVM(@pvm, @penv, args) <>0 then
    raise EJavaRuntimeCreation.Create('Could not create JVM');

It does not go to the exception, but raises another Exception: Project jtest1 raised exception class 'External: SIGSEGV'. At address 7FFFE62582B4

I read somewhere that SIGSEGV is entering an memory location which is a fault of illegal memory access. That brings me back to the code where the address of CreateVM is retrieved in this way:

@CreateVM := getProcAddress(DLLHandle, 'JNI_CreateJavaVM');

I am sure that a function with that name exist in that library:

~ > nm -D /usr/lib/jvm/java-8-oracle/jre/lib/amd64/server/libjvm.so

---fragment
000000000070e0f0 T jio_snprintf
000000000070de80 T jio_vfprintf
000000000070e0d0 T jio_vsnprintf
00000000006d0880 T JNI_CreateJavaVM
00000000006cd1d0 T JNI_GetCreatedJavaVMs
00000000006cd210 T JNI_GetDefaultJavaVMInitArgs
000000000070ee00 T JVM_Accept
0000000000713990 T JVM_ActiveProcessorCount
0000000000715a20 T JVM_AllocateNewArray
0000000000727340 T JVM_AllocateNewObj

So the call with getProcAddress(.....) does not lead to an valid address. It can be the case that the LoadLibrary(PChar(FRuntimeLib)) function does not load the library well, allthough it has a valid handle. (I also tried SafeLoadLibrary(FRuntimeLib); Or the getProcAddress(.....) has some problems. (By the way, I also tried GetProcedureAddress).

I also tried other jvm-libraries, so that cannot be the problem.

Who knows what is going wrong here?

Thanks in advance


Solution

  • Some things to consider:

    • use unit cmem, so that pascal and C memory is unified
    • maybe start with the simplest function, like a version check?
    • Does the JNI library use the same calling convention on Linux and Windows? Maybe your headers are Windows only and contain stdcall
    • I see a lot of @'s. If your libraries and calling code are from varying sources be very careful. Some might eat indirections due to CONST or VAR parameter use, carefully check they match.