Skip to content

Commit f1710cc

Browse files
committed
[GR-57608] Support --enable-native-access hosted option in native image
PullRequest: graal/18660
2 parents 55b3c34 + b7a35bc commit f1710cc

File tree

10 files changed

+152
-69
lines changed

10 files changed

+152
-69
lines changed

substratevm/mx.substratevm/suite.py

+1
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,7 @@
307307
"jdk.internal.ref",
308308
"jdk.internal.reflect",
309309
"jdk.internal.vm",
310+
"jdk.internal.vm.annotation",
310311
"jdk.internal.util",
311312
"jdk.internal.org.objectweb.asm",
312313
],

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/NativeImageClassLoaderOptions.java

+5
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ public class NativeImageClassLoaderOptions {
4848
" <target-module> can be ALL-UNNAMED to read all unnamed modules.")//
4949
public static final HostedOptionKey<AccumulatingLocatableMultiOptionValue.Strings> AddReads = new HostedOptionKey<>(AccumulatingLocatableMultiOptionValue.Strings.build());
5050

51+
@APIOption(name = "enable-native-access", launcherOption = true, valueSeparator = {APIOption.WHITESPACE_SEPARATOR, '='})//
52+
@Option(help = "A comma-separated list of modules that are permitted to perform restricted native operations." +
53+
" The module name can also be ALL-UNNAMED.")//
54+
public static final HostedOptionKey<AccumulatingLocatableMultiOptionValue.Strings> EnableNativeAccess = new HostedOptionKey<>(AccumulatingLocatableMultiOptionValue.Strings.build());
55+
5156
@APIOption(name = "list-modules")//
5257
@Option(help = "List observable modules and exit.")//
5358
public static final HostedOptionKey<Boolean> ListModules = new HostedOptionKey<>(false);

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java

+2
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@
133133
import jdk.internal.reflect.FieldAccessor;
134134
import jdk.internal.reflect.Reflection;
135135
import jdk.internal.reflect.ReflectionFactory;
136+
import jdk.internal.vm.annotation.Stable;
136137
import sun.reflect.annotation.AnnotationType;
137138
import sun.reflect.generics.factory.GenericsFactory;
138139
import sun.reflect.generics.repository.ClassRepository;
@@ -389,6 +390,7 @@ public final class DynamicHub implements AnnotatedElement, java.lang.reflect.Typ
389390
private CFunctionPointer[] vtable;
390391

391392
/** Field used for module information access at run-time. */
393+
@Stable //
392394
private Module module;
393395

394396
/** The class that serves as the host for the nest. All nestmates have the same host. */

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

+16-8
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
package com.oracle.svm.core.jdk;
2626

