From a2989dd12c31f159b8dcf376012653bc5d55a086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Tue, 18 Nov 2025 11:04:00 +0100 Subject: [PATCH 1/3] Add gdb-debughelpers HLRep for com.oracle.svm.espresso.classfile.descriptors.Symbol --- substratevm/debug/gdbpy/gdb-debughelpers.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/substratevm/debug/gdbpy/gdb-debughelpers.py b/substratevm/debug/gdbpy/gdb-debughelpers.py index aca9626e4d6a..3b5dec7bd241 100644 --- a/substratevm/debug/gdbpy/gdb-debughelpers.py +++ b/substratevm/debug/gdbpy/gdb-debughelpers.py @@ -831,6 +831,21 @@ def HLRep(original_class): return original_class +@HLRep +class EspressoSymbol: + target_type = 'com.oracle.svm.espresso.classfile.descriptors.Symbol' + + def __init__(self, svm_util: SVMUtil, obj: gdb.Value): + trace(f' - __init__({obj.type} @ {hex(svm_util.get_adr(obj))})') + value = svm_util.get_obj_field(obj, 'value') + self.__length = svm_util.get_int_field(value, 'len') + self.__array = svm_util.get_obj_field(value, 'data', None) + + def to_string(self) -> str: + trace(' - to_string') + byte_list = [self.__array[i] for i in range(self.__length)] + return f'EspressoSymbol({str(bytes(byte_list))})' + @HLRep class ArrayList: target_type = 'java.util.ArrayList' From 28ef218c69ea31c540b2c629d5cfe38f264b7c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Wed, 29 Oct 2025 16:56:53 +0100 Subject: [PATCH 2/3] Tag lowered ImageName and ImagePath as driver-generated to avoid misleading warnings in build output --- .../src/com/oracle/svm/driver/MacroOptionHandler.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MacroOptionHandler.java b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MacroOptionHandler.java index 5ef32ef3fff1..d0ef7a943096 100644 --- a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MacroOptionHandler.java +++ b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/MacroOptionHandler.java @@ -29,6 +29,7 @@ import java.util.HashSet; import com.oracle.svm.core.OS; +import com.oracle.svm.core.option.OptionOrigin; import com.oracle.svm.core.option.OptionUtils.InvalidMacroException; import com.oracle.svm.driver.MacroOption.AddedTwiceException; import com.oracle.svm.driver.MacroOption.VerboseInvalidMacroException; @@ -96,12 +97,12 @@ private void applyEnabled(MacroOption.EnabledOption enabledOption, String argume String imageName = enabledOption.getProperty(config, "ImageName"); if (imageName != null) { - nativeImage.addPlainImageBuilderArg(nativeImage.oHName + imageName); + nativeImage.addPlainImageBuilderArg(nativeImage.oHName + imageName, OptionOrigin.originDriver); } String imagePath = enabledOption.getProperty(config, "ImagePath"); if (imagePath != null) { - nativeImage.addPlainImageBuilderArg(nativeImage.oHPath + imagePath); + nativeImage.addPlainImageBuilderArg(nativeImage.oHPath + imagePath, OptionOrigin.originDriver); } String imageClass = enabledOption.getProperty(config, "ImageClass"); From 2702df58122e536d1112b4aa45a5980f29dac6ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 30 Oct 2025 14:12:51 +0100 Subject: [PATCH 3/3] Add macro to build Crema-based libjvm.so --- substratevm/mx.substratevm/mx_substratevm.py | 41 +++++ .../pointsto/standalone/StandaloneHost.java | 5 + .../com/oracle/graal/pointsto/api/HostVM.java | 14 ++ .../pointsto/reports/CallTreePrinter.java | 10 +- .../pointsto/reports/ObjectTreePrinter.java | 11 +- .../graal/pointsto/reports/ReportUtils.java | 20 --- .../posix/PosixSystemPropertiesSupport.java | 10 +- .../darwin/DarwinSystemPropertiesSupport.java | 5 + .../linux/LinuxSystemPropertiesSupport.java | 5 + .../WindowsSystemPropertiesSupport.java | 5 + .../core/hub/registry/BootClassRegistry.java | 6 +- .../svm/core/jdk/SystemPropertiesSupport.java | 39 +++++ .../jdk/Target_java_lang_ClassLoader.java | 13 +- ...dk_internal_loader_BuiltinClassLoader.java | 13 +- ...t_jdk_internal_module_ServicesCatalog.java | 12 +- .../svm/core/jni/functions/JNIFunctions.java | 11 +- .../svm/core/libjvm/LibJVMEntryPoints.java | 79 ++++++++++ .../core/libjvm/LibJVMMainMethodWrappers.java | 141 ++++++++++++++++++ .../svm/core/libjvm/LibJVMSubstitutions.java | 72 +++++++++ .../reflect/proxy/DynamicProxySupport.java | 60 +++++++- .../com/oracle/svm/driver/NativeImage.java | 21 +-- .../oracle/svm/hosted/ClassLoaderFeature.java | 29 ++-- .../oracle/svm/hosted/ImageClassLoader.java | 23 +++ .../svm/hosted/NativeImageGenerator.java | 5 + .../hosted/NativeImageSystemClassLoader.java | 22 +-- .../src/com/oracle/svm/hosted/SVMHost.java | 19 ++- .../image/NativeImageDebugInfoProvider.java | 7 +- .../imagelayer/InitialLayerFeature.java | 2 + .../hosted/jdk/JNIRegistrationSupport.java | 10 ++ .../svm/hosted/libjvm/LibJVMFeature.java | 63 ++++++++ .../reflect/proxy/DynamicProxyFeature.java | 2 +- .../hosted/snippets/ReflectionPlugins.java | 18 ++- .../src/JvmFuncs.c | 7 + .../src/JvmFuncs.c | 15 ++ .../polyglot/SeparatedClassLoadersTest.java | 17 ++- 35 files changed, 720 insertions(+), 112 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/libjvm/LibJVMEntryPoints.java create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/libjvm/LibJVMMainMethodWrappers.java create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/libjvm/LibJVMSubstitutions.java create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/libjvm/LibJVMFeature.java diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index b1d126938ed0..3cac985954a6 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -1813,6 +1813,47 @@ def prevent_build_path_in_libgraal(): mx_sdk_vm.register_graalvm_component(libsvmjdwp) +lib_jvm_preserved_packages = [ + 'java.util', + 'java.util.stream', + 'java.util.concurrent.locks', + 'java.lang', + 'java.lang.invoke', + 'java.io' +] + +mx_sdk_vm.register_graalvm_component(mx_sdk_vm.GraalVmJreComponent( + suite=suite, + name='SubstrateVM java', + short_name='svmjava', + dir_name='svm', + license_files=[], + third_party_license_files=[], + dependencies=[], + jar_distributions=[], + builder_jar_distributions=[], + support_distributions=[], + priority=0, + library_configs=[ + mx_sdk_vm.LibraryConfig( + destination='', + jar_distributions=[], + build_args=[ + '--features=com.oracle.svm.hosted.libjvm.LibJVMFeature' + ] + svm_experimental_options([ + '-H:+RuntimeClassLoading', + '-H:+InterpreterTraceSupport', + '-H:+AllowJRTFileSystem' + ] + ['-H:Preserve=package=' + pkg for pkg in lib_jvm_preserved_packages]), + headers=False, + home_finder=False, + ), + ], + support_libraries_distributions=[], + stability="experimental", + jlink=False, +)) + def _native_image_utils_extra_jvm_args(): packages = ['jdk.graal.compiler/jdk.graal.compiler.phases.common', 'jdk.internal.vm.ci/jdk.vm.ci.meta', 'jdk.internal.vm.ci/jdk.vm.ci.services', 'jdk.graal.compiler/jdk.graal.compiler.core.common.util'] args = ['--add-exports=' + packageName + '=ALL-UNNAMED' for packageName in packages] diff --git a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/StandaloneHost.java b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/StandaloneHost.java index 64bc6daa3faa..7e8f54c0197e 100644 --- a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/StandaloneHost.java +++ b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/StandaloneHost.java @@ -125,4 +125,9 @@ public Optional handleForeignCall(ForeignCallDescriptor foreignC public boolean isClosedTypeWorld() { return isClosedTypeWorld; } + + @Override + public String loaderName(AnalysisType type) { + return loaderName(typeToClass.get(type).getClassLoader()); + } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java index 52adbef0f881..7edb19bd0c3c 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java @@ -476,6 +476,20 @@ public Set getSharedLayerForbiddenModules() { return Set.of(); } + public abstract String loaderName(AnalysisType type); + + public static String loaderName(ClassLoader loader) { + if (loader == null) { + return "null"; + } + var loaderName = loader.getName(); + if (loaderName == null || loaderName.isBlank()) { + return loader.getClass().getName(); + } else { + return loaderName; + } + } + /** * Helpers to determine what analysis actions should be taken for a given Multi-Method version. */ diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/CallTreePrinter.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/CallTreePrinter.java index 281a14d0094d..2d8ed7b54f55 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/CallTreePrinter.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/CallTreePrinter.java @@ -110,7 +110,8 @@ static class MethodNodeReference implements Node { @Override public String format() { - return ReportUtils.loaderName(methodNode.method.getDeclaringClass()) + ':' + methodNode.method.format(METHOD_FORMAT) + " id-ref=" + methodNode.id; + var hostVM = methodNode.method.getUniverse().hostVM(); + return hostVM.loaderName(methodNode.method.getDeclaringClass()) + ':' + methodNode.method.format(METHOD_FORMAT) + " id-ref=" + methodNode.id; } } @@ -140,7 +141,7 @@ void addInvoke(InvokeNode invoke) { @Override public String format() { - return ReportUtils.loaderName(method.getDeclaringClass()) + ':' + method.format(METHOD_FORMAT) + " id=" + id; + return method.getUniverse().hostVM().loaderName(method.getDeclaringClass()) + ':' + method.format(METHOD_FORMAT) + " id=" + id; } } @@ -307,7 +308,7 @@ private static void printCallTreeNode(PrintWriter out, String prefix, MethodNode private void printUsedMethods(PrintWriter out) { List methodsList = new ArrayList<>(); for (AnalysisMethod method : methodToNode.keySet()) { - methodsList.add(ReportUtils.loaderName(method.getDeclaringClass()) + ':' + method.format(METHOD_FORMAT)); + methodsList.add(method.getUniverse().hostVM().loaderName(method.getDeclaringClass()) + ':' + method.format(METHOD_FORMAT)); } methodsList.sort(null); for (String name : methodsList) { @@ -334,7 +335,8 @@ public Set classesSet(boolean packageNameOnly) { name = packagePrefix(name); } } - classSet.add(ReportUtils.loaderName(type) + ':' + name); + + classSet.add(type.getUniverse().hostVM().loaderName(type) + ':' + name); } return classSet; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/ObjectTreePrinter.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/ObjectTreePrinter.java index 1861df62ad54..920fd9189a88 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/ObjectTreePrinter.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/ObjectTreePrinter.java @@ -44,6 +44,7 @@ import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.ObjectScanner; import com.oracle.graal.pointsto.ObjectScanningObserver; +import com.oracle.graal.pointsto.api.HostVM; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; @@ -84,12 +85,16 @@ String format() { private static String format(Object srcObj) { return switch (srcObj) { - case AnalysisField field -> ReportUtils.loaderName(field.getDeclaringClass()) + ':' + field.format("%H.%n:%T"); - case AnalysisMethod method -> ReportUtils.loaderName(method.getDeclaringClass()) + ':' + method.format("%H.%n(%p)"); + case AnalysisField field -> loaderName(field.getDeclaringClass()) + ':' + field.format("%H.%n:%T"); + case AnalysisMethod method -> loaderName(method.getDeclaringClass()) + ':' + method.format("%H.%n(%p)"); case BytecodePosition bcp -> "%s [bci: %d]".formatted(format(bcp.getMethod()), bcp.getBCI()); default -> throw JVMCIError.shouldNotReachHere("unknown srcObj"); }; } + + private static String loaderName(AnalysisType type) { + return type.getUniverse().hostVM().loaderName(type); + } } static class ObjectNodeBase { @@ -400,7 +405,7 @@ static String constantAsString(BigBang bb, JavaConstant constant) { Object object = constantAsObject(bb, constant); String loaderPrefix = ""; if (object != null) { - loaderPrefix = ReportUtils.loaderName(object.getClass().getClassLoader()) + ':'; + loaderPrefix = HostVM.loaderName(object.getClass().getClassLoader()) + ':'; } if (object instanceof String) { String str = (String) object; diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/ReportUtils.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/ReportUtils.java index 21e38e9e6751..5f729d34addd 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/ReportUtils.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/ReportUtils.java @@ -319,24 +319,4 @@ private static void followInput(PointsToAnalysis bb, TypeFlow flow, AnalysisT } } } - - public static String loaderName(AnalysisType type) { - var declaringJavaClass = type.getJavaClass(); - if (declaringJavaClass == null) { - return "err"; - } - return loaderName(declaringJavaClass.getClassLoader()); - } - - public static String loaderName(ClassLoader loader) { - if (loader == null) { - return "null"; - } - var loaderName = loader.getName(); - if (loaderName == null || loaderName.isBlank()) { - return loader.getClass().getName(); - } else { - return loaderName; - } - } } diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSystemPropertiesSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSystemPropertiesSupport.java index 928b61540c85..493ea0684af0 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSystemPropertiesSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSystemPropertiesSupport.java @@ -24,7 +24,6 @@ */ package com.oracle.svm.core.posix; -import jdk.graal.compiler.word.Word; import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CTypeConversion; @@ -33,8 +32,17 @@ import com.oracle.svm.core.posix.headers.Limits; import com.oracle.svm.core.posix.headers.Unistd; +import jdk.graal.compiler.word.Word; + public abstract class PosixSystemPropertiesSupport extends SystemPropertiesSupport { + @Override + protected String jvmLibName() { + return "libjvm" + jvmLibSuffix(); + } + + protected abstract String jvmLibSuffix(); + /* * Initialization code is adapted from the JDK native code that initializes the system * properties, as found in src/solaris/native/java/lang/java_props_md.c diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSystemPropertiesSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSystemPropertiesSupport.java index 5b7f221fca3e..654c96fce1ba 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSystemPropertiesSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSystemPropertiesSupport.java @@ -132,6 +132,11 @@ protected String osVersionValue() { } return osVersionValue = "Unknown"; } + + @Override + protected String jvmLibSuffix() { + return ".dylib"; + } } @SingletonTraits(access = BuildtimeAccessOnly.class, layeredCallbacks = NoLayeredCallbacks.class, layeredInstallationKind = Disallowed.class) diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxSystemPropertiesSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxSystemPropertiesSupport.java index 5ef04bac00fb..5d3757d8f060 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxSystemPropertiesSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxSystemPropertiesSupport.java @@ -94,6 +94,11 @@ protected String osVersionValue() { } return "Unknown"; } + + @Override + protected String jvmLibSuffix() { + return ".so"; + } } @SingletonTraits(access = BuildtimeAccessOnly.class, layeredCallbacks = SingleLayer.class, layeredInstallationKind = Independent.class) diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsSystemPropertiesSupport.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsSystemPropertiesSupport.java index fd8dcc88462f..d831c4cc0aa7 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsSystemPropertiesSupport.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsSystemPropertiesSupport.java @@ -219,6 +219,11 @@ protected String osVersionValue() { return cachedOsVersion; } + @Override + protected String jvmLibName() { + return "jvm.dll"; + } + private void computeOsNameAndVersion() { /* * Reimplementation of code from java_props_md.c diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/BootClassRegistry.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/BootClassRegistry.java index 5c6b7b4cd569..683fa76ebcae 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/BootClassRegistry.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/BootClassRegistry.java @@ -38,6 +38,7 @@ import com.oracle.svm.core.util.VMError; import com.oracle.svm.espresso.classfile.descriptors.Symbol; import com.oracle.svm.espresso.classfile.descriptors.Type; +import com.oracle.svm.espresso.classfile.descriptors.TypeSymbols; /** * This class registry corresponds to the {@code null} class loader when runtime class loading is @@ -77,7 +78,8 @@ public synchronized Class doLoadClass(Symbol type) { if (moduleName == null) { return null; } - Path classPath = getFileSystem().getPath("/modules/" + moduleName + "/" + type + ".class"); + var jrtTypePath = TypeSymbols.typeToName(type); + Path classPath = getFileSystem().getPath("/modules/" + moduleName + "/" + jrtTypePath + ".class"); if (!Files.exists(classPath)) { return null; } @@ -93,7 +95,7 @@ private static String packageFromType(Symbol type) { if (lastSlash == -1) { return null; } - return type.subSequence(0, lastSlash).toString(); + return type.subSequence(1, lastSlash).toString().replace('/', '.'); } @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemPropertiesSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemPropertiesSupport.java index 1b6a969bc10e..4e474633ca2f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemPropertiesSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemPropertiesSupport.java @@ -26,6 +26,7 @@ import static jdk.graal.compiler.core.common.LibGraalSupport.LIBGRAAL_SETTING_PROPERTY_PREFIX; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -40,13 +41,17 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.hosted.RuntimeSystemProperties; +import org.graalvm.nativeimage.impl.ProcessPropertiesSupport; import org.graalvm.nativeimage.impl.RuntimeSystemPropertiesSupport; import com.oracle.svm.core.FutureDefaultsOptions; +import com.oracle.svm.core.NeverInline; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.VM; import com.oracle.svm.core.c.locale.LocaleSupport; import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.libjvm.LibJVMMainMethodWrappers; +import com.oracle.svm.core.snippets.KnownIntrinsics; import com.oracle.svm.core.util.VMError; import jdk.graal.compiler.api.replacements.Fold; @@ -152,6 +157,7 @@ protected SystemPropertiesSupport() { lazyProperties.add(new LazySystemProperty(UserSystemProperty.NAME, this::userNameValue)); lazyProperties.add(new LazySystemProperty(UserSystemProperty.HOME, this::userHomeValue)); lazyProperties.add(new LazySystemProperty(UserSystemProperty.DIR, this::userDirValue)); + lazyProperties.add(new LazySystemProperty("java.home", this::javaHomeValue)); lazyProperties.add(new LazySystemProperty("java.io.tmpdir", this::javaIoTmpdirValue)); lazyProperties.add(new LazySystemProperty("java.library.path", this::javaLibraryPathValue)); lazyProperties.add(new LazySystemProperty("os.version", this::osVersionValue)); @@ -351,6 +357,39 @@ private synchronized void initializeProperty(LazySystemProperty property) { protected abstract String osVersionValue(); + @NeverInline("Reads the return address.") + private String javaHomeValue() { + + if (!ImageSingletons.contains(LibJVMMainMethodWrappers.class)) { + return null; + } + + if (!SubstrateOptions.SharedLibrary.getValue()) { + throw VMError.shouldNotReachHere("Invalid " + jvmLibName() + " image. Not a shared library image."); + } + var objectFileStr = ImageSingletons.lookup(ProcessPropertiesSupport.class).getObjectFile(KnownIntrinsics.readReturnAddress()); + if (objectFileStr == null) { + throw VMError.shouldNotReachHere("Unable to get path to " + jvmLibName() + " image."); + } + var pathToSharedLib = Path.of(objectFileStr); + if (!pathToSharedLib.endsWith(jvmLibName())) { + throw VMError.shouldNotReachHere("Invalid name for a " + jvmLibName() + " image: " + objectFileStr); + } + // At this point we know that this is a libjvm shared library image + try { + return pathToSharedLib // /{lib|bin}/svm/ + .getParent() // /{lib|bin}/svm + .getParent() // /{lib|bin} + .getParent().toString(); + } catch (NullPointerException e) { + throw VMError.shouldNotReachHere("Unable to determine java.home for " + objectFileStr); + } + } + + protected String jvmLibName() { + throw VMError.shouldNotReachHere("System property java.home is not supported in this configuration"); + } + protected abstract String javaIoTmpdirValue(); protected abstract String javaLibraryPathValue(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_ClassLoader.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_ClassLoader.java index c7cde95441ea..7b5d53ade8ef 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_ClassLoader.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_ClassLoader.java @@ -103,17 +103,22 @@ public final class Target_java_lang_ClassLoader { @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = AssertionLockComputer.class, isFinal = true) // GR-62338 private Object assertionLock; - @Alias // - private static ClassLoader scl; + @Delete private static ClassLoader scl; @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = ClassRegistries.ClassRegistryComputer.class)// @TargetElement(onlyWith = ClassForNameSupport.RespectsClassLoader.class)// public volatile AbstractClassRegistry classRegistry; + @Alias + static native ClassLoader getBuiltinAppClassLoader(); + @Substitute public static ClassLoader getSystemClassLoader() { - VMError.guarantee(scl != null); - return scl; + /* + * Setting custom SystemClassLoader via java.system.class.loader system property currently + * not supported for native-images. + */ + return getBuiltinAppClassLoader(); } @Delete diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_jdk_internal_loader_BuiltinClassLoader.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_jdk_internal_loader_BuiltinClassLoader.java index 45fd4da1c477..934c1c4ed4c9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_jdk_internal_loader_BuiltinClassLoader.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_jdk_internal_loader_BuiltinClassLoader.java @@ -32,10 +32,14 @@ import java.util.Enumeration; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.graalvm.nativeimage.hosted.FieldValueTransformer; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.RecomputeFieldValue; +import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.TargetElement; @@ -45,7 +49,7 @@ @SuppressWarnings({"unused", "static-method"}) final class Target_jdk_internal_loader_BuiltinClassLoader { - @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) // + @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = NewConcurrentHashMap.class) // private Map moduleToReader; @Substitute @@ -106,4 +110,11 @@ private URL findResourceOnClassPath(String name) { private Enumeration findResourcesOnClassPath(String name) { return ResourcesHelper.nameToResourceEnumerationURLs(name); } + + static final class NewConcurrentHashMap implements FieldValueTransformer { + @Override + public Object transform(Object receiver, Object originalValue) { + return new ConcurrentHashMap<>(); + } + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_jdk_internal_module_ServicesCatalog.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_jdk_internal_module_ServicesCatalog.java index bc4cec8d4c92..e98c7ca65930 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_jdk_internal_module_ServicesCatalog.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_jdk_internal_module_ServicesCatalog.java @@ -24,12 +24,16 @@ */ package com.oracle.svm.core.jdk; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; + import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.RecomputeFieldValue; +import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.util.VMError; + import jdk.internal.loader.ClassLoaderValue; import jdk.internal.module.ServicesCatalog; -import org.graalvm.nativeimage.hosted.FieldValueTransformer; @SuppressWarnings("unused") @TargetClass(value = ServicesCatalog.class) @@ -37,6 +41,12 @@ final class Target_jdk_internal_module_ServicesCatalog { @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = ServicesCatalogCLVTransformer.class, isFinal = true) // static ClassLoaderValue CLV; + + @Substitute + static void putServicesCatalog(ClassLoader loader, ServicesCatalog catalog) { + ServicesCatalog previous = CLV.get(loader); + VMError.guarantee(previous.equals(catalog), "ServicesCatalog.CLV is fully prepared at image build-time"); + } } final class ServicesCatalogCLVTransformer implements FieldValueTransformer { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIFunctions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIFunctions.java index 5ecf20cd2ccc..d3bc14e61364 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIFunctions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIFunctions.java @@ -110,6 +110,7 @@ import com.oracle.svm.core.jni.headers.JNIObjectRefType; import com.oracle.svm.core.jni.headers.JNIValue; import com.oracle.svm.core.jni.headers.JNIVersion; +import com.oracle.svm.core.libjvm.LibJVMMainMethodWrappers; import com.oracle.svm.core.log.Log; import com.oracle.svm.core.monitor.MonitorInflationCause; import com.oracle.svm.core.monitor.MonitorSupport; @@ -1641,14 +1642,14 @@ static long GetStringUTFLengthAsLong(JNIEnvironment env, JNIObjectHandle hstr) { * JNI functions. */ public static class Support { - static class JNIEnvEnterPrologue implements CEntryPointOptions.Prologue { + public static class JNIEnvEnterPrologue implements CEntryPointOptions.Prologue { @Uninterruptible(reason = "prologue") public static int enter(JNIEnvironment env) { return CEntryPointActions.enter((IsolateThread) env); } } - static class ReturnNullHandle implements CEntryPointOptions.PrologueBailout { + public static class ReturnNullHandle implements CEntryPointOptions.PrologueBailout { @Uninterruptible(reason = "prologue") public static JNIObjectHandle bailout(int prologueResult) { return JNIObjectHandles.nullHandle(); @@ -1840,7 +1841,11 @@ static JNIMethodId getMethodID(JNIObjectHandle hclazz, CCharPointer cname, CChar return getMethodID(clazz, name, signature, isStatic); } - private static JNIMethodId getMethodID(Class clazz, CharSequence name, CharSequence signature, boolean isStatic) { + private static JNIMethodId getMethodID(Class origClazz, CharSequence name, CharSequence signature, boolean isStatic) { + + // Workaround for GR-71358 + Class clazz = LibJVMMainMethodWrappers.patchMethodHolderClass(origClazz); + JNIMethodId methodID = JNIReflectionDictionary.getMethodID(clazz, name, signature, isStatic); if (methodID.isNull()) { String message = clazz.getName() + "." + name + signature; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/libjvm/LibJVMEntryPoints.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/libjvm/LibJVMEntryPoints.java new file mode 100644 index 000000000000..38f676b5b7e0 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/libjvm/LibJVMEntryPoints.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.svm.core.libjvm; + +import org.graalvm.nativeimage.c.function.CEntryPoint; +import org.graalvm.nativeimage.c.function.CEntryPoint.Publish; +import org.graalvm.nativeimage.c.type.CCharPointer; +import org.graalvm.nativeimage.c.type.CTypeConversion; + +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.c.function.CEntryPointOptions; +import com.oracle.svm.core.hub.ClassForNameSupport; +import com.oracle.svm.core.hub.registry.ClassRegistries; +import com.oracle.svm.core.hub.registry.SymbolsSupport; +import com.oracle.svm.core.jni.JNIObjectHandles; +import com.oracle.svm.core.jni.functions.JNIFunctions.Support.JNIEnvEnterPrologue; +import com.oracle.svm.core.jni.functions.JNIFunctions.Support.ReturnNullHandle; +import com.oracle.svm.core.jni.headers.JNIEnvironment; +import com.oracle.svm.core.jni.headers.JNIObjectHandle; + +import jdk.graal.compiler.word.Word; + +final class LibJVMEntryPoints { + + /** + * This entrypoint implements + * {@code *jclass JVM_FindClassFromBootLoader(JNIEnv *env, const char *name) }. + * + * It allows for finding classes from the VM's bootstrap class loader directly, without causing + * unnecessary searching of the classpath for the required classes. It is used for shared + * library images that provide the functionality of {@code libjvm.so}. + */ + @CEntryPoint(name = "JVM_FindClassFromBootLoader", exceptionHandler = ReturnNullHandleHandler.class, publishAs = Publish.SymbolOnly, include = LibJVMMainMethodWrappers.Enabled.class) + @CEntryPointOptions(prologue = JNIEnvEnterPrologue.class, prologueBailout = ReturnNullHandle.class) + static JNIObjectHandle findClassFromBootLoader(@SuppressWarnings("unused") JNIEnvironment env, CCharPointer name) { + if (!ClassForNameSupport.respectClassLoader()) { + return Word.nullPointer(); + } + + try { + var clazzSymbol = SymbolsSupport.getTypes().fromClassGetName(CTypeConversion.toJavaString(name)); + var bootRegistry = ClassRegistries.singleton().getRegistry(null); + var clazz = bootRegistry.loadClass(clazzSymbol); + return JNIObjectHandles.createLocal(clazz); + } catch (ClassNotFoundException e) { + return JNIObjectHandles.nullHandle(); + } + } + + private static final class ReturnNullHandleHandler implements CEntryPoint.ExceptionHandler { + @Uninterruptible(reason = "exception handler") + static JNIObjectHandle handle(@SuppressWarnings("unused") Throwable t) { + return JNIObjectHandles.nullHandle(); + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/libjvm/LibJVMMainMethodWrappers.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/libjvm/LibJVMMainMethodWrappers.java new file mode 100644 index 000000000000..7556ccac3a3f --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/libjvm/LibJVMMainMethodWrappers.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.svm.core.libjvm; + +import java.lang.reflect.InvocationTargetException; +import java.util.function.BooleanSupplier; + +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +import com.oracle.svm.core.traits.BuiltinTraits.NoLayeredCallbacks; +import com.oracle.svm.core.traits.BuiltinTraits.PartiallyLayerAware; +import com.oracle.svm.core.traits.BuiltinTraits.RuntimeAccessOnly; +import com.oracle.svm.core.traits.SingletonLayeredInstallationKind.Independent; +import com.oracle.svm.core.traits.SingletonTraits; +import com.oracle.svm.core.util.VMError; + +/** + * The methods in this class are stand-ins for the JNI entrypoints of a Crema-loaded application + * main-class. This can be removed once GR-71358 is implemented. This ImageSingleton is registered + * only if LibJVMFeature is enabled. + */ +@SingletonTraits(access = RuntimeAccessOnly.class, layeredCallbacks = NoLayeredCallbacks.class, layeredInstallationKind = Independent.class, other = PartiallyLayerAware.class) +public final class LibJVMMainMethodWrappers { + + @Platforms(Platform.HOSTED_ONLY.class) + public static final class Enabled implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + return ImageSingletons.contains(LibJVMMainMethodWrappers.class); + } + } + + private volatile Class validMainClass; + + public static LibJVMMainMethodWrappers singleton() { + return ImageSingletons.lookup(LibJVMMainMethodWrappers.class); + } + + public static Class patchMethodHolderClass(Class origClazz) { + if (!ImageSingletons.contains(LibJVMMainMethodWrappers.class)) { + return origClazz; + } + + Class mainClass = singleton().validMainClass; + if (mainClass != null && origClazz == mainClass) { + return LibJVMMainMethodWrappers.class; + } + return origClazz; + } + + public void setValidMainClass(Class validMainClass) { + this.validMainClass = validMainClass; + } + + static void main(String[] args) { + Class mainClass = singleton().validMainClass; + if (mainClass == null) { + throw VMError.shouldNotReachHere("Calling main(String[] args) via JNI failed."); + } + + Throwable throwable = null; + try { + var mainMethod = mainClass.getDeclaredMethod("main", String[].class); + mainMethod.invoke(null, (Object) args); + } catch (InvocationTargetException e) { + // Return Throwable as if not invoked via reflection + Throwable cause = e.getCause(); + if (cause != null) { + throwable = cause; + } else { + throwable = exceptionToError(mainClass, ".main(String[] args)", e); + } + } catch (NoSuchMethodException | IllegalAccessException e) { + throwable = exceptionToError(mainClass, ".main(String[] args)", e); + } catch (Throwable e) { + throwable = e; + } + + if (throwable != null) { + throwable.printStackTrace(); + } + } + + static void main() { + Class mainClass = singleton().validMainClass; + if (mainClass == null) { + throw VMError.shouldNotReachHere("Calling main() via JNI failed."); + } + + Throwable throwable = null; + try { + var mainMethod = mainClass.getDeclaredMethod("main"); + mainMethod.invoke(null); + } catch (InvocationTargetException e) { + // Return Throwable as if not invoked via reflection + Throwable cause = e.getCause(); + if (cause != null) { + throwable = cause; + } else { + throwable = exceptionToError(mainClass, ".main()", e); + } + } catch (NoSuchMethodException | IllegalAccessException e) { + throwable = exceptionToError(mainClass, ".main()", e); + } catch (Throwable e) { + throwable = e; + } + + if (throwable != null) { + throwable.printStackTrace(); + } + } + + private static Throwable exceptionToError(Class mainClass, String mainMethodStr, ReflectiveOperationException cause) { + return new Error("Failed to call " + mainClass.getName() + mainMethodStr + " via reflection", cause); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/libjvm/LibJVMSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/libjvm/LibJVMSubstitutions.java new file mode 100644 index 000000000000..dd4f463e74c3 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/libjvm/LibJVMSubstitutions.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.svm.core.libjvm; + +import java.lang.reflect.Method; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; + +import jdk.internal.access.JavaLangAccess; +import jdk.internal.misc.MethodFinder; + +class LibJVMSubstitutions { + + /** + * Workaround for GR-71358. See also {@link LibJVMMainMethodWrappers} + */ + @TargetClass(value = MethodFinder.class, onlyWith = LibJVMMainMethodWrappers.Enabled.class) + static final class Target_jdk_internal_misc_MethodFinder { + + @Alias // + static JavaLangAccess JLA; + + @Substitute + public static Method findMainMethod(Class cls) { + Method mainMethod = JLA.findMethod(cls, true, "main", String[].class); + + if (mainMethod == null) { + // if not public method, try to lookup a non-public one + mainMethod = JLA.findMethod(cls, false, "main", String[].class); + } + + if (mainMethod == null || !isValidMainMethod(mainMethod)) { + mainMethod = JLA.findMethod(cls, false, "main"); + } + + if (mainMethod == null || !isValidMainMethod(mainMethod)) { + return null; + } + + LibJVMMainMethodWrappers.singleton().setValidMainClass(mainMethod.getDeclaringClass()); + return mainMethod; + } + + @Alias + static native boolean isValidMainMethod(Method mainMethodCandidate); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/proxy/DynamicProxySupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/proxy/DynamicProxySupport.java index 8eeda0d166bc..56ae07c99ca7 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/proxy/DynamicProxySupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/proxy/DynamicProxySupport.java @@ -27,6 +27,7 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.Arrays; +import java.util.function.Function; import java.util.regex.Pattern; import org.graalvm.collections.EconomicMap; @@ -35,8 +36,8 @@ import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.dynamicaccess.AccessCondition; import org.graalvm.nativeimage.hosted.RuntimeClassInitialization; -import org.graalvm.nativeimage.impl.TypeReachabilityCondition; import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; +import org.graalvm.nativeimage.impl.TypeReachabilityCondition; import com.oracle.svm.core.configure.ConditionalRuntimeValue; import com.oracle.svm.core.configure.RuntimeDynamicAccessMetadata; @@ -63,6 +64,11 @@ public class DynamicProxySupport implements DynamicProxyRegistry { public static final Pattern PROXY_CLASS_NAME_PATTERN = Pattern.compile(".*\\$Proxy[0-9]+"); + @Platforms(Platform.HOSTED_ONLY.class) // + public static DynamicProxySupport singleton() { + return (DynamicProxySupport) ImageSingletons.lookup(DynamicProxyRegistry.class); + } + static final class ProxyCacheKey { private final Class[] interfaces; @@ -103,8 +109,15 @@ public String toString() { private final EconomicMap> proxyCache = ImageHeapMap.create("proxyCache"); + @Platforms(Platform.HOSTED_ONLY.class) // + private final EconomicMap, ClassLoader> proxyClassClassloaders = EconomicMap.create(); + + @Platforms(Platform.HOSTED_ONLY.class) // + private final Function, ClassLoader> loaderAccessor; + @Platforms(Platform.HOSTED_ONLY.class) - public DynamicProxySupport() { + public DynamicProxySupport(Function, ClassLoader> loaderAccessor) { + this.loaderAccessor = loaderAccessor; } @Override @@ -128,7 +141,7 @@ public synchronized void addProxyClass(AccessCondition condition, boolean preser } @Platforms(Platform.HOSTED_ONLY.class) - private static Object createProxyClass(Class[] interfaces, boolean preserved) { + private Object createProxyClass(Class[] interfaces, boolean preserved) { try { Class clazz = createProxyClassFromImplementedInterfaces(interfaces); @@ -161,12 +174,45 @@ private static Object createProxyClass(Class[] interfaces, boolean preserved) for (Class intf : interfaces) { reflectionSupport.register(AccessCondition.unconditional(), false, preserved, intf.getMethods()); } + + /* + * When the dynamic hubs for proxy classes are generated we have to make sure they get + * the correct runtime classloader. Remember which classloader is need for DynamicHub. + * See getProxyClassClassloader below. + */ + ClassLoader proxyRuntimeLoader = getCommonClassLoaderOrFail(null, loaderAccessor, interfaces); + /* We only add entries for proxy classes where we need to adjust the loader value */ + if (proxyRuntimeLoader != clazz.getClassLoader()) { + synchronized (proxyClassClassloaders) { + proxyClassClassloaders.put(clazz, proxyRuntimeLoader); + } + } + return clazz; } catch (Throwable t) { return t; } } + @Platforms(Platform.HOSTED_ONLY.class) + public ClassLoader getProxyClassClassloader(Class clazz, Function, ClassLoader> defaultSupplier) { + /* + * Using synchronized on proxyClassClassloaders, since it is very rare that it gets written + * to in createProxyClass. + */ + synchronized (proxyClassClassloaders) { + if (proxyClassClassloaders.containsKey(clazz)) { + /* + * If this is a proxy class we generated with createProxyClass, make sure it gets + * the correct runtime-classloader (based on what runtime-classloader the interfaces + * have that it was created for). Note that `null` is a valid classloader as well. + */ + return proxyClassClassloaders.get(clazz); + } + } + return defaultSupplier.apply(clazz); + } + @Override @Platforms(Platform.HOSTED_ONLY.class) public Class getProxyClassHosted(Class... interfaces) { @@ -177,13 +223,13 @@ public Class getProxyClassHosted(Class... interfaces) { @Platforms(Platform.HOSTED_ONLY.class) @SuppressWarnings("deprecation") private static Class createProxyClassFromImplementedInterfaces(Class[] interfaces) { - return Proxy.getProxyClass(getCommonClassLoaderOrFail(null, interfaces), interfaces); + return Proxy.getProxyClass(getCommonClassLoaderOrFail(null, Class::getClassLoader, interfaces), interfaces); } - private static ClassLoader getCommonClassLoaderOrFail(ClassLoader loader, Class... intfs) { + private static ClassLoader getCommonClassLoaderOrFail(ClassLoader loader, Function, ClassLoader> loaderAccessor, Class... intfs) { ClassLoader commonLoader = null; for (Class intf : intfs) { - ClassLoader intfLoader = intf.getClassLoader(); + ClassLoader intfLoader = loaderAccessor.apply(intf); if (ClassUtil.isSameOrParentLoader(commonLoader, intfLoader)) { commonLoader = intfLoader; } else if (!ClassUtil.isSameOrParentLoader(intfLoader, commonLoader)) { @@ -218,7 +264,7 @@ public Class getProxyClass(ClassLoader loader, Class... interfaces) { * common. This prevents that later we would be unable to return the proxy class if we * are passed a parent loader of the initially specified loader. */ - ClassLoader commonLoader = getCommonClassLoaderOrFail(loader, interfaces); + ClassLoader commonLoader = getCommonClassLoaderOrFail(loader, Class::getClassLoader, interfaces); if (!ClassUtil.isSameOrParentLoader(commonLoader, loader)) { throw incompatibleClassLoaders(loader, interfaces); } 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 457ae9337290..0b7769607922 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 @@ -502,13 +502,6 @@ public Path getBuilderInspectServerPath() { return null; } - /** - * @return base image classpath needed for every image (e.g. LIBRARY_SUPPORT) - */ - public List getImageProvidedClasspath() { - return getImageProvidedJars(); - } - /** * @return base image module-path needed for every image (e.g. LIBRARY_SUPPORT) */ @@ -1420,7 +1413,9 @@ private int completeImageBuild() { LinkedHashSet finalImageClasspath = new LinkedHashSet<>(imageClasspath); LinkedHashSet finalImageProvidedJars = new LinkedHashSet<>(this.imageProvidedJars); - finalImageProvidedJars.addAll(config.getImageProvidedModulePath()); + if (!finalImageModulePath.isEmpty() || !finalImageClasspath.isEmpty()) { + finalImageProvidedJars.addAll(config.getImageProvidedModulePath()); + } finalImageModulePath.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())); @@ -1921,13 +1916,11 @@ private static Map getModulesFromPath(Collection modulePath) } private Set getImplicitlyRequiredSystemModules(Collection modulePath) { - if (modulePath.isEmpty()) { - return Set.of(); + ModuleFinder finder = ModuleFinder.ofSystem(); + if (!modulePath.isEmpty()) { + ModuleFinder appModuleFinder = ModuleFinder.of(modulePath.toArray(Path[]::new)); + finder = ModuleFinder.compose(appModuleFinder, finder); } - - ModuleFinder systemModuleFinder = ModuleFinder.ofSystem(); - ModuleFinder appModuleFinder = ModuleFinder.of(modulePath.toArray(Path[]::new)); - ModuleFinder finder = ModuleFinder.compose(appModuleFinder, systemModuleFinder); Map modules = finder.findAll().stream() .collect(Collectors.toMap(m -> m.descriptor().name(), m -> m)); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderFeature.java index b12adcb8b78a..9a85ef4b51d0 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderFeature.java @@ -55,22 +55,19 @@ public class ClassLoaderFeature implements InternalFeature { private static final NativeImageSystemClassLoader nativeImageSystemClassLoader = NativeImageSystemClassLoader.singleton(); - private static final ClassLoader bootClassLoader; - private static final ClassLoader platformClassLoader; + /** + * Field {@link NativeImageSystemClassLoader#defaultSystemClassLoader} contains the original + * {@code jdk.internal.loader.ClassLoaders.AppClassLoader} of the VM that runs the builder. This + * is what we want in the image. + */ + private static final ClassLoader appClassLoader = nativeImageSystemClassLoader.defaultSystemClassLoader; - static { - if (ImageLayerBuildingSupport.buildingImageLayer()) { - platformClassLoader = ClassLoaders.platformClassLoader(); - bootClassLoader = BootLoaderSupport.getBootLoader(); - } else { - platformClassLoader = null; - bootClassLoader = null; - } - } + private static final ClassLoader platformClassLoader = ClassLoaders.platformClassLoader(); + private static final ClassLoader bootClassLoader = BootLoaderSupport.getBootLoader(); public static ClassLoader getRuntimeClassLoader(ClassLoader original) { if (replaceWithAppClassLoader(original)) { - return nativeImageSystemClassLoader.defaultSystemClassLoader; + return appClassLoader; } return original; @@ -95,7 +92,7 @@ private Object runtimeClassLoaderObjectReplacer(Object replaceCandidate) { JavaConstant replaceClassLoadersWithLayerConstant(CrossLayerConstantRegistry registry, Object object) { if (object instanceof ClassLoader loader) { - if (replaceWithAppClassLoader(loader) || loader == nativeImageSystemClassLoader.defaultSystemClassLoader) { + if (replaceWithAppClassLoader(loader) || loader == appClassLoader) { return registry.getConstant(APP_KEY_NAME); } else if (loader == platformClassLoader) { return registry.getConstant(PLATFORM_KEY_NAME); @@ -116,7 +113,7 @@ public void duringSetup(DuringSetupAccess access) { var packageManager = HostedClassLoaderPackageManagement.singleton(); var registry = CrossLayerConstantRegistry.singletonOrNull(); if (ImageLayerBuildingSupport.buildingImageLayer()) { - packageManager.initialize(nativeImageSystemClassLoader.defaultSystemClassLoader, registry); + packageManager.initialize(appClassLoader, registry); } var config = (FeatureImpl.DuringSetupAccessImpl) access; @@ -158,7 +155,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { * fieldValueInterceptors will be computed for the scanned objects. */ var registry = CrossLayerConstantRegistry.singletonOrNull(); - registry.registerHeapConstant(APP_KEY_NAME, nativeImageSystemClassLoader.defaultSystemClassLoader); + registry.registerHeapConstant(APP_KEY_NAME, appClassLoader); registry.registerHeapConstant(PLATFORM_KEY_NAME, platformClassLoader); registry.registerHeapConstant(BOOT_KEY_NAME, bootClassLoader); registry.registerFutureHeapConstant(APP_PACKAGE_KEY_NAME, config.getMetaAccess().lookupJavaType(ConcurrentHashMap.class)); @@ -214,7 +211,7 @@ static final class InitialLayerPackageMapTransformer extends PackageMapTransform @Override public Object transform(Object receiver, Object originalValue) { - if (receiver == nativeImageSystemClassLoader.defaultSystemClassLoader) { + if (receiver == appClassLoader) { /* * This map will be assigned within the application layer. Within this layer we * register a relocatable constant. diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java index f4cac01a08b4..95f6f12208ca 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java @@ -204,6 +204,29 @@ private static ResolvedJavaType getEnclosingTypeOrNull(ResolvedJavaType javaType } } + public ClassLoader getDynamicHubClassLoader(Class clazz) { + if (isCoreType(clazz)) { + /* + * Use null-loader for VM implementation classes. Our own VM implementation code (e.g. + * com.oracle.svm.core classes) are unrelated to the application code of the image and + * should not share the same classloader at image run-time. Using null as the + * classloader for such classes is in line with other use of the null-loader in Java. + */ + return null; + } else { + /* + * If the class is an application class then it was loaded by NativeImageClassLoader. + * The ClassLoaderFeature object replacer will unwrap the original AppClassLoader from + * the NativeImageClassLoader. + */ + return clazz.getClassLoader(); + } + } + + public boolean isCoreType(Class clazz) { + return getBuilderModules().contains(clazz.getModule()); + } + /** * Type of result returned by {@link ImageClassLoader#isPlatformSupported}. */ diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java index d9b93b3731b1..93a38d1247de 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java @@ -321,6 +321,7 @@ import jdk.graal.compiler.replacements.TargetGraphBuilderPlugins; import jdk.graal.compiler.word.WordOperationPlugin; import jdk.graal.compiler.word.WordTypes; +import jdk.internal.loader.ClassLoaders; import jdk.vm.ci.aarch64.AArch64; import jdk.vm.ci.amd64.AMD64; import jdk.vm.ci.code.CodeCacheProvider; @@ -1302,6 +1303,10 @@ private static void registerRootElements(Inflation bb) { bb.addRootClass(CFunctionPointer[].class, false, false).registerAsInstantiated(rootClassReason); bb.addRootClass(PointerBase[].class, false, false).registerAsInstantiated(rootClassReason); + bb.addRootClass(ClassLoaders.appClassLoader().getClass(), false, false).registerAsInstantiated(rootClassReason); + bb.addRootClass(ClassLoaders.platformClassLoader().getClass(), false, false).registerAsInstantiated(rootClassReason); + bb.addRootClass(BootLoaderSupport.getBootLoader().getClass(), false, false).registerAsInstantiated(rootClassReason); + /* MethodRef can conceal use of MethodPointer and MethodOffset until after analysis. */ bb.addRootClass(MethodPointer.class, false, true); if (SubstrateOptions.useRelativeCodePointers()) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageSystemClassLoader.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageSystemClassLoader.java index 0177fb6a2b9b..423bbc9bafd6 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageSystemClassLoader.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageSystemClassLoader.java @@ -91,31 +91,17 @@ public void setNativeImageClassLoader(ClassLoader nativeImageClassLoader) { this.nativeImageClassLoader = nativeImageClassLoader; } - /** - * Checks class loaders match. {@code start} is searched up to the system class loader. - */ - private boolean matchesClassLoaderOrParent(ClassLoader start, ClassLoader candidate) { - ClassLoader current = start; - do { - if (current == candidate) { - return true; - } - current = current.getParent(); - } while (current != defaultSystemClassLoader); - return false; - } - public boolean isNativeImageClassLoader(ClassLoader c) { - ClassLoader loader = nativeImageClassLoader; - if (loader == null) { + ClassLoader currentNativeImageClassLoader = nativeImageClassLoader; + if (currentNativeImageClassLoader == null) { return false; } - return matchesClassLoaderOrParent(nativeImageClassLoader, c); + return currentNativeImageClassLoader == c; } public boolean isDisallowedClassLoader(ClassLoader c) { for (ClassLoader disallowedClassLoader : disallowedClassLoaders) { - if (matchesClassLoaderOrParent(disallowedClassLoader, c)) { + if (disallowedClassLoader == c) { return true; } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java index bb93e8f34d53..6783152c7ccd 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java @@ -38,6 +38,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -107,6 +108,7 @@ import com.oracle.svm.core.jdk.LambdaFormHiddenMethod; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.option.SubstrateOptionsParser; +import com.oracle.svm.core.reflect.proxy.DynamicProxySupport; import com.oracle.svm.core.thread.ContinuationSupport; import com.oracle.svm.core.threadlocal.VMThreadLocalInfo; import com.oracle.svm.core.util.Counter; @@ -593,12 +595,7 @@ private DynamicHub createHub(AnalysisType type) { } int modifiers = javaClass.getModifiers(); - /* - * If the class is an application class then it was loaded by NativeImageClassLoader. The - * ClassLoaderFeature object replacer will unwrap the original AppClassLoader from the - * NativeImageClassLoader. - */ - ClassLoader hubClassLoader = javaClass.getClassLoader(); + ClassLoader hubClassLoader = DynamicProxySupport.singleton().getProxyClassClassloader(javaClass, loader::getDynamicHubClassLoader); /* Class names must be interned strings according to the Java specification. */ String name = encoder.encodeClass(type.toClassName()); @@ -1560,4 +1557,14 @@ public ConstantExpressionRegistry getConstantExpressionRegistry() { public Set getSharedLayerForbiddenModules() { return sharedLayerForbiddenModules; } + + @Override + public String loaderName(AnalysisType type) { + var originalLoader = type.getJavaClass().getClassLoader(); + var runtimeLoader = typeToHub.get(type).getClassLoader(); + if (Objects.equals(originalLoader, runtimeLoader)) { + return loaderName(originalLoader); + } + return loaderName(originalLoader) + "->" + loaderName(runtimeLoader); + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index a6ede3629b70..4e976cdfd1a5 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -75,6 +75,7 @@ import com.oracle.svm.core.meta.SharedMethod; import com.oracle.svm.core.meta.SharedType; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.SVMHost; import com.oracle.svm.hosted.c.NativeLibraries; import com.oracle.svm.hosted.c.info.AccessorInfo; import com.oracle.svm.hosted.c.info.ElementInfo; @@ -703,6 +704,9 @@ protected TypeEntry createTypeEntry(SharedType type) { * image singleton. */ TypeEntry foreignTypeEntry = SubstrateDebugTypeEntrySupport.singleton().getTypeEntry(typeSignature); + if (foreignTypeEntry == null) { + throw VMError.shouldNotReachHere("Missing TypeEntry for '" + typeName + "' from loader '" + loaderName + "' in SubstrateDebugTypeEntrySupport"); + } // update class offset if the class object is in the heap foreignTypeEntry.setClassOffset(classOffset); @@ -755,7 +759,8 @@ public static TypeEntry processElementInfo(NativeLibraries nativeLibs, AnalysisM int size = elementSize(elementInfo); // We need the loader name here to match the type signature generated later for looking up // type entries. - String loaderName = UniqueShortNameProvider.singleton().uniqueShortLoaderName(type.getJavaClass().getClassLoader()); + var runtimeLoader = ((SVMHost) type.getUniverse().hostVM()).dynamicHub(type).getClassLoader(); + String loaderName = UniqueShortNameProvider.singleton().uniqueShortLoaderName(runtimeLoader); long typeSignature = getTypeSignature(typeName + loaderName); // Reuse already created type entries. diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/InitialLayerFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/InitialLayerFeature.java index 96bbb87742ce..aaf23ccbb0ea 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/InitialLayerFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/InitialLayerFeature.java @@ -28,6 +28,7 @@ import java.lang.reflect.Proxy; +import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.nativeimage.hosted.Feature; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; @@ -73,6 +74,7 @@ public void duringSetup(DuringSetupAccess a) { metaAccess.lookupJavaType(Proxy.getProxyClass(ClassLoaders.appClassLoader(), Uninterruptible.class)).registerAsInstantiated("Core type"); metaAccess.lookupJavaType(BootstrapMethodInfo.class).registerAsInstantiated("Core type"); metaAccess.lookupJavaType(BootstrapMethodInfo.ExceptionWrapper.class).registerAsInstantiated("Core type"); + metaAccess.lookupJavaType(UnmanagedMemory.class).registerAsReachable("Core type"); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JNIRegistrationSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JNIRegistrationSupport.java index 0d8629240810..b20f56864374 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JNIRegistrationSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JNIRegistrationSupport.java @@ -76,6 +76,7 @@ import com.oracle.svm.hosted.c.NativeLibraries; import com.oracle.svm.hosted.c.codegen.CCompilerInvoker; import com.oracle.svm.hosted.c.util.FileUtils; +import com.oracle.svm.hosted.image.AbstractImage.NativeImageKind; import com.oracle.svm.hosted.imagelayer.CapnProtoAdapters; import com.oracle.svm.hosted.imagelayer.SVMImageLayerSingletonLoader; import com.oracle.svm.hosted.imagelayer.SVMImageLayerWriter; @@ -325,6 +326,15 @@ private void makeShimLibrary(String shimName) { List linkerCommand; Path image = accessImpl.getImagePath(); Path shimLibrary = image.resolveSibling(System.mapLibraryName(shimName)); + if (accessImpl.getImageKind() == NativeImageKind.SHARED_LIBRARY && shimLibrary.equals(image)) { + /* + * A shared library image gets built with the same name this shim-library would have. + * This is an advanced use-case, and we assume the user knows what they are doing. Thus, + * we will suppress producing a shim library in this case. + */ + return; + } + if (isWindows()) { /* Dependencies are the native image (so we can re-export from it) and C Runtime. */ linkerCommand = ImageSingletons.lookup(CCompilerInvoker.class) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/libjvm/LibJVMFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/libjvm/LibJVMFeature.java new file mode 100644 index 000000000000..a5e8f13e2c9a --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/libjvm/LibJVMFeature.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.svm.hosted.libjvm; + +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.hosted.Feature; +import org.graalvm.nativeimage.hosted.RuntimeResourceAccess; + +import com.oracle.svm.core.jdk.JNIRegistrationUtil; +import com.oracle.svm.core.libjvm.LibJVMMainMethodWrappers; +import com.oracle.svm.util.dynamicaccess.JVMCIRuntimeJNIAccess; + +final class LibJVMFeature extends JNIRegistrationUtil implements Feature { + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + + JVMCIRuntimeJNIAccess.register(method(access, "java.lang.VersionProps", "print", boolean.class)); + + RuntimeResourceAccess.addResourceBundle(Object.class.getModule(), "sun.launcher.resources.launcher"); + + String launcherHelper = "sun.launcher.LauncherHelper"; + JVMCIRuntimeJNIAccess.register(method(access, launcherHelper, "checkAndLoadMain", boolean.class, int.class, String.class)); + JVMCIRuntimeJNIAccess.register(method(access, launcherHelper, "makePlatformString", boolean.class, byte[].class)); + JVMCIRuntimeJNIAccess.register(method(access, launcherHelper, "getApplicationClass")); + JVMCIRuntimeJNIAccess.register(fields(access, launcherHelper, "isStaticMain", "noArgMain")); + + JVMCIRuntimeJNIAccess.register(method(access, launcherHelper, "listModules")); + + // Workaround for GR-71358 + ImageSingletons.add(LibJVMMainMethodWrappers.class, new LibJVMMainMethodWrappers()); + var libJVMMainMethodWrappersName = LibJVMMainMethodWrappers.class.getName(); + JVMCIRuntimeJNIAccess.register(method(access, libJVMMainMethodWrappersName, "main", String[].class)); + JVMCIRuntimeJNIAccess.register(method(access, libJVMMainMethodWrappersName, "main")); + + // This is needed so that the jdk.internal.loader.ClassLoaders fields get set via + // ArchivedClassLoaders and adjusted with BuiltinClassLoader.setClassPath(URLClassPath) + initializeAtRunTime(access, "jdk.internal.loader.ClassLoaders"); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/DynamicProxyFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/DynamicProxyFeature.java index d260de2b2d7b..54c0ef5d37cf 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/DynamicProxyFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/DynamicProxyFeature.java @@ -59,7 +59,7 @@ public final class DynamicProxyFeature implements InternalFeature { public void afterRegistration(AfterRegistrationAccess a) { FeatureImpl.AfterRegistrationAccessImpl access = (FeatureImpl.AfterRegistrationAccessImpl) a; ImageClassLoader imageClassLoader = access.getImageClassLoader(); - DynamicProxySupport dynamicProxySupport = new DynamicProxySupport(); + DynamicProxySupport dynamicProxySupport = new DynamicProxySupport(imageClassLoader::getDynamicHubClassLoader); ImageSingletons.add(DynamicProxyRegistry.class, dynamicProxySupport); ImageSingletons.add(RuntimeProxyCreationSupport.class, dynamicProxySupport); proxyRegistry = new ProxyRegistry(dynamicProxySupport, imageClassLoader); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/ReflectionPlugins.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/ReflectionPlugins.java index 3d13ed4553a5..987b525680cf 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/ReflectionPlugins.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/ReflectionPlugins.java @@ -50,7 +50,6 @@ import org.graalvm.nativeimage.hosted.RuntimeReflection; import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport; -import com.oracle.svm.util.OriginalClassProvider; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.svm.core.MissingRegistrationUtils; import com.oracle.svm.core.ParsingReason; @@ -65,12 +64,14 @@ import com.oracle.svm.hosted.ImageClassLoader; import com.oracle.svm.hosted.NativeImageSystemClassLoader; import com.oracle.svm.hosted.ReachabilityCallbackNode; +import com.oracle.svm.hosted.SVMHost; import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; import com.oracle.svm.hosted.dynamicaccessinference.DynamicAccessInferenceLog; import com.oracle.svm.hosted.dynamicaccessinference.StrictDynamicAccessInferenceFeature; import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor; import com.oracle.svm.hosted.substitute.SubstitutionReflectivityFilter; import com.oracle.svm.util.ModuleSupport; +import com.oracle.svm.util.OriginalClassProvider; import com.oracle.svm.util.ReflectionUtil; import com.oracle.svm.util.TypeResult; @@ -483,8 +484,19 @@ private boolean processClassGetClassLoader(GraphBuilderContext b, ResolvedJavaMe return false; } - // GR-57649 generalize code if needed in more places - ClassLoader loader = clazz.getClassLoader(); + ClassLoader loader; + if (reason == ParsingReason.AutomaticUnsafeTransformation || reason == ParsingReason.EarlyClassInitializerAnalysis) { + /* + * We are getting called before analysis, DynamicHubs are not available at this point. + * This is acceptable because those graphs will not be used by the analysis later. + */ + // GR-57649 generalize code if needed in more places + loader = clazz.getClassLoader(); + } else { + /* Get loader from DynamicHub. The one from the hosted clazz can be different. */ + loader = ((SVMHost) aUniverse.hostVM()).dynamicHub(clazz).getClassLoader(); + } + JavaConstant result; if (loader == null) { result = JavaConstant.NULL_POINTER; diff --git a/substratevm/src/com.oracle.svm.native.jvm.posix/src/JvmFuncs.c b/substratevm/src/com.oracle.svm.native.jvm.posix/src/JvmFuncs.c index bc84a9f0f5eb..4b8c63aa65e1 100644 --- a/substratevm/src/com.oracle.svm.native.jvm.posix/src/JvmFuncs.c +++ b/substratevm/src/com.oracle.svm.native.jvm.posix/src/JvmFuncs.c @@ -79,6 +79,13 @@ JNIEXPORT int JNICALL JVM_GetInterfaceVersion() { return JVM_INTERFACE_VERSION; } +/* Declare JVM_FindClassFromBootLoader as weak symbol and provide fallback implementation */ +JNIEXPORT void JNICALL JVM_FindClassFromBootLoader(JNIEnv *env, char *fqn) __attribute__((weak)); + +void JVM_FindClassFromBootLoader(JNIEnv *env, char *fqn) { + (*env)->FatalError(env, "JVM_FindClassFromBootLoader called: Unimplemented"); +} + #ifdef __linux__ /* Support for cpusets on Linux (JDK-6515172). diff --git a/substratevm/src/com.oracle.svm.native.jvm.windows/src/JvmFuncs.c b/substratevm/src/com.oracle.svm.native.jvm.windows/src/JvmFuncs.c index 0ff8be4d0840..87319d8d507d 100644 --- a/substratevm/src/com.oracle.svm.native.jvm.windows/src/JvmFuncs.c +++ b/substratevm/src/com.oracle.svm.native.jvm.windows/src/JvmFuncs.c @@ -50,6 +50,21 @@ JNIEXPORT int JNICALL JVM_GetInterfaceVersion() { return JVM_INTERFACE_VERSION; } +/* Declaration only. Implemented via JVM_FindClassFromBootLoader_default */ +JNIEXPORT void JNICALL JVM_FindClassFromBootLoader(JNIEnv *env, char *fqn); + +#if defined(_MSC_VER) + #if defined(_M_IX86) + #pragma comment(linker, "/alternatename:_JVM_FindClassFromBootLoader=_JVM_FindClassFromBootLoader_default") + #elif defined(_M_X64) || defined(_M_ARM64) + #pragma comment(linker, "/alternatename:JVM_FindClassFromBootLoader=JVM_FindClassFromBootLoader_default") + #endif +#endif + +JNIEXPORT void JNICALL JVM_FindClassFromBootLoader_default(JNIEnv *env, char *fqn) { + (*env)->FatalError(env, "JVM_FindClassFromBootLoader called: Unimplemented"); +} + jlong as_long(LARGE_INTEGER x) { return jlong_from(x.HighPart, x.LowPart); } diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/SeparatedClassLoadersTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/SeparatedClassLoadersTest.java index ab2535425704..453fb050b887 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/SeparatedClassLoadersTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/SeparatedClassLoadersTest.java @@ -49,13 +49,6 @@ import java.net.URLClassLoader; import java.security.ProtectionDomain; -import com.oracle.truffle.api.TruffleLanguage.Registration; -import com.oracle.truffle.api.TruffleLogger; -import com.oracle.truffle.api.nodes.RootNode; -import com.oracle.truffle.api.test.ReflectionUtils; -import com.oracle.truffle.api.test.SubprocessTestUtils; -import com.oracle.truffle.api.test.common.AbstractExecutableTestLanguage; -import com.oracle.truffle.api.test.common.TestUtils; import org.graalvm.collections.EconomicMap; import org.graalvm.nativeimage.ImageInfo; import org.graalvm.polyglot.Engine; @@ -67,6 +60,13 @@ import org.junit.Test; import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.TruffleLanguage.Registration; +import com.oracle.truffle.api.TruffleLogger; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.test.ReflectionUtils; +import com.oracle.truffle.api.test.SubprocessTestUtils; +import com.oracle.truffle.api.test.common.AbstractExecutableTestLanguage; +import com.oracle.truffle.api.test.common.TestUtils; import com.oracle.truffle.tck.tests.TruffleTestAssumptions; public class SeparatedClassLoadersTest { @@ -85,6 +85,9 @@ public void storeLoader() { @Test public void sdkAndTruffleAPIInSeparateClassLoaders() { + /* This test is specific to HotSpot. */ + Assume.assumeFalse(ImageInfo.inImageCode()); + ClassLoaders classLoaders = createContextClassLoaders(); Object contextClassLoaderEngine = createEngineInContextClassLoader(classLoaders); try {