Skip to content

Commit b54d74e

Browse files
committed
Add support for loading libraries from directory containing native image
1 parent de5d6aa commit b54d74e

File tree

4 files changed

+47
-7
lines changed

4 files changed

+47
-7
lines changed

docs/reference-manual/native-image/JNI.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ The Native Image JNI implementation supports both approaches.
1919

2020
### Table of Contents
2121

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

33+
## Loading Native Libraries
34+
35+
When loading native libraries using `System.loadLibrary()` (and related APIs), the native image will search the
36+
directory containing the native image before searching the Java library path. So as long as the native libraries
37+
to be loaded are in the same directory as the native image, no other settings should be necessary.
38+
3239
## Reflection Metadata
3340

3441
JNI supports looking up classes by their names, and looking up methods and fields by their names and signatures.
@@ -187,4 +194,4 @@ For that reason, it can be beneficial to wrap synchronization in Java code.
187194
188195
- [Interoperability with Native Code](InteropWithNativeCode.md)
189196
- [JNI Invocation API](JNIInvocationAPI.md)
190-
- [Reachability Metadata: Java Native Interface](ReachabilityMetadata.md#java-native-interface)
197+
- [Reachability Metadata: Java Native Interface](ReachabilityMetadata.md#java-native-interface)

docs/security/security-guide.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ See the [documentation](../reference-manual/native-image/CertificateManagement.m
118118

119119
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.
120120

121+
The directory containing the native image is part of the search path when loading native libraries using `System.loadLibrary()` at runtime.
122+
121123
### Serialization in Native Image
122124

123125
Native Image supports Serialization to help users deserialize the constructors for classes, contained in a native executable in the first place.

substratevm/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ This changelog summarizes major changes to GraalVM Native Image.
1818
* (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.
1919
* (GR-43410) Added support for the JFR event `ExecutionSample`.
2020
* (GR-44058) Red Hat added support for the JFR event `ObjectAllocationInNewTLAB`.
21+
* (GR-42467) The search path for `System.loadLibrary()` by default includes the directory containing the native image.
2122

2223
## Version 22.3.0
2324
* (GR-35721) Remove old build output style and the `-H:±BuildOutputUseNewStyle` option.

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/NativeLibrarySupport.java

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,17 @@
3535
import org.graalvm.nativeimage.ImageSingletons;
3636
import org.graalvm.nativeimage.Platform.HOSTED_ONLY;
3737
import org.graalvm.nativeimage.Platforms;
38+
import org.graalvm.nativeimage.ProcessProperties;
39+
import org.graalvm.nativeimage.impl.ProcessPropertiesSupport;
3840
import org.graalvm.word.PointerBase;
3941
import org.graalvm.word.WordFactory;
4042

43+
import com.oracle.svm.core.NeverInline;
44+
import com.oracle.svm.core.SubstrateOptions;
4145
import com.oracle.svm.core.SubstrateUtil;
42-
import com.oracle.svm.core.jdk.PlatformNativeLibrarySupport.NativeLibrary;
4346
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
47+
import com.oracle.svm.core.jdk.PlatformNativeLibrarySupport.NativeLibrary;
48+
import com.oracle.svm.core.snippets.KnownIntrinsics;
4449

4550
@AutomaticallyRegisteredImageSingleton
4651
public final class NativeLibrarySupport {
@@ -62,7 +67,10 @@ public static NativeLibrarySupport singleton() {
6267

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

65-
private String[] paths;
70+
/** The path of the directory containing the native image. */
71+
private String sysPath;
72+
/** Paths derived from the {@code java.library.path} system property. */
73+
private String[] usrPaths;
6674

6775
private LibraryInitializer libraryInitializer;
6876

@@ -97,17 +105,25 @@ public void loadLibraryRelative(String name) {
97105
if (loadLibrary0(new File(name), true)) {
98106
return;
99107
}
100-
String libname = System.mapLibraryName(name);
101-
if (paths == null) {
108+
if (usrPaths == null) {
109+
/*
110+
* Note that `sysPath` will be `null` if we fail to get the image directory in which
111+
* case we effectively fall back to using only `usrPaths`.
112+
*/
113+
sysPath = getImageDirectory();
102114
String[] tokens = SubstrateUtil.split(System.getProperty("java.library.path", ""), File.pathSeparator);
103115
for (int i = 0; i < tokens.length; i++) {
104116
if (tokens[i].isEmpty()) {
105117
tokens[i] = ".";
106118
}
107119
}
108-
paths = tokens;
120+
usrPaths = tokens;
121+
}
122+
String libname = System.mapLibraryName(name);
123+
if (sysPath != null && loadLibrary0(new File(sysPath, libname), false)) {
124+
return;
109125
}
110-
for (String path : paths) {
126+
for (String path : usrPaths) {
111127
File libpath = new File(path, libname);
112128
if (loadLibrary0(libpath, false)) {
113129
return;
@@ -120,6 +136,20 @@ public void loadLibraryRelative(String name) {
120136
throw new UnsatisfiedLinkError("no " + name + " in java.library.path");
121137
}
122138

139+
/** Returns the directory containing the native image, or {@code null}. */
140+
@NeverInline("Reads the return address.")
141+
private static String getImageDirectory() {
142+
/*
143+
* While one might expect code for shared libraries to work for executables as well, this is
144+
* not necessarily the case. For example, `dladdr` on Linux returns `argv[0]` for
145+
* executables, which is completely useless when running an executable from `$PATH`, since
146+
* then `argv[0]` contains only the name of the executable.
147+
*/
148+
String image = !SubstrateOptions.SharedLibrary.getValue() ? ProcessProperties.getExecutableName()
149+
: ImageSingletons.lookup(ProcessPropertiesSupport.class).getObjectFile(KnownIntrinsics.readReturnAddress());
150+
return image != null ? new File(image).getParent() : null;
151+
}
152+
123153
private boolean loadLibrary0(File file, boolean asBuiltin) {
124154
String canonical;
125155
try {

0 commit comments

Comments
 (0)