Skip to content

Commit b03f147

Browse files
committed
Handle layered and Darwin AWT JNI registration
Reapply the AWT setup when awt is inherited from a previous layer, tighten the desktop integration tests once environment checks pass, and keep Darwin absolute built-in AWT loads restricted to trusted image and JDK library locations.
1 parent f75dd54 commit b03f147

7 files changed

Lines changed: 64 additions & 29 deletions

File tree

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

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@
3232
import java.util.Collection;
3333

3434
import org.graalvm.nativeimage.ImageSingletons;
35-
import org.graalvm.nativeimage.Platform;
3635
import org.graalvm.nativeimage.ProcessProperties;
3736
import org.graalvm.nativeimage.impl.ProcessPropertiesSupport;
3837
import org.graalvm.word.PointerBase;
3938
import org.graalvm.word.impl.Word;
4039

4140
import com.oracle.svm.core.NeverInline;
41+
import com.oracle.svm.core.OS;
4242
import com.oracle.svm.core.SubstrateOptions;
4343
import com.oracle.svm.core.snippets.KnownIntrinsics;
4444
import com.oracle.svm.shared.util.StringUtil;
@@ -103,6 +103,9 @@ public void loadLibraryAbsolute(File file) {
103103
if (loadLibrary0(file, false)) {
104104
return;
105105
}
106+
if (loadBuiltinDarwinLibraryAbsoluteFallback(file)) {
107+
return;
108+
}
106109
throw new UnsatisfiedLinkError("Can't load library: " + file);
107110
}
108111

