From ed869945188be08751963435f99da769918880c9 Mon Sep 17 00:00:00 2001 From: Josef Eisl Date: Tue, 12 Nov 2024 09:57:17 +0100 Subject: [PATCH] svm: set native access for the image run time modules --- .../oracle/svm/hosted/ModuleLayerFeature.java | 59 +++++++++++++------ .../hosted/NativeImageClassLoaderSupport.java | 33 ++++++++++- 2 files changed, 74 insertions(+), 18 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java index d42553a6112f3..4ab506a555df7 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java @@ -62,6 +62,7 @@ import org.graalvm.nativeimage.hosted.FieldValueTransformer; import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.svm.core.NativeImageClassLoaderOptions; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; @@ -130,27 +131,26 @@ public final class ModuleLayerFeature implements InternalFeature { public void duringSetup(DuringSetupAccess access) { FeatureImpl.DuringSetupAccessImpl accessImpl = (FeatureImpl.DuringSetupAccessImpl) access; moduleLayerFeatureUtils = new ModuleLayerFeatureUtils(accessImpl.imageClassLoader); - - RuntimeModuleSupport.instance().setHostedToRuntimeModuleMapper(moduleLayerFeatureUtils::getOrCreateRuntimeModuleForHostedModule); + RuntimeModuleSupport.instance().setHostedToRuntimeModuleMapper(m -> moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(m, accessImpl)); /* * Register an object replacer that will ensure all references to hosted module instances * are replaced with the appropriate runtime module instance. */ - access.registerObjectReplacer(this::replaceHostedModules); + access.registerObjectReplacer(source -> replaceHostedModules(source, accessImpl)); } - private Object replaceHostedModules(Object source) { + private Object replaceHostedModules(Object source, AnalysisAccessBase access) { if (source instanceof Module module) { - return moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(module); + return moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(module, access); } else if (source instanceof Class clazz) { /* * If the field Class(=DynamicHub).module is not reachable, we do not see all Module * instances directly. So we also need to scan the module in Class/DynamicHub objects. */ - moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(clazz.getModule()); + moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(clazz.getModule(), access); } else if (source instanceof DynamicHub hub) { - moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(hub.getModule()); + moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(hub.getModule(), access); } return source; } @@ -451,7 +451,7 @@ private ModuleLayer synthesizeRuntimeModuleLayer(List parentLayers, runtimeModuleLayer = moduleLayerFeatureUtils.createNewModuleLayerInstance(runtimeModuleLayerConfiguration); Map nameToModule = moduleLayerFeatureUtils.synthesizeNameToModule(accessImpl, runtimeModuleLayer, clf); for (Module syntheticModule : syntheticModules) { - Module runtimeSyntheticModule = moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(syntheticModule); + Module runtimeSyntheticModule = moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(syntheticModule, accessImpl); nameToModule.putIfAbsent(runtimeSyntheticModule.getName(), runtimeSyntheticModule); moduleLayerFeatureUtils.patchModuleLayerField(accessImpl, runtimeSyntheticModule, runtimeModuleLayer); } @@ -538,8 +538,10 @@ private void replicateNativeAccess(AfterAnalysisAccessImpl accessImpl, Set modulesPair : modulePairs.entrySet()) { Module hosted = modulesPair.getKey(); Module runtime = modulesPair.getValue(); - if (moduleLayerFeatureUtils.allowsNativeAccess(hosted)) { - moduleLayerFeatureUtils.setNativeAccess(accessImpl, runtime, true); + if (moduleLayerFeatureUtils.allowsNativeAccess(hosted) || moduleLayerFeatureUtils.isNativeAccessEnabledForRuntimeModule(runtime)) { + if (!moduleLayerFeatureUtils.allowsNativeAccess(runtime)) { + moduleLayerFeatureUtils.enableNativeAccess(accessImpl, runtime); + } } } @@ -630,10 +632,14 @@ private static final class ModuleLayerFeatureUtils { private final Field moduleLayerModulesField; private final Field moduleReferenceLocationField; private final Field moduleReferenceImplLocationField; + private final Set nativeAccessEnabled; ModuleLayerFeatureUtils(ImageClassLoader cl) { runtimeModules = new HashMap<>(); imageClassLoader = cl; + nativeAccessEnabled = NativeImageClassLoaderOptions.EnableNativeAccess.getValue().values().stream().flatMap(m -> Arrays.stream(SubstrateUtil.split(m, ","))) + .collect(Collectors.toSet()); + Method classGetDeclaredMethods0Method = ReflectionUtil.lookupMethod(Class.class, "getDeclaredFields0", boolean.class); try { ModuleSupport.accessModuleByClass(ModuleSupport.Access.OPEN, ModuleLayerFeature.class, Module.class); @@ -694,6 +700,15 @@ private static final class ModuleLayerFeatureUtils { } } + private boolean isNativeAccessEnabledForRuntimeBootLayerModule(String runtimeModuleName) { + return nativeAccessEnabled.contains(runtimeModuleName); + } + + private boolean isNativeAccessEnabledForRuntimeModule(Module runtimeModule) { + String runtimeModuleName = runtimeModule.getName(); + return RuntimeModuleSupport.instance().getBootLayer() == runtimeModule.getLayer() && isNativeAccessEnabledForRuntimeBootLayerModule(runtimeModuleName); + } + /** * A manual field lookup is necessary due to reflection filters present in newer JDK * versions. This method should be removed once {@link ReflectionUtil} becomes immune to @@ -797,7 +812,7 @@ public Module getRuntimeModuleForHostedModule(ClassLoader loader, String hostedM } } - public Module getOrCreateRuntimeModuleForHostedModule(Module hostedModule) { + public Module getOrCreateRuntimeModuleForHostedModule(Module hostedModule, AnalysisAccessBase access) { /* * Special module instances such as ALL_UNNAMED and EVERYONE_MODULE are not replicated * as they only serve as marker modules (all their fields are null, including the loader @@ -806,11 +821,13 @@ public Module getOrCreateRuntimeModuleForHostedModule(Module hostedModule) { if (hostedModule == allUnnamedModule || hostedModule == everyoneModule) { return hostedModule; } else { - return getOrCreateRuntimeModuleForHostedModule(hostedModule.getClassLoader(), hostedModule.getName(), hostedModule.getDescriptor()); + boolean enableNativeAccess = allowsNativeAccess(hostedModule) || isNativeAccessEnabledForRuntimeBootLayerModule(hostedModule.getName()); + return getOrCreateRuntimeModuleForHostedModule(hostedModule.getClassLoader(), hostedModule.getName(), hostedModule.getDescriptor(), access, enableNativeAccess); } } - public Module getOrCreateRuntimeModuleForHostedModule(ClassLoader loader, String hostedModuleName, ModuleDescriptor runtimeModuleDescriptor) { + public Module getOrCreateRuntimeModuleForHostedModule(ClassLoader loader, String hostedModuleName, ModuleDescriptor runtimeModuleDescriptor, AnalysisAccessBase access, + boolean enableNativeAccess) { synchronized (runtimeModules) { Module runtimeModule = getRuntimeModuleForHostedModule(loader, hostedModuleName, true); if (runtimeModule != null) { @@ -828,6 +845,9 @@ public Module getOrCreateRuntimeModuleForHostedModule(ClassLoader loader, String } runtimeModules.putIfAbsent(loader, new HashMap<>()); runtimeModules.get(loader).put(hostedModuleName, runtimeModule); + if (enableNativeAccess) { + enableNativeAccess(access, runtimeModule); + } return runtimeModule; } } @@ -855,7 +875,8 @@ Map synthesizeNameToModule(AnalysisAccessBase access, ModuleLaye ModuleDescriptor descriptor = mref.descriptor(); String name = descriptor.name(); ClassLoader loader = clf.apply(name); - Module m = getOrCreateRuntimeModuleForHostedModule(loader, name, descriptor); + boolean nativeAccess = false; + Module m = getOrCreateRuntimeModuleForHostedModule(loader, name, descriptor, access, nativeAccess); if (!descriptor.equals(m.getDescriptor())) { moduleDescriptorField.set(m, descriptor); access.rescanField(m, moduleDescriptorField); @@ -1123,11 +1144,15 @@ boolean allowsNativeAccess(Module module) { } - void setNativeAccess(AfterAnalysisAccessImpl accessImpl, Module module, boolean value) { + /** + * Allows the given module to perform native access. + */ + void enableNativeAccess(AnalysisAccessBase access, Module module) { + VMError.guarantee(!allowsNativeAccess(module), "Cannot reset native access"); assert moduleEnableNativeAccessField != null : "Only available on JDK19+"; try { - moduleEnableNativeAccessField.set(module, value); - accessImpl.rescanField(module, moduleEnableNativeAccessField); + moduleEnableNativeAccessField.set(module, true); + access.rescanField(module, moduleEnableNativeAccessField); } catch (IllegalAccessException e) { throw VMError.shouldNotReachHere("Failed to reflectively set Module.enableNativeAccess.", e); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java index e243c31a2167d..8dee71d3ca543 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java @@ -75,6 +75,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import com.oracle.svm.core.SubstrateUtil; import org.graalvm.collections.EconomicMap; import org.graalvm.collections.EconomicSet; import org.graalvm.collections.MapCursor; @@ -379,7 +380,29 @@ private static void implAddReadsAllUnnamed(Module module) { implAddReadsAllUnnamed.setAccessible(true); implAddReadsAllUnnamed.invoke(module); } catch (ReflectiveOperationException | NoSuchElementException e) { - VMError.shouldNotReachHere("Could reflectively call Module.implAddReadsAllUnnamed", e); + VMError.shouldNotReachHere("Could not reflectively call Module.implAddReadsAllUnnamed", e); + } + } + + static void implAddEnableNativeAccess(Module module) { + try { + Method implAddEnableNativeAccess = Module.class.getDeclaredMethod("implAddEnableNativeAccess"); + ModuleSupport.accessModuleByClass(ModuleSupport.Access.OPEN, NativeImageClassLoaderSupport.class, Module.class); + implAddEnableNativeAccess.setAccessible(true); + implAddEnableNativeAccess.invoke(module); + } catch (ReflectiveOperationException | NoSuchElementException e) { + VMError.shouldNotReachHere("Could not reflectively call Module.implAddEnableNativeAccess", e); + } + } + + static void implAddEnableNativeAccessToAllUnnamed() { + try { + Method implAddEnableNativeAccess = Module.class.getDeclaredMethod("implAddEnableNativeAccessToAllUnnamed"); + ModuleSupport.accessModuleByClass(ModuleSupport.Access.OPEN, NativeImageClassLoaderSupport.class, Module.class); + implAddEnableNativeAccess.setAccessible(true); + implAddEnableNativeAccess.invoke(null); + } catch (ReflectiveOperationException | NoSuchElementException e) { + VMError.shouldNotReachHere("Could not reflectively call Module.implAddEnableNativeAccessToAllUnnamed", e); } } @@ -436,6 +459,14 @@ void processClassLoaderOptions() { } } }); + NativeImageClassLoaderOptions.EnableNativeAccess.getValue(parsedHostedOptions).values().stream().flatMap(m -> Arrays.stream(SubstrateUtil.split(m, ","))).forEach(moduleName -> { + if ("ALL-UNNAMED".equals(moduleName)) { + implAddEnableNativeAccessToAllUnnamed(); + } else { + Module module = findModule(moduleName).orElseThrow(() -> userWarningModuleNotFound(NativeImageClassLoaderOptions.EnableNativeAccess, moduleName)); + implAddEnableNativeAccess(module); + } + }); } private static void warn(String m) {