diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/CGlobalDataPointerSingleton.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/CGlobalDataPointerSingleton.java new file mode 100644 index 000000000000..d024d64bf8e8 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/CGlobalDataPointerSingleton.java @@ -0,0 +1,77 @@ +/* + * 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; + +import java.util.EnumSet; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +import com.oracle.svm.core.c.BoxedRelocatedPointer; +import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; +import com.oracle.svm.core.graal.code.CGlobalDataBasePointer; +import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter; +import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags; +import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonSupport; +import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; + +/** + * This singleton contains the {@link CGlobalDataBasePointer} of the current layer. In layered + * images, there is one {@link com.oracle.svm.core.c.CGlobalData} memory space for each layer, so + * when reading a {@link com.oracle.svm.core.c.CGlobalData}, the corresponding base needs to be + * used. + */ +@AutomaticallyRegisteredImageSingleton +public class CGlobalDataPointerSingleton implements MultiLayeredImageSingleton { + + /** + * Image heap object storing the base address of CGlobalData memory using a relocation. Before + * the image heap is set up, CGlobalData must be accessed via relocations in the code instead. + */ + private final BoxedRelocatedPointer cGlobalDataRuntimeBaseAddress = new BoxedRelocatedPointer(CGlobalDataBasePointer.INSTANCE); + + @Platforms(Platform.HOSTED_ONLY.class) + public static CGlobalDataPointerSingleton currentLayer() { + return LayeredImageSingletonSupport.singleton().lookup(CGlobalDataPointerSingleton.class, false, true); + } + + public static CGlobalDataPointerSingleton[] allLayers() { + return MultiLayeredImageSingleton.getAllLayers(CGlobalDataPointerSingleton.class); + } + + public BoxedRelocatedPointer getRuntimeBaseAddress() { + return cGlobalDataRuntimeBaseAddress; + } + + @Override + public EnumSet getImageBuilderFlags() { + return LayeredImageSingletonBuilderFlags.ALL_ACCESS; + } + + @Override + public PersistFlags preparePersist(ImageSingletonWriter writer) { + return PersistFlags.NOTHING; + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateDiagnostics.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateDiagnostics.java index 70933ac4aad5..b71a310d319e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateDiagnostics.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateDiagnostics.java @@ -50,6 +50,7 @@ import com.oracle.svm.core.SubstrateDiagnostics.DumpDeoptStubPointer; import com.oracle.svm.core.SubstrateDiagnostics.DumpRecentDeoptimizations; import com.oracle.svm.core.SubstrateDiagnostics.DumpRuntimeCodeInfoMemory; +import com.oracle.svm.core.c.BoxedRelocatedPointer; import com.oracle.svm.core.c.NonmovableArrays; import com.oracle.svm.core.code.CodeInfo; import com.oracle.svm.core.code.CodeInfoAccess; @@ -67,7 +68,6 @@ import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.graal.RuntimeCompilation; -import com.oracle.svm.core.graal.code.CGlobalDataInfo; import com.oracle.svm.core.graal.stackvalue.UnsafeStackValue; import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.heap.RestrictHeapAccess; @@ -912,7 +912,15 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev if (SubstrateOptions.useRelativeCodePointers()) { log.string("Code base: ").zhex(KnownIntrinsics.codeBase()).newline(); } - log.string("CGlobalData base: ").zhex(CGlobalDataInfo.CGLOBALDATA_RUNTIME_BASE_ADDRESS.getPointer()).newline(); + CGlobalDataPointerSingleton[] layeredSingletons = CGlobalDataPointerSingleton.allLayers(); + if (ImageLayerBuildingSupport.buildingImageLayer()) { + for (int i = 0; i < layeredSingletons.length; ++i) { + log.string("CGlobalData base for layer ").unsigned(i).string(": ").zhex(layeredSingletons[i].getRuntimeBaseAddress().getPointer()).newline(); + } + } else { + BoxedRelocatedPointer baseAddress = layeredSingletons[0].getRuntimeBaseAddress(); + log.string("CGlobalData base: ").zhex(baseAddress.getPointer()).newline(); + } log.string("Containerized: ").bool(Container.singleton().isContainerized()).newline(); if (Container.singleton().isContainerized()) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/code/CGlobalDataInfo.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/code/CGlobalDataInfo.java index 6eedbd5bf7ac..1b0b04099dd8 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/code/CGlobalDataInfo.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/code/CGlobalDataInfo.java @@ -32,7 +32,6 @@ import com.oracle.svm.core.BuildPhaseProvider.AfterHeapLayout; import com.oracle.svm.core.BuildPhaseProvider.AfterHostedUniverse; -import com.oracle.svm.core.c.BoxedRelocatedPointer; import com.oracle.svm.core.c.CGlobalDataImpl; import com.oracle.svm.core.heap.UnknownPrimitiveField; import com.oracle.svm.core.util.VMError; @@ -43,12 +42,6 @@ * {@link CGlobalDataImpl} within static fields. */ public final class CGlobalDataInfo { - /** - * Image heap object storing the base address of CGlobalData memory using a relocation. Before - * the image heap is set up, CGlobalData must be accessed via relocations in the code instead. - */ - public static final BoxedRelocatedPointer CGLOBALDATA_RUNTIME_BASE_ADDRESS = new BoxedRelocatedPointer(CGlobalDataBasePointer.INSTANCE); - private final CGlobalDataImpl data; private final boolean isSymbolReference; @@ -68,13 +61,16 @@ public final class CGlobalDataInfo { /** Cache until writing the image in case the {@link Supplier} is costly or has side-effects. */ @Platforms(HOSTED_ONLY.class) private byte[] bytes; + private final int layerNum; + @Platforms(Platform.HOSTED_ONLY.class) - public CGlobalDataInfo(CGlobalDataImpl data, boolean definedAsGlobalInPriorLayer) { + public CGlobalDataInfo(CGlobalDataImpl data, boolean definedAsGlobalInPriorLayer, int layerNum) { assert data != null; this.data = data; this.isSymbolReference = data.isSymbolReference(); assert !this.isSymbolReference || data.symbolName != null; this.definedAsGlobalInPriorLayer = definedAsGlobalInPriorLayer; + this.layerNum = layerNum; } public CGlobalDataImpl getData() { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/LayeredImageHeapMap.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/LayeredImageHeapMap.java index 6c52806af764..c9f00036d0e1 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/LayeredImageHeapMap.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/LayeredImageHeapMap.java @@ -193,10 +193,12 @@ public void remove() { public boolean advance() { boolean advance = current.advance(); if (!advance) { - if (cursors.hasNext()) { + while (cursors.hasNext()) { current = cursors.next(); - if (keys.add(current.getKey())) { - return true; + while (current.advance()) { + if (keys.add(current.getKey())) { + return true; + } } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/CGlobalDataFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/CGlobalDataFeature.java index a11d134779f3..fbe4cab52aec 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/CGlobalDataFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/CGlobalDataFeature.java @@ -40,6 +40,7 @@ import org.graalvm.nativeimage.ImageSingletons; import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.svm.core.CGlobalDataPointerSingleton; import com.oracle.svm.core.ParsingReason; import com.oracle.svm.core.c.BoxedRelocatedPointer; import com.oracle.svm.core.c.CGlobalData; @@ -50,14 +51,17 @@ import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.graal.code.CGlobalDataInfo; import com.oracle.svm.core.graal.nodes.CGlobalDataLoadAddressNode; +import com.oracle.svm.core.imagelayer.DynamicImageLayerInfo; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; import com.oracle.svm.core.traits.BuiltinTraits.BuildtimeAccessOnly; import com.oracle.svm.core.traits.BuiltinTraits.NoLayeredCallbacks; import com.oracle.svm.core.traits.SingletonLayeredInstallationKind.Independent; import com.oracle.svm.core.traits.SingletonTraits; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.FeatureImpl; import com.oracle.svm.hosted.image.RelocatableBuffer; import com.oracle.svm.hosted.imagelayer.CodeLocation; +import com.oracle.svm.hosted.imagelayer.LoadImageSingletonFeature; import com.oracle.svm.hosted.meta.HostedSnippetReflectionProvider; import com.oracle.svm.util.ReflectionUtil; @@ -90,10 +94,12 @@ import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugin.RequiredInvocationPlugin; import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration; import jdk.graal.compiler.nodes.java.LoadFieldNode; +import jdk.graal.compiler.nodes.java.LoadIndexedNode; import jdk.graal.compiler.nodes.memory.ReadNode; import jdk.graal.compiler.nodes.memory.address.OffsetAddressNode; import jdk.graal.compiler.phases.util.Providers; import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; @@ -103,6 +109,8 @@ public class CGlobalDataFeature implements InternalFeature { private final Method getCGlobalDataInfoMethod = ReflectionUtil.lookupMethod(CGlobalDataNonConstantRegistry.class, "getCGlobalDataInfo", CGlobalDataImpl.class); + private final Field layerNumField = ReflectionUtil.lookupField(CGlobalDataInfo.class, "layerNum"); + private final Field cGlobalDataRuntimeBaseAddressField = ReflectionUtil.lookupField(CGlobalDataPointerSingleton.class, "cGlobalDataRuntimeBaseAddress"); private final Field offsetField = ReflectionUtil.lookupField(CGlobalDataInfo.class, "offset"); private final Field isSymbolReferenceField = ReflectionUtil.lookupField(CGlobalDataInfo.class, "isSymbolReference"); private final Field baseHolderPointerField = ReflectionUtil.lookupField(BoxedRelocatedPointer.class, "pointer"); @@ -132,6 +140,17 @@ public void duringSetup(DuringSetupAccess a) { a.registerObjectReplacer(this::replaceObject); } + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + if (ImageLayerBuildingSupport.buildingImageLayer()) { + /* + * The non-constant registry needs to be rescanned in layered images to make sure it is + * always reachable in every layer and that all the data is properly tracked. + */ + ((FeatureImpl.BeforeAnalysisAccessImpl) access).rescanObject(nonConstantRegistry); + } + } + @Override public void afterHeapLayout(AfterHeapLayoutAccess access) { layout(); @@ -180,8 +199,39 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec } } - JavaConstant baseHolderConstant = providers.getSnippetReflection().forObject(CGlobalDataInfo.CGLOBALDATA_RUNTIME_BASE_ADDRESS); - ConstantNode baseHolder = ConstantNode.forConstant(baseHolderConstant, b.getMetaAccess(), b.getGraph()); + ValueNode baseHolder; + if (ImageLayerBuildingSupport.buildingImageLayer()) { + /* + * When building layered images, each layer has its own CGlobalData base + * pointer, meaning the one associated with this specific CGlobalDataInfo + * needs to be used. + */ + JavaConstant cGlobalDataPointerSingletonClass = providers.getSnippetReflection().forObject(CGlobalDataPointerSingleton.class); + ConstantNode classConstant = ConstantNode.forConstant(cGlobalDataPointerSingletonClass, b.getMetaAccess(), b.getGraph()); + + /* Load the array containing all the singletons. */ + ValueNode layers = b.add(ImageSingletons.lookup(LoadImageSingletonFeature.class).loadMultiLayeredImageSingleton(b, classConstant)); + + /* + * Get the layer number of the CGlobalDataInfo to get the index to use in + * the singleton array. + */ + ValueNode layerNum = b.add(LoadFieldNode.create(b.getAssumptions(), info, b.getMetaAccess().lookupJavaField(layerNumField))); + + /* Use the layer number to get the corresponding singleton. */ + ValueNode singleton = b.add(LoadIndexedNode.create(b.getAssumptions(), layers, layerNum, null, JavaKind.Object, b.getMetaAccess(), b.getConstantReflection())); + + /* Get the CGlobalData base pointer from the singleton. */ + baseHolder = b.add(LoadFieldNode.create(b.getAssumptions(), singleton, b.getMetaAccess().lookupJavaField(cGlobalDataRuntimeBaseAddressField))); + } else { + /* + * In standalone image, there is only one CGlobalData base pointer, so there + * is no need to have a custom access. + */ + JavaConstant baseHolderConstant = providers.getSnippetReflection().forObject(CGlobalDataPointerSingleton.currentLayer().getRuntimeBaseAddress()); + baseHolder = ConstantNode.forConstant(baseHolderConstant, b.getMetaAccess(), b.getGraph()); + } + ResolvedJavaField holderPointerField = providers.getMetaAccess().lookupJavaField(baseHolderPointerField); StampPair pointerStamp = StampPair.createSingle(providers.getWordTypes().getWordStamp((ResolvedJavaType) holderPointerField.getType())); LoadFieldNode baseAddress = b.add(LoadFieldNode.createOverrideStamp(pointerStamp, baseHolder, holderPointerField)); @@ -270,7 +320,7 @@ CGlobalDataInfo createCGlobalDataInfo(CGlobalDataImpl data, boolean definedAs "We currently do not allow CGlobalData code locations to be in a hidden class. Please adapt the code accordingly. Location: %s", data.codeLocation); } - CGlobalDataInfo cGlobalDataInfo = new CGlobalDataInfo(data, definedAsGlobalInPriorLayer); + CGlobalDataInfo cGlobalDataInfo = new CGlobalDataInfo(data, definedAsGlobalInPriorLayer, DynamicImageLayerInfo.getCurrentLayerNumber()); if (data.nonConstant) { nonConstantRegistry.registerNonConstantSymbol(cGlobalDataInfo); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LoadImageSingletonFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LoadImageSingletonFeature.java index 5a7021c65c98..36d4dec20b63 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LoadImageSingletonFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LoadImageSingletonFeature.java @@ -37,7 +37,6 @@ import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; -import java.util.function.BiFunction; import java.util.function.Consumer; import org.graalvm.nativeimage.AnnotationAccess; @@ -128,29 +127,13 @@ public boolean isInConfiguration(IsInConfigurationAccess access) { @Override public void registerInvocationPlugins(Providers providers, GraphBuilderConfiguration.Plugins plugins, ParsingReason reason) { - BiFunction loadMultiLayeredImageSingleton = (b, classNode) -> { - Class key = b.getSnippetReflection().asObject(Class.class, classNode.asJavaConstant()); - - if (ImageLayerBuildingSupport.buildingSharedLayer()) { - /* - * Load reference to the proper slot within the cross-layer singleton table. - */ - return LoadImageSingletonFactory.loadLayeredImageSingleton(key, b.getMetaAccess()); - } else { - /* - * Can directly load the array of all objects - */ - JavaConstant multiLayerArray = getMultiLayerConstant(key, b.getMetaAccess(), b.getSnippetReflection()); - return ConstantNode.forConstant(multiLayerArray, 1, true, b.getMetaAccess()); - } - }; InvocationPlugins.Registration r = new InvocationPlugins.Registration(plugins.getInvocationPlugins(), MultiLayeredImageSingleton.class); r.register(new InvocationPlugin.RequiredInvocationPlugin("getAllLayers", Class.class) { @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unused, ValueNode classNode) { - b.addPush(JavaKind.Object, loadMultiLayeredImageSingleton.apply(b, classNode)); + b.addPush(JavaKind.Object, loadMultiLayeredImageSingleton(b, classNode)); return true; } }); @@ -159,7 +142,7 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unused, ValueNode classNode, ValueNode indexNode) { try (InvocationPluginHelper helper = new InvocationPluginHelper(b, targetMethod)) { - ValueNode layerArray = b.add(loadMultiLayeredImageSingleton.apply(b, classNode)); + ValueNode layerArray = b.add(loadMultiLayeredImageSingleton(b, classNode)); helper.intrinsicArrayRangeCheck(layerArray, indexNode, ConstantNode.forInt(1)); var arrayElem = LoadIndexedNode.create(null, layerArray, indexNode, null, JavaKind.Object, b.getMetaAccess(), b.getConstantReflection()); @@ -181,6 +164,23 @@ static void checkAllowNullEntries(Class key) { "This MultiLayeredSingleton requires an entry to be installed in every layer. Please see the javadoc within MultiLayeredAllowNullEntries for more details."); } + public ValueNode loadMultiLayeredImageSingleton(GraphBuilderContext b, ValueNode classNode) { + Class key = b.getSnippetReflection().asObject(Class.class, classNode.asJavaConstant()); + + if (ImageLayerBuildingSupport.buildingSharedLayer()) { + /* + * Load reference to the proper slot within the cross-layer singleton table. + */ + return LoadImageSingletonFactory.loadLayeredImageSingleton(key, b.getMetaAccess()); + } else { + /* + * Can directly load the array of all objects + */ + JavaConstant multiLayerArray = getMultiLayerConstant(key, b.getMetaAccess(), b.getSnippetReflection()); + return ConstantNode.forConstant(multiLayerArray, 1, true, b.getMetaAccess()); + } + } + /** * This method needs to be called after all image singletons are registered. Currently, some * singletons are registered in