2727
import com.oracle.svm.core.annotate.Alias;
28+
import com.oracle.svm.core.annotate.RecomputeFieldValue;
2829
import com.oracle.svm.core.annotate.Substitute;
2930
import com.oracle.svm.core.annotate.TargetClass;
3031
import com.oracle.svm.core.annotate.TargetElement;
@@ -40,6 +41,19 @@
4041
@SuppressWarnings("unused")
4142
@TargetClass(value = java.lang.Module.class)
4243
public final class Target_java_lang_Module {
44+
45+
/**
46+
* {@link Alias} to make {@code Module.layer} non-final. The actual run-time value is set via
47+
* reflection in {@code ModuleLayerFeatureUtils#patchModuleLayerField}, which is called after
48+
* analysis. Thus, we cannot leave it {@code final}, because the analysis might otherwise
49+
* constant-fold the initial {@code null} value. Ideally, we would make it {@code @Stable}, but
50+
* our substitution system currently does not allow this (GR-60154).
51+
*/
52+
@Alias //
53+
@RecomputeFieldValue(isFinal = false, kind = RecomputeFieldValue.Kind.None)
54+
// @Stable (no effect currently GR-60154)
55+
private ModuleLayer layer;
56+
4357
@Substitute
4458
@TargetElement(onlyWith = ForeignDisabled.class)
4559
@SuppressWarnings("static-method")
@@ -51,15 +65,9 @@ public boolean isNativeAccessEnabled() {
5165
@TargetElement(onlyWith = JDK21OrEarlier.class)
5266
public native void ensureNativeAccess(Class<?> owner, String methodName);
5367

54-
/**
55-
* Allow all native access. This is a workaround for JDK-8331671 until we have a better
56-
* solution. (GR-57608)
57-
*/
58-
@Substitute
68+
@Alias
5969
@TargetElement(onlyWith = JDKLatest.class)
60-
public void ensureNativeAccess(Class<?> owner, String methodName, Class<?> currentClass, boolean jni) {
61-
/* Do Nothing */
62-
}
70+
public native void ensureNativeAccess(Class<?> owner, String methodName, Class<?> currentClass, boolean jni);
6371

6472
@Substitute
6573
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+22/src/hotspot/share/classfile/modules.cpp#L279-L478")

substratevm/src/com.oracle.svm.driver/resources/Help.txt

-3
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ where options include:
3131
-J<flag> pass <flag> directly to the JVM running the image generator
3232
--diagnostics-mode enable diagnostics output: class initialization, substitutions, etc.
3333
--enable-preview allow classes to depend on preview features of this release
34-
--enable-native-access <module name>[,<module name>...]
35-
modules that are permitted to perform restricted native operations.
36-
<module name> can also be ALL-UNNAMED.
3734
--verbose enable verbose output
3835
--version print product version and exit
3936
--help print this help message

substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/DefaultOptionHandler.java

-20
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ class DefaultOptionHandler extends NativeImage.OptionHandler<NativeImage> {
4747
/* Defunct legacy options that we have to accept to maintain backward compatibility */
4848
private static final String noServerOption = "--no-server";
4949

50-
private static final String nativeAccessOption = "--enable-native-access";
51-
5250
DefaultOptionHandler(NativeImage nativeImage) {
5351
super(nativeImage);
5452
}
@@ -130,15 +128,6 @@ public boolean consume(ArgumentQueue args) {
130128
args.poll();
131129
nativeImage.addCustomJavaArgs("--enable-preview");
132130
return true;
133-
case nativeAccessOption:
134-
args.poll();
135-
String modules = args.poll();
136-
if (modules == null) {
137-
NativeImage.showError(nativeAccessOption + moduleSetModifierOptionErrorMessage);
138-
}
139-
nativeImage.addEnableNativeAccess(modules);
140-
nativeImage.addEnableNativeAccess("org.graalvm.nativeimage.foreign");
141-
return true;
142131
}
143132

144133
String singleArgClasspathPrefix = newStyleClasspathOptionName + "=";
@@ -212,15 +201,6 @@ public boolean consume(ArgumentQueue args) {
212201
nativeImage.addLimitedModules(limitModulesArgs);
213202
return true;
214203
}
215-
if (headArg.startsWith(nativeAccessOption + "=")) {
216-
args.poll();
217-
String nativeAccessModules = headArg.substring(nativeAccessOption.length() + 1);
218-
if (nativeAccessModules.isEmpty()) {
219-
NativeImage.showError(headArg + moduleSetModifierOptionErrorMessage);
220-
}
221-
nativeImage.addCustomJavaArgs(headArg + ",org.graalvm.nativeimage.foreign");
222-
return true;
223-
}
224204
return false;
225205
}
226206

substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java

+2-7
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,6 @@ private static <T> String oR(OptionKey<T> option) {
305305
private final List<ExcludeConfig> excludedConfigs = new ArrayList<>();
306306
private final LinkedHashSet<String> addModules = new LinkedHashSet<>();
307307
private final LinkedHashSet<String> limitModules = new LinkedHashSet<>();
308-
private final LinkedHashSet<String> enableNativeAccessModules = new LinkedHashSet<>();
309308

310309
private long imageBuilderPid = -1;
311310

@@ -1381,8 +1380,8 @@ private int completeImageBuild() {
13811380
if (config.modulePathBuild && !finalImageClasspath.isEmpty()) {
13821381
imageBuilderJavaArgs.add(DefaultOptionHandler.addModulesOption + "=ALL-DEFAULT");
13831382
}
1384-
enableNativeAccessModules.addAll(getModulesFromPath(imageBuilderModulePath).keySet());
1385-
assert !enableNativeAccessModules.isEmpty();
1383+
// allow native access for all modules on the image builder module path
1384+
var enableNativeAccessModules = getModulesFromPath(imageBuilderModulePath).keySet();
13861385
imageBuilderJavaArgs.add("--enable-native-access=" + String.join(",", enableNativeAccessModules));
13871386

13881387
boolean useColorfulOutput = configureBuildOutput();
@@ -2057,10 +2056,6 @@ public void addLimitedModules(String limitModulesArg) {
20572056
limitModules.addAll(Arrays.asList(SubstrateUtil.split(limitModulesArg, ",")));
20582057
}
20592058

2060-
public void addEnableNativeAccess(String enableNativeAccessArg) {
2061-
enableNativeAccessModules.addAll(Arrays.asList(SubstrateUtil.split(enableNativeAccessArg, ",")));
2062-
}
2063-
20642059
void addImageBuilderClasspath(Path classpath) {
20652060
imageBuilderClasspath.add(canonicalize(classpath));
20662061
}

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

+42-17
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
import org.graalvm.nativeimage.hosted.FieldValueTransformer;
6363

6464
import com.oracle.graal.pointsto.meta.AnalysisType;
65+
import com.oracle.svm.core.NativeImageClassLoaderOptions;
6566
import com.oracle.svm.core.SubstrateOptions;
6667
import com.oracle.svm.core.SubstrateUtil;
6768
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
@@ -130,27 +131,26 @@ public final class ModuleLayerFeature implements InternalFeature {
130131
public void duringSetup(DuringSetupAccess access) {
131132
FeatureImpl.DuringSetupAccessImpl accessImpl = (FeatureImpl.DuringSetupAccessImpl) access;
132133
moduleLayerFeatureUtils = new ModuleLayerFeatureUtils(accessImpl.imageClassLoader);
133-
134-
RuntimeModuleSupport.instance().setHostedToRuntimeModuleMapper(moduleLayerFeatureUtils::getOrCreateRuntimeModuleForHostedModule);
134+
RuntimeModuleSupport.instance().setHostedToRuntimeModuleMapper(m -> moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(m, accessImpl));
135135

136136
/*
137137
* Register an object replacer that will ensure all references to hosted module instances
138138
* are replaced with the appropriate runtime module instance.
139139
*/
140-
access.registerObjectReplacer(this::replaceHostedModules);
140+
access.registerObjectReplacer(source -> replaceHostedModules(source, accessImpl));
141141
}
142142

143-
private Object replaceHostedModules(Object source) {
143+
private Object replaceHostedModules(Object source, AnalysisAccessBase access) {
144144
if (source instanceof Module module) {
145-
return moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(module);
145+
return moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(module, access);
146146
} else if (source instanceof Class<?> clazz) {
147147
/*
148148
* If the field Class(=DynamicHub).module is not reachable, we do not see all Module
149149
* instances directly. So we also need to scan the module in Class/DynamicHub objects.
150150
*/
151-
moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(clazz.getModule());
151+
moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(clazz.getModule(), access);
152152
} else if (source instanceof DynamicHub hub) {
153-
moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(hub.getModule());
153+
moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(hub.getModule(), access);
154154
}
155155
return source;
156156
}
@@ -451,7 +451,7 @@ private ModuleLayer synthesizeRuntimeModuleLayer(List<ModuleLayer> parentLayers,
451451
runtimeModuleLayer = moduleLayerFeatureUtils.createNewModuleLayerInstance(runtimeModuleLayerConfiguration);
452452
Map<String, Module> nameToModule = moduleLayerFeatureUtils.synthesizeNameToModule(accessImpl, runtimeModuleLayer, clf);
453453
for (Module syntheticModule : syntheticModules) {
454-
Module runtimeSyntheticModule = moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(syntheticModule);
454+
Module runtimeSyntheticModule = moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(syntheticModule, accessImpl);
455455
nameToModule.putIfAbsent(runtimeSyntheticModule.getName(), runtimeSyntheticModule);
456456
moduleLayerFeatureUtils.patchModuleLayerField(accessImpl, runtimeSyntheticModule, runtimeModuleLayer);
457457
}
@@ -538,8 +538,10 @@ private void replicateNativeAccess(AfterAnalysisAccessImpl accessImpl, Set<Modul
538538
for (Map.Entry<Module, Module> modulesPair : modulePairs.entrySet()) {
539539
Module hosted = modulesPair.getKey();
540540
Module runtime = modulesPair.getValue();
541-
if (moduleLayerFeatureUtils.allowsNativeAccess(hosted)) {
542-
moduleLayerFeatureUtils.setNativeAccess(accessImpl, runtime, true);
541+
if (moduleLayerFeatureUtils.allowsNativeAccess(hosted) || moduleLayerFeatureUtils.isNativeAccessEnabledForRuntimeModule(runtime)) {
542+
if (!moduleLayerFeatureUtils.allowsNativeAccess(runtime)) {
543+
moduleLayerFeatureUtils.enableNativeAccess(accessImpl, runtime);
544+
}
543545
}
544546
}
545547

@@ -630,10 +632,14 @@ private static final class ModuleLayerFeatureUtils {
630632
private final Field moduleLayerModulesField;
631633
private final Field moduleReferenceLocationField;
632634
private final Field moduleReferenceImplLocationField;
635+
private final Set<String> nativeAccessEnabled;
633636

634637
ModuleLayerFeatureUtils(ImageClassLoader cl) {
635638
runtimeModules = new HashMap<>();
636639
imageClassLoader = cl;
640+
nativeAccessEnabled = NativeImageClassLoaderOptions.EnableNativeAccess.getValue().values().stream().flatMap(m -> Arrays.stream(SubstrateUtil.split(m, ",")))
641+
.collect(Collectors.toSet());
642+
637643
Method classGetDeclaredMethods0Method = ReflectionUtil.lookupMethod(Class.class, "getDeclaredFields0", boolean.class);
638644
try {
639645
ModuleSupport.accessModuleByClass(ModuleSupport.Access.OPEN, ModuleLayerFeature.class, Module.class);
@@ -694,6 +700,15 @@ private static final class ModuleLayerFeatureUtils {
694700
}
695701
}
696702

703+
private boolean isNativeAccessEnabledForRuntimeBootLayerModule(String runtimeModuleName) {
704+
return nativeAccessEnabled.contains(runtimeModuleName);
705+
}
706+
707+
private boolean isNativeAccessEnabledForRuntimeModule(Module runtimeModule) {
708+
String runtimeModuleName = runtimeModule.getName();
709+
return RuntimeModuleSupport.instance().getBootLayer() == runtimeModule.getLayer() && isNativeAccessEnabledForRuntimeBootLayerModule(runtimeModuleName);
710+
}
711+
697712
/**
698713
* A manual field lookup is necessary due to reflection filters present in newer JDK
699714
* versions. This method should be removed once {@link ReflectionUtil} becomes immune to
@@ -797,7 +812,7 @@ public Module getRuntimeModuleForHostedModule(ClassLoader loader, String hostedM
797812
}
798813
}
799814

800-
public Module getOrCreateRuntimeModuleForHostedModule(Module hostedModule) {
815+
public Module getOrCreateRuntimeModuleForHostedModule(Module hostedModule, AnalysisAccessBase access) {
801816
/*
802817
* Special module instances such as ALL_UNNAMED and EVERYONE_MODULE are not replicated
803818
* as they only serve as marker modules (all their fields are null, including the loader
@@ -806,11 +821,13 @@ public Module getOrCreateRuntimeModuleForHostedModule(Module hostedModule) {
806821
if (hostedModule == allUnnamedModule || hostedModule == everyoneModule) {
807822
return hostedModule;
808823
} else {
809-
return getOrCreateRuntimeModuleForHostedModule(hostedModule.getClassLoader(), hostedModule.getName(), hostedModule.getDescriptor());
824+
boolean enableNativeAccess = allowsNativeAccess(hostedModule) || isNativeAccessEnabledForRuntimeBootLayerModule(hostedModule.getName());
825+
return getOrCreateRuntimeModuleForHostedModule(hostedModule.getClassLoader(), hostedModule.getName(), hostedModule.getDescriptor(), access, enableNativeAccess);
810826
}
811827
}
812828

813-
public Module getOrCreateRuntimeModuleForHostedModule(ClassLoader loader, String hostedModuleName, ModuleDescriptor runtimeModuleDescriptor) {
829+
public Module getOrCreateRuntimeModuleForHostedModule(ClassLoader loader, String hostedModuleName, ModuleDescriptor runtimeModuleDescriptor, AnalysisAccessBase access,
830+
boolean enableNativeAccess) {
814831
synchronized (runtimeModules) {
815832
Module runtimeModule = getRuntimeModuleForHostedModule(loader, hostedModuleName, true);
816833
if (runtimeModule != null) {
@@ -828,6 +845,9 @@ public Module getOrCreateRuntimeModuleForHostedModule(ClassLoader loader, String
828845
}
829846
runtimeModules.putIfAbsent(loader, new HashMap<>());
830847
runtimeModules.get(loader).put(hostedModuleName, runtimeModule);
848+
if (enableNativeAccess) {
849+
enableNativeAccess(access, runtimeModule);
850+
}
831851
return runtimeModule;
832852
}
833853
}
@@ -855,7 +875,8 @@ Map<String, Module> synthesizeNameToModule(AnalysisAccessBase access, ModuleLaye
855875
ModuleDescriptor descriptor = mref.descriptor();
856876
String name = descriptor.name();
857877
ClassLoader loader = clf.apply(name);
858-
Module m = getOrCreateRuntimeModuleForHostedModule(loader, name, descriptor);
878+
boolean nativeAccess = false;
879+
Module m = getOrCreateRuntimeModuleForHostedModule(loader, name, descriptor, access, nativeAccess);
859880
if (!descriptor.equals(m.getDescriptor())) {
860881
moduleDescriptorField.set(m, descriptor);
861882
access.rescanField(m, moduleDescriptorField);
@@ -1123,11 +1144,15 @@ boolean allowsNativeAccess(Module module) {
11231144

11241145
}
11251146

1126-
void setNativeAccess(AfterAnalysisAccessImpl accessImpl, Module module, boolean value) {
1147+
/**
1148+
* Allows the given module to perform native access.
1149+
*/
1150+
void enableNativeAccess(AnalysisAccessBase access, Module module) {
1151+
VMError.guarantee(!allowsNativeAccess(module), "Cannot reset native access");
11271152
assert moduleEnableNativeAccessField != null : "Only available on JDK19+";
11281153
try {
1129-
moduleEnableNativeAccessField.set(module, value);
1130-
accessImpl.rescanField(module, moduleEnableNativeAccessField);
1154+
moduleEnableNativeAccessField.set(module, true);
1155+
access.rescanField(module, moduleEnableNativeAccessField);
11311156
} catch (IllegalAccessException e) {
11321157
throw VMError.shouldNotReachHere("Failed to reflectively set Module.enableNativeAccess.", e);
11331158
}

0 commit comments

Comments
 (0)