@@ -145,12 +148,6 @@ public void loadLibraryRelative(String name) {
145148

146149
private boolean loadLibrary0(File file, boolean builtin) {
147150
try {
148-
if (!builtin) {
149-
String builtInName = asBuiltinLibraryName(file.getName());
150-
if (builtInName != null && addLibrary(builtInName, true)) {
151-
return true;
152-
}
153-
}
154151
String canonical = builtin ? file.getName() : file.getCanonicalPath();
155152
return addLibrary(canonical, builtin);
156153
} catch (IOException e) {
@@ -159,12 +156,47 @@ private boolean loadLibrary0(File file, boolean builtin) {
159156
}
160157

161158
private static String asBuiltinLibraryName(String fileName) {
162-
if (Platform.includedIn(Platform.DARWIN.class) && fileName.startsWith("lib") && fileName.endsWith(".dylib")) {
159+
if (OS.getCurrent() == OS.DARWIN && fileName.startsWith("lib") && fileName.endsWith(".dylib")) {
163160
return fileName.substring("lib".length(), fileName.length() - ".dylib".length());
164161
}
165162
return null;
166163
}
167164

165+
private boolean loadBuiltinDarwinLibraryAbsoluteFallback(File file) {
166+
String builtInName = asBuiltinLibraryName(file.getName());
167+
if (builtInName == null) {
168+
return false;
169+
}
170+
try {
171+
return isBuiltinDarwinLibraryLocation(file) && addLibrary(builtInName, true);
172+
} catch (IOException e) {
173+
return false;
174+
}
175+
}
176+
177+
private static boolean isBuiltinDarwinLibraryLocation(File file) throws IOException {
178+
if (OS.getCurrent() != OS.DARWIN) {
179+
return false;
180+
}
181+
182+
File canonicalParent = file.getCanonicalFile().getParentFile();
183+
if (canonicalParent == null) {
184+
return false;
185+
}
186+
187+
String imageDirectory = getImageDirectory();
188+
if (imageDirectory != null && canonicalParent.equals(new File(imageDirectory).getCanonicalFile())) {
189+
return true;
190+
}
191+
192+
String javaHome = System.getProperty("java.home");
193+
if (javaHome != null && canonicalParent.equals(new File(javaHome, "lib").getCanonicalFile())) {
194+
return true;
195+
}
196+
197+
return false;
198+
}
199+
168200
protected abstract boolean addLibrary(String canonical, boolean builtin);
169201

170202
public abstract PointerBase findSymbol(String name);

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporterSupport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ private static boolean recommendTraceAgentForAWT() {
134134
if (!ImageSingletons.contains(JNIRegistrationSupport.class) || !ImageSingletons.contains(JNIReflectionDictionary.class)) {
135135
return false;
136136
}
137-
if (!JNIRegistrationSupport.singleton().isRegisteredLibrary("awt")) {
137+
if (!JNIRegistrationSupport.singleton().isAnyLayerRegisteredLibrary("awt")) {
138138
return false; // AWT not used
139139
}
140140
// check if any class located in java.awt or sun.awt is registered for JNI access

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JNIRegistrationAWTSupport.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,11 @@ public void beforeAnalysis(BeforeAnalysisAccess access) {
6262
@Override
6363
public void afterAnalysis(AfterAnalysisAccess access) {
6464
JNIRegistrationSupport jniRegistrationSupport = JNIRegistrationSupport.singleton();
65-
if (jniRegistrationSupport.isRegisteredLibrary("awt")) {
65+
if ((isLinux() || isDarwin()) && jniRegistrationSupport.isPreviousLayerRegisteredLibrary("awt") &&
66+
!jniRegistrationSupport.isCurrentLayerRegisteredLibrary("awt")) {
67+
registerHeadlessJavaDesktopSupport("awt");
68+
}
69+
if (jniRegistrationSupport.isAnyLayerRegisteredLibrary("awt")) {
6670
jniRegistrationSupport.addJvmShimExports(
6771
"JVM_IsStaticallyLinked");
6872
jniRegistrationSupport.addJavaShimExports(
@@ -104,11 +108,11 @@ public void afterAnalysis(AfterAnalysisAccess access) {
104108
jniRegistrationSupport.registerLibrary("awt_xawt");
105109
}
106110
}
107-
if (jniRegistrationSupport.isRegisteredLibrary("javaaccessbridge")) {
111+
if (jniRegistrationSupport.isAnyLayerRegisteredLibrary("javaaccessbridge")) {
108112
/* Dependency on `jawt` is not expressed in Java, so we register it manually here. */
109113
jniRegistrationSupport.registerLibrary("jawt");
110114
}
111-
if (jniRegistrationSupport.isRegisteredLibrary("javajpeg")) {
115+
if (jniRegistrationSupport.isAnyLayerRegisteredLibrary("javajpeg")) {
112116
jniRegistrationSupport.addJavaShimExports(
113117
"JNU_GetEnv",
114118
"JNU_ThrowByName",
@@ -119,7 +123,7 @@ public void afterAnalysis(AfterAnalysisAccess access) {
119123

120124
@Override
121125
public void beforeImageWrite(BeforeImageWriteAccess access) {
122-
if (isDarwin() && JNIRegistrationSupport.singleton().isRegisteredLibrary("awt")) {
126+
if (isDarwin() && JNIRegistrationSupport.singleton().isAnyLayerRegisteredLibrary("awt")) {
123127
((BeforeImageWriteAccessImpl) access).registerLinkerInvocationTransformer(linkerInvocation -> {
124128
linkerInvocation.addNativeLinkerOption("-Wl,-framework,AppKit");
125129
linkerInvocation.addNativeLinkerOption("-Wl,-framework,Accelerate");
@@ -134,7 +138,7 @@ public void beforeImageWrite(BeforeImageWriteAccess access) {
134138
return linkerInvocation;
135139
});
136140
}
137-
if (isWindows() && JNIRegistrationSupport.singleton().isRegisteredLibrary("awt")) {
141+
if (isWindows() && JNIRegistrationSupport.singleton().isAnyLayerRegisteredLibrary("awt")) {
138142
((BeforeImageWriteAccessImpl) access).registerLinkerInvocationTransformer(linkerInvocation -> {
139143
/*
140144
* Add Windows libraries that are pulled in as a side effect of exporting the

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JNIRegistrationSupport.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,10 +188,18 @@ private void addLibrary(String libname) {
188188
}
189189
}
190190

191-
public boolean isRegisteredLibrary(String libname) {
191+
boolean isCurrentLayerRegisteredLibrary(String libname) {
192192
return jniRegistrationSupportSingleton.currentLayerRegisteredLibraries.contains(libname);
193193
}
194194

195+
boolean isPreviousLayerRegisteredLibrary(String libname) {
196+
return jniRegistrationSupportSingleton.prevLayerRegisteredLibraries.contains(libname);
197+
}
198+
199+
public boolean isAnyLayerRegisteredLibrary(String libname) {
200+
return isCurrentLayerRegisteredLibrary(libname) || isPreviousLayerRegisteredLibrary(libname);
201+
}
202+
195203
/** Adds exports that `jvm` shim should re-export. */
196204
void addJvmShimExports(String... exports) {
197205
addShimExports("jvm", exports);

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/DarwinBuiltinJNISymbolSupport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ private static Set<String> scanAvailableSymbols() throws IOException, Interrupte
117117
}
118118

119119
private static Set<String> readDefinedSymbols(Path staticLibrary) throws IOException, InterruptedException {
120-
List<String> commandLine = List.of("nm", "-g", "-U", "-j", staticLibrary.toString());
120+
List<String> commandLine = List.of("nm", "--extern-only", "--defined-only", "--format=just-symbols", staticLibrary.toString());
121121
ProcessBuilder command = FileUtils.prepareCommand(commandLine, null).redirectErrorStream(true);
122122
FileUtils.traceCommand(command);
123123
Process process = command.start();

substratevm/src/com.oracle.svm.test/src/com/oracle/svm/integrationtest/HeadlessJavaDesktopTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public class HeadlessJavaDesktopTest {
3939
@Test
4040
public void headlessJavaDesktopSmokeTest() throws Exception {
4141
String osName = System.getProperty("os.name", "");
42-
Assume.assumeTrue(osName.startsWith("Linux") || osName.startsWith("Mac") || osName.startsWith("Darwin"));
42+
Assume.assumeTrue(osName.startsWith("Linux") || osName.startsWith("Mac"));
4343

4444
System.setProperty("java.awt.headless", "true");
4545

substratevm/src/com.oracle.svm.test/src/com/oracle/svm/integrationtest/NonHeadlessJavaDesktopTest.java

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,8 @@
2424
*/
2525
package com.oracle.svm.integrationtest;
2626

27-
import java.awt.AWTError;
2827
import java.awt.Dimension;
2928
import java.awt.GraphicsEnvironment;
30-
import java.awt.HeadlessException;
3129
import java.awt.Toolkit;
3230
import java.awt.image.BufferedImage;
3331
import java.io.ByteArrayOutputStream;
@@ -48,16 +46,9 @@ public void nonHeadlessJavaDesktopSmokeTest() throws Exception {
4846
Assume.assumeTrue(display != null && !display.isEmpty());
4947

5048
System.clearProperty("java.awt.headless");
51-
Toolkit toolkit;
52-
Dimension screenSize;
53-
try {
54-
Assume.assumeFalse(GraphicsEnvironment.isHeadless());
55-
toolkit = Toolkit.getDefaultToolkit();
56-
screenSize = toolkit.getScreenSize();
57-
} catch (AWTError | HeadlessException e) {
58-
Assume.assumeNoException(e);
59-
return;
60-
}
49+
Assume.assumeFalse(GraphicsEnvironment.isHeadless());
50+
Toolkit toolkit = Toolkit.getDefaultToolkit();
51+
Dimension screenSize = toolkit.getScreenSize();
6152
Assert.assertTrue("Invalid screen width: " + screenSize.width, screenSize.width > 0);
6253
Assert.assertTrue("Invalid screen height: " + screenSize.height, screenSize.height > 0);
6354

0 commit comments

Comments
 (0)