I am trying to unload a dll in java. I have read this and this but it seems that you can not guarantee that the dll will actually be unloaded at a certain time. This is because System.gc() simply "asks kindly" for the garbage collector to run.
So here is a break down of the situation. I have a dll that provides some functionality via JNI. Lets call this dll MainDll. MainDll is loaded from a call to System.load("MainDll"). I need to be able to unload and load this dll on the fly.
Is it possible to create another dll that's sole purpose is to load and unload MainDll. Lets call this dll LoaderDll. I could then simple call System.load("LoaderDll") and have some native functions to load and unload MainDll. The reason for doing this, is I have access to functions on the native system that can load and unload the dll on the fly. The tricky part with this is, will I have still be able to access the native functions I have written in MainDll if it loaded from inside LoaderDll.
Sorry if this is a confusing question. It seems its a little difficult to explain.
Thanks
Create a wrapper DLL that does the loading/unloading. Also have wrapper methods in the DLL that turn around and delegate the calls to the loaded MainDll DLL. This way your Java JNI code only knows about a single DLL. It can still request the unload [LoaderDll::unload()] which internally unloads the MainDll.
This should work as long as the methods/functions in LoaderDll can trigger a load of MainDll when they are called when MainDll is not currently loaded, assuming that is the desired behavior instead of throwing an exception/error.
One issue with this would be that LoaderDll would always be loaded.
Add a level of indirection.
Make your native methods call forwarding routines in LoaderDLL. The forwarding routines can use C facilities to forward the calls to code in mainDLL.
If you have a need to dynamically load and unload code, have you considered OSGi. This works in felix at least.
In Oracle/Sun's JDK, System.gc() will trigger a full gc (unless it has been turned off on the command line). It could be just a hint on other JVMs.
Based on your clarifying comments, I think the simplest approach would be to spawn a new JVM, whose sole responsibility is managing your DLL. Probably exposing an RMI interface to access those classes (although a simple stream may be sufficient).
I haven't still encountered a situation where System.gc() not to trigger the garbage collector although it is just a hint.
This tutorial actually helped me to do my work: Unload Java JNI DLL
Related
I have a C++ codebase, in which I'm using JNI to create a JVM and occasionally interact with a library implemented in Java. I'm curious whether, in this use case, Java's garbage collector will still reliably run and clean up?
Most of the information that I find online about JNI seems to be about the "opposite" use case, where people generally appear to have mainly Java code, which sometimes interacts with native code through JNI. For such a use case, I find for example the following online:
The automatic garbage collection of local references that are no longer in scope prevents memory leaks in most situations. This automatic garbage collection occurs when a native thread returns to Java (native methods) or detaches from the JVM (Invocation API). Local reference memory leaks are possible if automatic garbage collection does not occur. A memory leak might occur if a native method does not return to the JVM, or if a program that uses the Invocation API does not detach from the JVM.
I'm not sure what exactly "returns to Java" in this context means. Is just occasionally calling into Java-based methods from C++ sufficient, does that already count as "returning to Java"? If not, are there any ways to make sure that the garbage collector gets a chance to run in my use case?
The JVM created with JNI is a full JVM, including GC.
Think of it this way: The java command that you normally use to run Java programs, is nothing but a small JNI program that creates a JVM, locates the class named on the command-line, and makes a static call to the main(String[]) method.
We know that the JVM calls on the underlying system to allocate memory and CPU time, access files, and many more. How does it work internally to achieve its activities?
Does the JVM use system calls?
Does the JVM use system calls?
Yes.
How does it work internally to achieve its activities?
The typical pattern is that some of the methods in a Java class are labelled as native. When the JVM encounters a call to a native method it makes a call into C or C++ code that is part of the JVM executable. The native method implementation typically does the following:
Check arguments from Java, and translates them into a C / C++ compatible form. For example, String arguments need to be converted to zero-terminated form.
Call the standard C / C++ library function with the arguments it needs.
The library function makes the syscall.
The OS does its stuff and the syscall returns.
The standard C / C++ library function returns.
The native method implementation checks the 'errno'. If there was an error, it creates a Java exception object and throws it.
Otherwise, the native method implementation converts results, etc into Java objects and returns them to the caller of the Java method.
The details vary, depending on what the native method does.
If you want to get a deeper understanding, I recommend that you checkout a copy of the OpenJDK source tree and start trawling. (You need to do the hard yards yourself ....)
Indeed, JVM needs to leverage system calls which is an operating system way to allow processes to interact with underlying system resources.
You can run strace java -version to see a bunch of system calls (mmap, mprotect, openat, etc.) executed even during this very limited java/jvm run.
Another good way to find out more is to dig trough JVM sources for native methods.
One example could be an implementation of FileChannel#force method
which internally calls fsync system call (for example): https://github.com/AdoptOpenJDK/openjdk-jdk11u/blob/5f01925b80ed851b133ee26fbcb07026ac04149e/src/java.base/unix/native/libnio/ch/FileDispatcherImpl.c#L172
Yes, system calls are the only way that an OS allows access to any program.
In the case of Java, this is why some OS-specific “features” show through, so spoiling the ideal of write-once-run-anywhere. For example, I’ve had a program that I developed on a Windows box fail when run on a Linux box.
The problem turned out to be that in the resources directory, the filename was all-lower case, but my program had the file name inMixed case. The program worked on windows since filenames on windows are case-insensitive, but in Linux they are case-sensitive.
I basically understand the idea of managed and native code and their difference. But how is it technically possible for them to communicate with each other? Imagine the following example:
I got some static or dynamic c++ library which is compiled for a specific platform. Now I write a Java Programm. Inside this code I call the library functions with the 'native' keyword. I build a jar file with the bytecode and the c++ library files will stay separate. The result will no longer be platform-independent.
But how does the java programm know if the called native methods exists?
How is the whole programmcode executed during runtime? I know that the bytecode will be interpreted or compiled with JIT.
How does this all fit in the sandboxing paradigm? Is the native code also executed inside the sandbox?
Does it work because both (java and c++) code is machine code in the end?
Maybe this is a dumb question. But I was always wondering...
EDIT: I got 3 good answers. really can't decide which helped me the most. But i will mark this question as answered to close this topic from my side.
It doesn't know until you call the method. The native code resides in a .DLL or .so; the java runtime looks for specific entry points that correspond to the native methods you created (if you're using JNI, there's a tool that can parse the methods and create function stubs that'll result in those entry points when compiled). If the wanted entry point is not there, an exception will be thrown.
The code generated by the JIT is not entirely self-suficient; it has to call external native code (both for low-level runtime routines or OS services) from time to time. The same mechanism is used to invoke the code for your native methods.
No. You can do everything you'd do in a pure C/C++ program there. The only things that'll stop it from doing any damage are external security measures you have (login privilege restrictions, other OS protections, security software, etc.) But the VM won't protect you.
No, JNI existed even before JIT appeared. The mechanism is the same, if the bytecode is being run by an interpreter, and you want this interpreter to invoke native code, you just need some logic in it to determine that a given method is "external" and should be called as native code. This information is contained in the compiled .class file, and when the interpreter or JIT loads it, it creates a memory representation that makes easy to direct the call upon a method lookup.
The JVM will check the libraries you defined and see if the method is there
Bytecode will be interpreted or JITted and a call to native code is added. This may include boxing/deboxing values and other things needed to convert the data into suitable format. The libraries have a certain interface which is explained to the Java compiler and it will produce the required interface logic.
Depends on the sandbox. By default native code is native code. It doesn't call Java APIs so the JVM cannot govern it in any way. But there may be other limitations, for example the JVM could run the native code with libraries that provide sandboxing, or the operating system might have a way of sandboxing.
It depends on what you mean. In the end anything the computer does is machine code, but it doesn't really matter in this case. What matters is the translation and execution part. That is the glue that makes everything work.
Think of the system as people. Person A only speaks Japanese, but wants to reserve a hotel in Paris. The receptionist B only speaks French. Person A can get a translator that will translate their commands to French, command receptionist B and in return translate what B produced into a form person A understands. This is the JNI part.
It depends on the platform. On Linux, Solaris, etc., the JRE uses dlopen. On Windows, it uses LoadLibraryEx and GetProcAddress. If the JRE is running in interpreted mode, it calls that function; in compiled mode, it compiles Java bytecode into native code that calls that function.
On all JREs I'm familiar with, you can't call a native function in a static library directly; only one in a dynamic library.
Native code doesn't have to be limited to a single platform; if it's standard C, you can probably compile it with a cross-compiler for every platform on which a JRE is available.
I understand that Java can load/execute DLL code, but I'm wondering if there are any security checks to prevent untrusted code from the system being called by a JVM. Couldn't this destroy the system -- are there any OS features that prevent this? Or can someone just write in Java itself some method that prevents untrusted code from being loaded? Thanks for your help.
No. Once you call out to native code (via JNI) then that native code is free to do anything (subject to the OS itself giving permission). There's no concept of sandboxing the native code invoked from the JVM.
Note that this is a particular headache with JNI code. Badly coded native code can take down the JVM (as opposed to simply throwing an exception) and the consequent debugging/resolution is particularly hard.
The loading of native code can itself be prevented. Typically e.g. applets run such security context that they cannot load native libraries. However, if the JVM lets your Java code call into untrusted native code, all bets are off.
I have a java application which uses JNI in some parts to do some work. It follows the usual loading of DLL and then calling native methods of DLL. Is there any way we can restrict what native methods can do from the java application? For example, can we restrict DLLs not to open any files or not to open any sockets even if it has the code to do it? It can just forbid DLLs it loads for doing certain things, may be by loggin something or throwing an exception.
No you can't. The DLL gets loaded as a whole and then the Java side has no control on what the native code is doing.
One solution might be kind of man in the middle approach. This would involve coding a "shell" DLL that has the same interface as the original DLL. You tell Java to load a "shell" DLL for instance by putting it in a specific location and using the java.library.path property. Then the role of the "shell" DLL is to load the "true" DLL by sandboxing it and redirecting standard functions. This sounds like a lot of pain and this something that would happen in the native side on things, not from Java.
Edit 2021: today it's also relevant to point out that the sandbox to run Java in would likely be a virtual machine, in the cloud, Docker or what have you, in a locked down configuration.
I liked Gregory Pakosz' answer a lot. However, what you could do is sandbox the Java instance itself. Start the Java application itself in a restricted context.
In Windows or Unix you can create a user which is limited to a certain directory and only has access to some DLLs. Thus the DLL called from JNI can do whatever it wants, but it will not get very far, because the user the Java runs as can not do very much.
If your Java program needs to do privileged things, the Java side of it will have to talk to another program (Java or not) to do its' privileged things for it.
Just keep in mind, that if you can not trust the DLL, you can no longer trust the Java code either, since the DLL might have "hacked" the Java machine. On the other hand, no nasty stuff should be able to break out of the limits of the user they run as. (Barring misconfiguration or a bug in the OS.)
Normally you would run your application under the Java security Manager but I don't believe it has any effect on code running through the JNI.
You could implement some kind of setting that your JNI code could get. For example, on an UNIX system, you could create groups for special types of privileges, and check if the current user has the required privileges, else just return 0 or something.