Skip to content

Commit

Permalink
Add support for loading libraries from directory containing native image
Browse files Browse the repository at this point in the history
  • Loading branch information
pejovica committed Feb 13, 2023
1 parent f83f84d commit d632222
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 7 deletions.
9 changes: 8 additions & 1 deletion docs/reference-manual/native-image/JNI.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ The Native Image JNI implementation supports both approaches.

### Table of Contents

* [Loading Native Libraries](#loading-native-libraries)
* [Reflection Metadata](#reflection-metadata)
* [Object Handles](#object-handles)
* [Java-to-Native Method Calls](#java-to-native-method-calls)
Expand All @@ -29,6 +30,12 @@ The Native Image JNI implementation supports both approaches.
* [Exceptions](#exceptions)
* [Monitors](#monitors)

## Loading Native Libraries

When loading native libraries using `System.loadLibrary()` (and related APIs), the native image will search the
directory containing the native image before searching the Java library path. So as long as the native libraries
to be loaded are in the same directory as the native image, no other settings should be necessary.

## Reflection Metadata

JNI supports looking up classes by their names, and looking up methods and fields by their names and signatures.
Expand Down Expand Up @@ -187,4 +194,4 @@ For that reason, it can be beneficial to wrap synchronization in Java code.
- [Interoperability with Native Code](InteropWithNativeCode.md)
- [JNI Invocation API](JNIInvocationAPI.md)
- [Reachability Metadata: Java Native Interface](ReachabilityMetadata.md#java-native-interface)
- [Reachability Metadata: Java Native Interface](ReachabilityMetadata.md#java-native-interface)
2 changes: 2 additions & 0 deletions docs/security/security-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ See the [documentation](../reference-manual/native-image/CertificateManagement.m

In addition, developers can run the `native-image` builder in a dedicated environment, such as a container, that does not contain any sensitive information in the first place.

The directory containing the native image is part of the search path when loading native libraries using `System.loadLibrary()` at runtime.

### Serialization in Native Image

Native Image supports Serialization to help users deserialize the constructors for classes, contained in a native executable in the first place.
Expand Down
1 change: 1 addition & 0 deletions substratevm/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ This changelog summarizes major changes to GraalVM Native Image.
* (GR-19890) Native Image now sets up build environments for Windows users automatically. Running in an x64 Native Tools Command Prompt is no longer a requirement.
* (GR-43410) Added support for the JFR event `ExecutionSample`.
* (GR-44058) Red Hat added support for the JFR event `ObjectAllocationInNewTLAB`.
* (GR-42467) The search path for `System.loadLibrary()` by default includes the directory containing the native image.

## Version 22.3.0
* (GR-35721) Remove old build output style and the `-H:±BuildOutputUseNewStyle` option.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,17 @@
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform.HOSTED_ONLY;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.ProcessProperties;
import org.graalvm.nativeimage.impl.ProcessPropertiesSupport;
import org.graalvm.word.PointerBase;
import org.graalvm.word.WordFactory;

import com.oracle.svm.core.NeverInline;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.jdk.PlatformNativeLibrarySupport.NativeLibrary;
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
import com.oracle.svm.core.jdk.PlatformNativeLibrarySupport.NativeLibrary;
import com.oracle.svm.core.snippets.KnownIntrinsics;

@AutomaticallyRegisteredImageSingleton
public final class NativeLibrarySupport {
Expand All @@ -62,7 +67,10 @@ public static NativeLibrarySupport singleton() {

private final Deque<NativeLibrary> currentLoadContext = new ArrayDeque<>();

private String[] paths;
/** The path of the directory containing the native image. */
private String sysPath;
/** Paths derived from the {@code java.library.path} system property. */
private String[] usrPaths;

private LibraryInitializer libraryInitializer;

Expand Down Expand Up @@ -97,17 +105,25 @@ public void loadLibraryRelative(String name) {
if (loadLibrary0(new File(name), true)) {
return;
}
String libname = System.mapLibraryName(name);
if (paths == null) {
if (usrPaths == null) {
/*
* Note that `sysPath` will be `null` if we fail to get the image directory in which
* case we effectively fall back to using only `usrPaths`.
*/
sysPath = getImageDirectory();
String[] tokens = SubstrateUtil.split(System.getProperty("java.library.path", ""), File.pathSeparator);
for (int i = 0; i < tokens.length; i++) {
if (tokens[i].isEmpty()) {
tokens[i] = ".";
}
}
paths = tokens;
usrPaths = tokens;
}
String libname = System.mapLibraryName(name);
if (sysPath != null && loadLibrary0(new File(sysPath, libname), false)) {
return;
}
for (String path : paths) {
for (String path : usrPaths) {
File libpath = new File(path, libname);
if (loadLibrary0(libpath, false)) {
return;
Expand All @@ -120,6 +136,20 @@ public void loadLibraryRelative(String name) {
throw new UnsatisfiedLinkError("no " + name + " in java.library.path");
}

/** Returns the directory containing the native image, or {@code null}. */
@NeverInline("Reads the return address.")
private static String getImageDirectory() {
/*
* While one might expect code for shared libraries to work for executables as well, this is
* not necessarily the case. For example, `dladdr` on Linux returns `argv[0]` for
* executables, which is completely useless when running an executable from `$PATH`, since
* then `argv[0]` contains only the name of the executable.
*/
String image = !SubstrateOptions.SharedLibrary.getValue() ? ProcessProperties.getExecutableName()
: ImageSingletons.lookup(ProcessPropertiesSupport.class).getObjectFile(KnownIntrinsics.readReturnAddress());
return image != null ? new File(image).getParent() : null;
}

private boolean loadLibrary0(File file, boolean asBuiltin) {
String canonical;
try {
Expand Down

0 comments on commit d632222

Please sign in to comment.