Skip to content

Commit

Permalink
svm: set native access for the image run time modules
Browse files Browse the repository at this point in the history
  • Loading branch information
zapster committed Nov 13, 2024
1 parent 369ea76 commit ed86994
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -451,7 +451,7 @@ private ModuleLayer synthesizeRuntimeModuleLayer(List<ModuleLayer> parentLayers,
runtimeModuleLayer = moduleLayerFeatureUtils.createNewModuleLayerInstance(runtimeModuleLayerConfiguration);
Map<String, Module> 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);
}
Expand Down Expand Up @@ -538,8 +538,10 @@ private void replicateNativeAccess(AfterAnalysisAccessImpl accessImpl, Set<Modul
for (Map.Entry<Module, Module> 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);
}
}
}

Expand Down Expand Up @@ -630,10 +632,14 @@ private static final class ModuleLayerFeatureUtils {
private final Field moduleLayerModulesField;
private final Field moduleReferenceLocationField;
private final Field moduleReferenceImplLocationField;
private final Set<String> 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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand All @@ -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;
}
}
Expand Down Expand Up @@ -855,7 +875,8 @@ Map<String, Module> 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);
Expand Down Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit ed86994

Please sign in to comment.