From 34898a59aef233769a206d4479f6c0817383811a Mon Sep 17 00:00:00 2001 From: jvukicev Date: Mon, 4 Aug 2025 11:53:32 +0200 Subject: [PATCH] Filter out image-provided class and module path entries from dynamic access and preserve selectors --- .../com/oracle/svm/core/SharedConstants.java | 6 +++ .../core/doc-files/TrackDynamicAccessHelp.txt | 11 +++--- .../com/oracle/svm/driver/NativeImage.java | 1 + .../hosted/NativeImageClassLoaderSupport.java | 37 +++++++++++++++++-- .../phases/DynamicAccessDetectionPhase.java | 1 - 5 files changed, 46 insertions(+), 10 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SharedConstants.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SharedConstants.java index 3adbdabdf28f..2e3e9751e6ae 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SharedConstants.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SharedConstants.java @@ -41,4 +41,10 @@ public final class SharedConstants { * environment variable is not set for all other builds. */ public static final String REBUILD_AFTER_ANALYSIS_MARKER = "com.oracle.svm.rebuild"; + + /** + * The name of the environment variable containing paths to JAR files that are provided + * internally by the native image on its image class or module path. + */ + public static final String IMAGE_PROVIDED_JARS_ENV_VARIABLE = "com.oracle.svm.provided"; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/doc-files/TrackDynamicAccessHelp.txt b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/doc-files/TrackDynamicAccessHelp.txt index c5cca972b525..b74aa2427c22 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/doc-files/TrackDynamicAccessHelp.txt +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/doc-files/TrackDynamicAccessHelp.txt @@ -9,11 +9,12 @@ The flag can be used in following ways: 1. -H:TrackDynamicAccess=all reports all dynamic access calls made across the entire project 2. -H:TrackDynamicAccess=path= reports all dynamic access calls made from the specified class-path entry 3. -H:TrackDynamicAccess=module= reports all dynamic access calls made from the specified module -4. -H:TrackDynamicAccess=package= reports all dynamic access calls made from the specified package -5. -H:TrackDynamicAccess=none disables all previous selections for dynamic access detection -6. -H:TrackDynamicAccess=to-console outputs all detected dynamic access calls to the console -7. -H:TrackDynamicAccess=no-dump disables the serialization of detected dynamic access calls -8. A comma-separated list of the previous cases. For example, -H:TrackDynamicAccess=path=,module=,package= +4. -H:TrackDynamicAccess=module=ALL-UNNAMED reports all dynamic access calls made from all class-path entries +5. -H:TrackDynamicAccess=package= reports all dynamic access calls made from the specified package +6. -H:TrackDynamicAccess=none disables all previous selections for dynamic access detection +7. -H:TrackDynamicAccess=to-console outputs all detected dynamic access calls to the console +8. -H:TrackDynamicAccess=no-dump disables the serialization of detected dynamic access calls +9. A comma-separated list of the previous cases. For example, -H:TrackDynamicAccess=path=,module=,package= Example of the option usage: diff --git a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java index 8febd0ebe186..9e9752cc864c 100644 --- a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java +++ b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java @@ -1474,6 +1474,7 @@ private int completeImageBuild() { finalImageClasspath.addAll(finalImageProvidedJars); } finalImageProvidedJars.forEach(this::processClasspathNativeImageMetaInf); + imageBuilderJavaArgs.add("-D" + SharedConstants.IMAGE_PROVIDED_JARS_ENV_VARIABLE + "=" + String.join(File.pathSeparator, finalImageProvidedJars.stream().map(Path::toString).toList())); if (!config.buildFallbackImage()) { Optional fallbackThresholdEntry = getHostedOptionArgument(imageBuilderArgs, oHFallbackThreshold); 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 45fbe6091bba..aff60ff3ee36 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 @@ -78,6 +78,7 @@ import org.graalvm.nativeimage.libgraal.hosted.LibGraalLoader; import com.oracle.svm.core.NativeImageClassLoaderOptions; +import com.oracle.svm.core.SharedConstants; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.option.AccumulatingLocatableMultiOptionValue; @@ -114,6 +115,8 @@ public final class NativeImageClassLoaderSupport { private final List imagemp; private final List buildmp; + private final Set imageProvidedJars; + private final EconomicMap> classes; private final EconomicMap> packages; private final EconomicSet emptySet; @@ -245,6 +248,8 @@ protected NativeImageClassLoaderSupport(ClassLoader defaultSystemClassLoader, St upgradeAndSystemModuleFinder = createUpgradeAndSystemModuleFinder(); + imageProvidedJars = parseImageProvidedJarsProperty(); + ModuleFinder modulePathsFinder = getModulePathsFinder(); Set moduleNames = modulePathsFinder.findAll().stream() .map(moduleReference -> moduleReference.descriptor().name()) @@ -305,7 +310,7 @@ public List getClassLoaders() { return classLoaders; } - private ModuleFinder getModulePathsFinder() { + public ModuleFinder getModulePathsFinder() { return ModuleFinder.of(imagemp.toArray(Path[]::new)); } @@ -324,7 +329,12 @@ public void loadAllClasses(ForkJoinPool executor, ImageClassLoader imageClassLoa LogUtils.warning(msg); var origin = new IncludeOptionsSupport.ExtendedOptionWithOrigin(new IncludeOptionsSupport.ExtendedOption("", PreserveOptionsSupport.PRESERVE_ALL), preserveAllOrigin); - getModulePathsFinder().findAll().forEach(m -> preserveSelectors.addModule(m.descriptor().name(), origin)); + for (ModuleReference m : getModulePathsFinder().findAll()) { + var modulePath = m.location().map(Path::of).orElse(null); + if (!imageProvidedJars.contains(modulePath)) { + preserveSelectors.addModule(m.descriptor().name(), origin); + } + } PreserveOptionsSupport.JDK_MODULES_TO_PRESERVE.forEach(moduleName -> preserveSelectors.addModule(moduleName, origin)); preserveSelectors.addModule(ALL_UNNAMED, origin); } @@ -775,6 +785,18 @@ static Optional getMainClassFromModule(Object module) { return ((Module) module).getDescriptor().mainClass(); } + private static Set parseImageProvidedJarsProperty() { + Set imageProvidedJars = new HashSet<>(); + String args = System.getProperty(SharedConstants.IMAGE_PROVIDED_JARS_ENV_VARIABLE, ""); + if (!args.isEmpty()) { + String[] parts = args.split(File.pathSeparator); + for (String part : parts) { + imageProvidedJars.add(Path.of(part)); + } + } + return Collections.unmodifiableSet(imageProvidedJars); + } + private final class LoadClassHandler { private final ForkJoinPool executor; @@ -1180,7 +1202,12 @@ public void setPreserveAll(ValueWithOrigin valueWithOrigin) { public void setTrackAllDynamicAccess(ValueWithOrigin valueWithOrigin) { var origin = new IncludeOptionsSupport.ExtendedOptionWithOrigin(new IncludeOptionsSupport.ExtendedOption("", DynamicAccessDetectionFeature.TRACK_ALL), valueWithOrigin); - getModulePathsFinder().findAll().forEach(m -> dynamicAccessSelectors.addModule(m.descriptor().name(), origin)); + for (ModuleReference m : getModulePathsFinder().findAll()) { + var modulePath = m.location().map(Path::of).orElse(null); + if (!imageProvidedJars.contains(modulePath)) { + dynamicAccessSelectors.addModule(m.descriptor().name(), origin); + } + } dynamicAccessSelectors.addModule(ALL_UNNAMED, origin); } @@ -1307,7 +1334,9 @@ public void addModule(String moduleName, IncludeOptionsSupport.ExtendedOptionWit IncludeOptionsSupport.ExtendedOptionWithOrigin includeOptionsSupport = new IncludeOptionsSupport.ExtendedOptionWithOrigin(extendedOptionWithOrigin.option(), extendedOptionWithOrigin.valueWithOrigin()); for (Path path : applicationClassPath()) { - classpathEntries.put(path, includeOptionsSupport); + if (!imageProvidedJars.contains(path)) { + classpathEntries.put(path, includeOptionsSupport); + } } } else { moduleNames.put(moduleName, extendedOptionWithOrigin); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/DynamicAccessDetectionPhase.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/DynamicAccessDetectionPhase.java index b83dd0e2a80e..c0d413e310db 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/DynamicAccessDetectionPhase.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/DynamicAccessDetectionPhase.java @@ -221,7 +221,6 @@ public record MethodInfo(DynamicAccessKind accessKind, String signature) { new MethodSignature("getResourceAsStream", String.class))); resourceMethodSignatures.put(Class.class, Set.of( new MethodSignature("getResource", String.class), - new MethodSignature("getResourceAsStream", String.class))); foreignMethodSignatures.put(Linker.class, Set.of(