diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/InvalidMethodPointerHandler.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/InvalidMethodPointerHandler.java index 60ba1e1547af..f60c4803091f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/InvalidMethodPointerHandler.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/InvalidMethodPointerHandler.java @@ -50,6 +50,10 @@ * the stubs provide full diagnostic output with a stack trace. */ public final class InvalidMethodPointerHandler { + @Platforms(Platform.HOSTED_ONLY.class) // + public static final Method INVALID_CODE_ADDRESS_HANDLER_METHOD = ReflectionUtil.lookupMethod(InvalidMethodPointerHandler.class, "invalidCodeAddressHandler"); + public static final String INVALID_CODE_ADDRESS_MSG = "Fatal error: The invoked code address is invalid and not supposed to be called"; + @Platforms(Platform.HOSTED_ONLY.class) // public static final Method INVALID_VTABLE_ENTRY_HANDLER_METHOD = ReflectionUtil.lookupMethod(InvalidMethodPointerHandler.class, "invalidVTableEntryHandler"); public static final String INVALID_VTABLE_ENTRY_MSG = "Fatal error: Virtual method call used an illegal vtable entry that was seen as unused by the static analysis"; @@ -58,8 +62,23 @@ public final class InvalidMethodPointerHandler { public static final Method METHOD_POINTER_NOT_COMPILED_HANDLER_METHOD = ReflectionUtil.lookupMethod(InvalidMethodPointerHandler.class, "methodPointerNotCompiledHandler"); public static final String METHOD_POINTER_NOT_COMPILED_MSG = "Fatal error: Method pointer invoked on a method that was not compiled because it was not seen as invoked by the static analysis nor was it directly registered for compilation"; + /** + * This method is a placeholder that is put at the beginning of the code section, so that code + * offset 0 and the resulting address become invalid and can be tested for as such. For this to + * work, this method should never be intentionally called or referenced anywhere. + */ + @StubCallingConvention + @NeverInline("We need a separate frame that stores all registers") + @Uninterruptible(reason = "Precaution.") + private static void invalidCodeAddressHandler() { + Pointer callerSP = KnownIntrinsics.readCallerStackPointer(); + CodePointer callerIP = KnownIntrinsics.readReturnAddress(); + failFatally(callerSP, callerIP, INVALID_CODE_ADDRESS_MSG); + } + @StubCallingConvention @NeverInline("We need a separate frame that stores all registers") + @Uninterruptible(reason = "Precaution.") private static void invalidVTableEntryHandler() { Pointer callerSP = KnownIntrinsics.readCallerStackPointer(); CodePointer callerIP = KnownIntrinsics.readReturnAddress(); @@ -68,6 +87,7 @@ private static void invalidVTableEntryHandler() { @StubCallingConvention @NeverInline("We need a separate frame that stores all registers") + @Uninterruptible(reason = "Precaution.") private static void methodPointerNotCompiledHandler() { Pointer callerSP = KnownIntrinsics.readCallerStackPointer(); CodePointer callerIP = KnownIntrinsics.readReturnAddress(); 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 2f912ff5f7e9..8a54d2fc2cdd 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 @@ -891,7 +891,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev log.string("Runtime information:").indent(true); log.string("Isolate id: ").signed(Isolates.getIsolateId()).newline(); log.string("Heap base: ").zhex(KnownIntrinsics.heapBase()).newline(); - if (SubstrateOptions.RelativeCodePointers.getValue()) { + if (SubstrateOptions.useRelativeCodePointers()) { log.string("Code base: ").zhex(KnownIntrinsics.codeBase()).newline(); } log.string("CGlobalData base: ").zhex(CGlobalDataInfo.CGLOBALDATA_RUNTIME_BASE_ADDRESS.getPointer()).newline(); @@ -1145,7 +1145,7 @@ private static final class ImageCodeLocationInfoPrinter { * NOTE: this method may only be called by a single thread. */ public boolean printLocationInfo(Log log, UnsignedWord value) { - if (SubstrateOptions.RelativeCodePointers.getValue() && KnownIntrinsics.codeBase().equal(value)) { + if (SubstrateOptions.useRelativeCodePointers() && KnownIntrinsics.codeBase().equal(value)) { log.string("is the code base"); return true; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index 3c7bcd48486d..00e2dd932fc1 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -1252,6 +1252,10 @@ protected void onValueUpdate(EconomicMap, Object> values, Integer o throw UserError.invalidOptionValue(key, key.getValue(), "Dumping runtime compiled code is not supported on Windows."); } }); + + @Option(help = "Avoid linker relocations for code and instead emit address computations.", type = OptionType.Expert) // + @LayerVerifiedOption(severity = Severity.Error, kind = Kind.Changed, positional = false) // + public static final HostedOptionKey RelativeCodePointers = new HostedOptionKey<>(false, SubstrateOptions::validateRelativeCodePointers); } @Option(help = "Overwrites the available number of processors provided by the OS. Any value <= 0 means using the processor count from the OS.")// @@ -1547,13 +1551,9 @@ public static boolean printClosedArenaUponThrow() { return PrintClosedArenaUponThrow.getValue(); } - @Option(help = "Avoid linker relocations for code and instead emit address computations.", type = OptionType.Expert) // - @LayerVerifiedOption(severity = Severity.Error, kind = Kind.Changed, positional = false) // - public static final HostedOptionKey RelativeCodePointers = new HostedOptionKey<>(false, SubstrateOptions::validateRelativeCodePointers); - @Fold public static boolean useRelativeCodePointers() { - return RelativeCodePointers.getValue(); + return ConcealedOptions.RelativeCodePointers.getValue(); } private static void validateRelativeCodePointers(HostedOptionKey optionKey) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java index a21d9103c587..50dbe3050222 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java @@ -90,7 +90,6 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import org.graalvm.word.WordBase; import com.oracle.svm.configure.config.ConfigurationType; import com.oracle.svm.configure.config.SignatureUtil; @@ -124,6 +123,7 @@ import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; import com.oracle.svm.core.jdk.ProtectionDomainSupport; import com.oracle.svm.core.jdk.Resources; +import com.oracle.svm.core.meta.MethodRef; import com.oracle.svm.core.meta.SharedType; import com.oracle.svm.core.metadata.MetadataTracer; import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils; @@ -365,7 +365,7 @@ public final class DynamicHub implements AnnotatedElement, java.lang.reflect.Typ private final byte layerId; @UnknownObjectField(availability = AfterHostedUniverse.class)// - private WordBase[] vtable; + private MethodRef[] vtable; private final DynamicHubCompanion companion; @@ -650,7 +650,7 @@ public void setSharedData(int layoutEncoding, int monitorOffset, int identityHas } @Platforms(Platform.HOSTED_ONLY.class) - public void setClosedTypeWorldData(WordBase[] vtable, int typeID, short typeCheckStart, short typeCheckRange, short typeCheckSlot, short[] typeCheckSlots) { + public void setClosedTypeWorldData(MethodRef[] vtable, int typeID, short typeCheckStart, short typeCheckRange, short typeCheckSlot, short[] typeCheckSlots) { assert this.vtable == null : "Initialization must be called only once"; this.typeID = typeID; @@ -662,7 +662,7 @@ public void setClosedTypeWorldData(WordBase[] vtable, int typeID, short typeChec } @Platforms(Platform.HOSTED_ONLY.class) - public void setOpenTypeWorldData(WordBase[] vtable, int typeID, int typeCheckDepth, int numClassTypes, int numInterfaceTypes, int[] typeCheckSlots) { + public void setOpenTypeWorldData(MethodRef[] vtable, int typeID, int typeCheckDepth, int numClassTypes, int numInterfaceTypes, int[] typeCheckSlots) { assert this.vtable == null : "Initialization must be called only once"; this.typeID = typeID; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/MethodOffset.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/MethodOffset.java index d44868968361..54fe386c6bce 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/MethodOffset.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/MethodOffset.java @@ -28,20 +28,23 @@ import java.util.Objects; -import org.graalvm.word.WordBase; - +import com.oracle.svm.core.snippets.KnownIntrinsics; import com.oracle.svm.core.util.VMError; import jdk.vm.ci.meta.ResolvedJavaMethod; -/** The offset of the compiled code of a method from the code base. */ -public final class MethodOffset implements WordBase { +/** + * The offset of the compiled code of a method from the {@linkplain KnownIntrinsics#codeBase() code + * base}. + */ +public final class MethodOffset implements MethodRef { private final ResolvedJavaMethod method; public MethodOffset(ResolvedJavaMethod method) { this.method = Objects.requireNonNull(method); } + @Override public ResolvedJavaMethod getMethod() { return method; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/MethodPointer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/MethodPointer.java index 9122d5e9bfa1..253d6274964f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/MethodPointer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/MethodPointer.java @@ -33,10 +33,8 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; -/** - * A pointer to the compiled code of a method. - */ -public final class MethodPointer implements CFunctionPointer { +/** The absolute address of the compiled code of a method. */ +public final class MethodPointer implements CFunctionPointer, MethodRef { private final ResolvedJavaMethod method; private final boolean permitsRewriteToPLT; @@ -50,6 +48,7 @@ public MethodPointer(ResolvedJavaMethod method) { this(method, true); } + @Override public ResolvedJavaMethod getMethod() { return method; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/MethodRef.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/MethodRef.java new file mode 100644 index 000000000000..79cee80fb12a --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/MethodRef.java @@ -0,0 +1,46 @@ +/* + * 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.meta; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.word.WordBase; + +import com.oracle.svm.core.hub.DynamicHub; + +import jdk.vm.ci.meta.ResolvedJavaMethod; + +/** + * A reference to a {@linkplain ResolvedJavaMethod method}. Subtypes are instantiated for specific + * target methods during the image build and embedded in image heap objects, for example, in + * dispatch tables of {@link DynamicHub} objects. For the image, the references are turned into + * addresses or offsets which can be used to invoke the target method. + */ +public interface MethodRef extends WordBase { + + @Platforms(Platform.HOSTED_ONLY.class) + ResolvedJavaMethod getMethod(); + +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateAccessor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateAccessor.java index fadc29cd8def..6c5f83623945 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateAccessor.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateAccessor.java @@ -29,9 +29,13 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.function.CFunctionPointer; +import org.graalvm.word.Pointer; +import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.jdk.InternalVMMethod; +import com.oracle.svm.core.meta.MethodRef; +import com.oracle.svm.core.snippets.KnownIntrinsics; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -57,13 +61,13 @@ public abstract class SubstrateAccessor { * The first-level function that is invoked. It expands the boxed Object[] signature to the * expanded real signature. */ - final CFunctionPointer expandSignature; + private final MethodRef expandSignature; /** * The direct call target, if there is any. For non-virtual invokes, this is the second-level * function that is invoked. For virtual invokes, this value is ignored and the actual target * function is loaded from the vtable. */ - final CFunctionPointer directTarget; + private final MethodRef directTarget; /** * Class that needs to be initialized before invoking the target method. Null when no * initialization is necessary, i.e., when invoking non-static methods or when the class is @@ -72,7 +76,7 @@ public abstract class SubstrateAccessor { final DynamicHub initializeBeforeInvoke; @Platforms(Platform.HOSTED_ONLY.class) - SubstrateAccessor(Executable member, CFunctionPointer expandSignature, CFunctionPointer directTarget, ResolvedJavaMethod targetMethod, DynamicHub initializeBeforeInvoke) { + SubstrateAccessor(Executable member, MethodRef expandSignature, MethodRef directTarget, ResolvedJavaMethod targetMethod, DynamicHub initializeBeforeInvoke) { this.member = member; this.expandSignature = expandSignature; this.directTarget = directTarget; @@ -84,8 +88,9 @@ public Executable getMember() { return member; } - public CFunctionPointer getExpandSignature() { - return expandSignature; + @Platforms(Platform.HOSTED_ONLY.class) + public ResolvedJavaMethod getExpandSignatureMethod() { + return expandSignature.getMethod(); } @Platforms(Platform.HOSTED_ONLY.class) @@ -93,11 +98,28 @@ public ResolvedJavaMethod getTargetMethod() { return targetMethod; } + @SuppressWarnings("unchecked") + protected static T getCodePointer(MethodRef ref) { + Pointer p = (Pointer) ref; + if (SubstrateOptions.useRelativeCodePointers() && p.notEqual(0)) { + p = p.add(KnownIntrinsics.codeBase()); + } + return (T) p; + } + + protected final T getDirectTarget() { + return getCodePointer(directTarget); + } + + protected final T getExpandSignature() { + return getCodePointer(expandSignature); + } + public Object invokeSpecial(Object obj, Object[] args) { - CFunctionPointer target = directTarget; + CFunctionPointer target = getDirectTarget(); if (target.isNull()) { throw new IllegalArgumentException("Cannot do invokespecial for an abstract method"); } - return ((ReflectionAccessorHolder.MethodInvokeFunctionPointer) expandSignature).invoke(obj, args, target); + return ((ReflectionAccessorHolder.MethodInvokeFunctionPointer) getExpandSignature()).invoke(obj, args, target); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateConstructorAccessor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateConstructorAccessor.java index 581c7d7f2602..a4f83f2511bf 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateConstructorAccessor.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateConstructorAccessor.java @@ -28,11 +28,11 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import org.graalvm.nativeimage.c.function.CFunctionPointer; import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.jdk.InternalVMMethod; +import com.oracle.svm.core.meta.MethodRef; import com.oracle.svm.core.reflect.ReflectionAccessorHolder.MethodInvokeFunctionPointer; import jdk.internal.reflect.ConstructorAccessor; @@ -41,12 +41,12 @@ @InternalVMMethod public final class SubstrateConstructorAccessor extends SubstrateAccessor implements ConstructorAccessor { - private final CFunctionPointer factoryMethodTarget; + private final MethodRef factoryMethodTarget; @Platforms(Platform.HOSTED_ONLY.class) // private final ResolvedJavaMethod factoryMethod; - public SubstrateConstructorAccessor(Executable member, CFunctionPointer expandSignature, CFunctionPointer directTarget, ResolvedJavaMethod targetMethod, CFunctionPointer factoryMethodTarget, + public SubstrateConstructorAccessor(Executable member, MethodRef expandSignature, MethodRef directTarget, ResolvedJavaMethod targetMethod, MethodRef factoryMethodTarget, ResolvedJavaMethod factoryMethod, DynamicHub initializeBeforeInvoke) { super(member, expandSignature, directTarget, targetMethod, initializeBeforeInvoke); this.factoryMethodTarget = factoryMethodTarget; @@ -63,7 +63,7 @@ public Object newInstance(Object[] args) { if (initializeBeforeInvoke != null) { EnsureClassInitializedNode.ensureClassInitialized(DynamicHub.toClass(initializeBeforeInvoke)); } - return ((MethodInvokeFunctionPointer) expandSignature).invoke(null, args, factoryMethodTarget); + return ((MethodInvokeFunctionPointer) getExpandSignature()).invoke(null, args, getCodePointer(factoryMethodTarget)); } @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java index 1a3836f2caf0..db0d03fb629a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java @@ -34,6 +34,7 @@ import com.oracle.svm.core.graal.nodes.LoadMethodByIndexNode; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.jdk.InternalVMMethod; +import com.oracle.svm.core.meta.MethodRef; import com.oracle.svm.core.reflect.ReflectionAccessorHolder.MethodInvokeFunctionPointer; import com.oracle.svm.core.reflect.ReflectionAccessorHolder.MethodInvokeFunctionPointerForCallerSensitiveAdapter; import com.oracle.svm.core.util.VMError; @@ -41,12 +42,8 @@ import jdk.internal.reflect.MethodAccessor; import jdk.vm.ci.meta.ResolvedJavaMethod; -interface MethodAccessorJDK19 { - Object invoke(Object obj, Object[] args, Class caller); -} - @InternalVMMethod -public final class SubstrateMethodAccessor extends SubstrateAccessor implements MethodAccessor, MethodAccessorJDK19 { +public final class SubstrateMethodAccessor extends SubstrateAccessor implements MethodAccessor { public static final int VTABLE_INDEX_STATICALLY_BOUND = -1; public static final int VTABLE_INDEX_NOT_YET_COMPUTED = -2; @@ -56,8 +53,8 @@ public final class SubstrateMethodAccessor extends SubstrateAccessor implements public static final int INTERFACE_TYPEID_UNNEEDED = -3; /** - * The expected receiver type, which is checked before invoking the {@link #expandSignature} - * method, or null for static methods. + * The expected receiver type, which is checked before invoking the address from + * {@link #getExpandSignature()}, or {@code null} for static methods. */ private final Class receiverType; /** The actual value is computed after static analysis using a field value transformer. */ @@ -66,7 +63,7 @@ public final class SubstrateMethodAccessor extends SubstrateAccessor implements private final boolean callerSensitiveAdapter; @Platforms(Platform.HOSTED_ONLY.class) - public SubstrateMethodAccessor(Executable member, Class receiverType, CFunctionPointer expandSignature, CFunctionPointer directTarget, ResolvedJavaMethod targetMethod, int vtableIndex, + public SubstrateMethodAccessor(Executable member, Class receiverType, MethodRef expandSignature, MethodRef directTarget, ResolvedJavaMethod targetMethod, int vtableIndex, DynamicHub initializeBeforeInvoke, boolean callerSensitiveAdapter) { super(member, expandSignature, directTarget, targetMethod, initializeBeforeInvoke); this.receiverType = receiverType; @@ -107,7 +104,7 @@ private CFunctionPointer invokeTarget(Object obj) { */ VMError.guarantee(vtableIndex != VTABLE_INDEX_NOT_YET_COMPUTED && interfaceTypeID != INTERFACE_TYPEID_NOT_YET_COMPUTED, "Missed recomputation at image build time"); if (vtableIndex == VTABLE_INDEX_STATICALLY_BOUND) { - return directTarget; + return getDirectTarget(); } else { return (CFunctionPointer) LoadMethodByIndexNode.loadMethodByIndex(obj.getClass(), vtableIndex, interfaceTypeID); } @@ -119,14 +116,14 @@ public Object invoke(Object obj, Object[] args) { throw VMError.shouldNotReachHere("Cannot invoke method that has a @CallerSensitiveAdapter without an explicit caller"); } preInvoke(obj); - return ((MethodInvokeFunctionPointer) expandSignature).invoke(obj, args, invokeTarget(obj)); + return ((MethodInvokeFunctionPointer) getExpandSignature()).invoke(obj, args, invokeTarget(obj)); } @Override public Object invoke(Object obj, Object[] args, Class caller) { if (callerSensitiveAdapter) { preInvoke(obj); - return ((MethodInvokeFunctionPointerForCallerSensitiveAdapter) expandSignature).invoke(obj, args, invokeTarget(obj), caller); + return ((MethodInvokeFunctionPointerForCallerSensitiveAdapter) getExpandSignature()).invoke(obj, args, invokeTarget(obj), caller); } else { /* Not a @CallerSensitiveAdapter method, so we can ignore the caller argument. */ return invoke(obj, args); diff --git a/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp b/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp index 8b8c10e1a3b8..e61007b56e20 100644 --- a/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp +++ b/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp @@ -169,9 +169,11 @@ struct ConstantReference { methodPointer :group { methodId @4 :MethodId; } - cEntryPointLiteralCodePointer @5 :CEntryPointLiteralReference; - cGlobalDataBasePointer @6 :Void; - methodOffset @7 :Void; + methodOffset :group { + methodId @5 :MethodId; + } + cEntryPointLiteralCodePointer @6 :CEntryPointLiteralReference; + cGlobalDataBasePointer @7 :Void; } } 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 32e85f41a044..e9928073f1ff 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 @@ -160,6 +160,8 @@ import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; import com.oracle.svm.core.jdk.ServiceCatalogSupport; import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonSupport; +import com.oracle.svm.core.meta.MethodOffset; +import com.oracle.svm.core.meta.MethodPointer; import com.oracle.svm.core.option.HostedOptionValues; import com.oracle.svm.core.option.OptionClassFilter; import com.oracle.svm.core.option.RuntimeOptionValues; @@ -1235,6 +1237,12 @@ public static void initializeBigBang(Inflation bb, OptionValues options, Feature bb.addRootClass(CFunctionPointer[].class, false, false).registerAsInstantiated("root class"); bb.addRootClass(PointerBase[].class, false, false).registerAsInstantiated("root class"); + /* MethodRef can conceal use of MethodPointer and MethodOffset until after analysis. */ + bb.addRootClass(MethodPointer.class, false, true); + if (SubstrateOptions.useRelativeCodePointers()) { + bb.addRootClass(MethodOffset.class, false, true); + } + bb.addRootMethod(ReflectionUtil.lookupMethod(SubstrateArraycopySnippets.class, "doArraycopy", Object.class, int.class, Object.class, int.class, int.class), true, "Runtime support, registered in " + NativeImageGenerator.class); bb.addRootMethod(ReflectionUtil.lookupMethod(Object.class, "getClass"), true, "Runtime support, registered in " + NativeImageGenerator.class); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/SVMHostedValueProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/SVMHostedValueProvider.java index 692601da030e..6e8a47f96598 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/SVMHostedValueProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/SVMHostedValueProvider.java @@ -38,6 +38,7 @@ import com.oracle.graal.pointsto.util.AnalysisError; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.meta.MethodOffset; +import com.oracle.svm.core.meta.MethodRef; import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.classinitialization.SimulateClassInitializerSupport; @@ -100,7 +101,7 @@ public JavaConstant readArrayElement(JavaConstant array, int index) { } /** - * {@link #forObject} replaces patched words such as relocatable pointers with + * {@link #forObject} replaces patched words such as {@link MethodRef} with * {@link PatchedWordConstant}, and regular {@link WordBase} values with * {@link PrimitiveConstant}. No other {@link WordBase} values can be reachable at this point. */ @@ -157,9 +158,8 @@ public JavaConstant interceptHosted(JavaConstant constant) { /** * Intercept {@link WordBase} constants and: *
    - *
  • replace {@link RelocatedPointer} and {@link MethodOffset} constants with - * {@link PatchedWordConstant} to easily and reliably distinguish them from other - * {@link WordBase} values during image build.
  • + *
  • replace {@link MethodRef} constants with {@link PatchedWordConstant} to easily and + * reliably distinguish them from other {@link WordBase} values during image build.
  • *
  • replace regular {@link WordBase} values with corresponding integer kind * {@link PrimitiveConstant}.
  • *
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java index afff6136ca05..11258b71e808 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java @@ -61,6 +61,7 @@ import com.oracle.svm.core.heap.RestrictHeapAccess; import com.oracle.svm.core.heap.RestrictHeapAccessCallees; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; +import com.oracle.svm.core.meta.MethodRef; import com.oracle.svm.core.meta.SubstrateMethodOffsetConstant; import com.oracle.svm.core.meta.SubstrateMethodPointerConstant; import com.oracle.svm.core.util.InterruptImageBuilding; @@ -245,12 +246,12 @@ public String toString() { } } - public static class MethodConstantReason extends CompileReason { + public static class MethodRefConstantReason extends CompileReason { private final HostedMethod owner; private final HostedMethod callTarget; - public MethodConstantReason(HostedMethod owner, HostedMethod callTarget, CompileReason prevReason) { + public MethodRefConstantReason(HostedMethod owner, HostedMethod callTarget, CompileReason prevReason) { super(prevReason); this.owner = owner; this.callTarget = callTarget; @@ -258,7 +259,7 @@ public MethodConstantReason(HostedMethod owner, HostedMethod callTarget, Compile @Override public String toString() { - return "Method " + callTarget.format("%r %h.%n(%p)") + " is reachable through a method pointer/offset from " + owner.format("%r %h.%n(%p)"); + return "Method " + callTarget.format("%r %h.%n(%p)") + " is reachable via MethodRef from " + owner.format("%r %h.%n(%p)"); } } @@ -1415,7 +1416,7 @@ protected void ensureCalleesCompiled(HostedMethod method, CompileReason reason, } } } - ensureCompiledForMethodConstants(method, reason, result); + ensureCompiledForMethodRefConstants(method, reason, result); } protected void removeDeoptTargetOptimizations(Suites suites) { @@ -1426,18 +1427,19 @@ protected void removeDeoptTargetOptimizations(LIRSuites lirSuites) { DeoptimizationUtils.removeDeoptTargetOptimizations(lirSuites); } - protected final void ensureCompiledForMethodConstants(HostedMethod method, CompileReason reason, CompilationResult result) { + protected final void ensureCompiledForMethodRefConstants(HostedMethod method, CompileReason reason, CompilationResult result) { for (DataPatch dataPatch : result.getDataPatches()) { if (dataPatch.reference instanceof ConstantReference constantRef) { VMConstant constant = constantRef.getConstant(); - HostedMethod referencedMethod = null; + MethodRef ref = null; if (constant instanceof SubstrateMethodPointerConstant pointerConstant) { - referencedMethod = (HostedMethod) pointerConstant.pointer().getMethod(); + ref = pointerConstant.pointer(); } else if (constant instanceof SubstrateMethodOffsetConstant offsetConstant) { - referencedMethod = (HostedMethod) offsetConstant.offset().getMethod(); + ref = offsetConstant.offset(); } - if (referencedMethod != null) { - ensureCompiled(referencedMethod, new MethodConstantReason(method, referencedMethod, reason)); + if (ref != null) { + HostedMethod referencedMethod = (HostedMethod) ref.getMethod(); + ensureCompiled(referencedMethod, new MethodRefConstantReason(method, referencedMethod, reason)); } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java index 2c1874d868ce..3a4b780dbc56 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java @@ -103,6 +103,7 @@ import com.oracle.svm.core.jni.access.JNIAccessibleMethod; import com.oracle.svm.core.meta.MethodOffset; import com.oracle.svm.core.meta.MethodPointer; +import com.oracle.svm.core.meta.MethodRef; import com.oracle.svm.core.option.SubstrateOptionsParser; import com.oracle.svm.core.os.ImageHeapProvider; import com.oracle.svm.core.reflect.SubstrateAccessor; @@ -650,9 +651,9 @@ private static boolean checkCodeRelocationKind(Info info) { * accessors}, {@linkplain JNIAccessibleMethod JNI accessors} and in * {@link FunctionPointerHolder}. * - * With {@link SubstrateOptions#RelativeCodePointers}, virtual dispatch tables contain offsets - * relative to a code base address and so do not need to be patched at runtime, which also - * avoids the cost of private copies of memory pages with the patched values. + * With {@link SubstrateOptions#useRelativeCodePointers()}, virtual dispatch tables contain + * offsets relative to a code base address and so do not need to be patched at runtime, which + * also avoids the cost of private copies of memory pages with the patched values. * * With code offsets and layered images, however, the code base refers only to the initial * layer's code section, so we patch offsets to code from other layers to become relative to @@ -666,18 +667,13 @@ private static boolean checkCodeRelocationKind(Info info) { */ private void markSiteOfRelocationToCode(final ProgbitsSectionImpl sectionImpl, final int offset, final RelocatableBuffer.Info info) { Object targetObject = info.getTargetObject(); - assert targetObject instanceof MethodPointer || targetObject instanceof MethodOffset : "Wrong type for code relocation: " + targetObject.toString(); + assert targetObject instanceof MethodRef : "Wrong type for code relocation: " + targetObject.toString(); if (sectionImpl.getElement() == textSection) { validateNoDirectRelocationsInTextSection(info); } - ResolvedJavaMethod method; - if (targetObject instanceof MethodOffset methodOffset) { - method = methodOffset.getMethod(); - } else { - method = ((MethodPointer) targetObject).getMethod(); - } + ResolvedJavaMethod method = ((MethodRef) targetObject).getMethod(); HostedMethod hMethod = (method instanceof HostedMethod) ? (HostedMethod) method : heap.hUniverse.lookup(method); boolean injectedNotCompiled = isInjectedNotCompiled(hMethod); HostedMethod target = getMethodRefTargetMethod(metaAccess, hMethod); @@ -1129,6 +1125,18 @@ final class MethodPointerInvalidHandlerFeature implements InternalFeature { @Override public void beforeAnalysis(BeforeAnalysisAccess a) { FeatureImpl.BeforeAnalysisAccessImpl access = (FeatureImpl.BeforeAnalysisAccessImpl) a; - access.registerAsRoot(InvalidMethodPointerHandler.METHOD_POINTER_NOT_COMPILED_HANDLER_METHOD, true, "InvalidMethodPointerHandler, registered in " + MethodPointerInvalidHandlerFeature.class); + Method invalidCodeAddressHandler = getInvalidCodeAddressHandler(); + if (invalidCodeAddressHandler != null) { + access.registerAsRoot(invalidCodeAddressHandler, true, "Registered in " + MethodPointerInvalidHandlerFeature.class); + } + access.registerAsRoot(InvalidMethodPointerHandler.METHOD_POINTER_NOT_COMPILED_HANDLER_METHOD, true, "Registered in " + MethodPointerInvalidHandlerFeature.class); + } + + static Method getInvalidCodeAddressHandler() { + if (HostedImageLayerBuildingSupport.buildingExtensionLayer()) { + /* Code offset 0 is in the initial layer, where the handler is already present. */ + return null; + } + return InvalidMethodPointerHandler.INVALID_CODE_ADDRESS_HANDLER_METHOD; } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java index 8f45ca665955..210ee9aeb966 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java @@ -45,6 +45,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -166,7 +167,7 @@ public NativeImageCodeCache(Map compilations, N this.imageHeap = imageHeap; this.dataSection = new DataSection(); this.targetPlatform = targetPlatform; - this.orderedCompilations = computeCompilationOrder(compilations); + this.orderedCompilations = computeCompilationOrder(compilations, imageHeap.hMetaAccess); } public abstract int getCodeCacheSize(); @@ -188,11 +189,27 @@ public Pair getLastCompilation() { return orderedCompilations.getLast(); } - protected List> computeCompilationOrder(Map compilationMap) { + private static List> computeCompilationOrder(Map compilationMap, HostedMetaAccess metaAccess) { var orderedMethods = ImageSingletons.lookup(CodeSectionLayouter.class).layout(compilationMap); - return orderedMethods.stream() - .map(hm -> Pair.create(hm, compilations.get(hm))) - .collect(Collectors.toList()); + + /* We force this method to be at code offset 0 to make that offset and address invalid. */ + HostedMethod invalidMethod = getInvalidCodeAddressHandler(metaAccess); + + var orderedCompilations = new ArrayList>(); + if (invalidMethod != null) { + orderedCompilations.add(Pair.create(invalidMethod, compilationMap.get(invalidMethod))); + } + for (HostedMethod method : orderedMethods) { + if (!Objects.equals(invalidMethod, method)) { + orderedCompilations.add(Pair.create(method, compilationMap.get(method))); + } + } + return orderedCompilations; + } + + private static HostedMethod getInvalidCodeAddressHandler(HostedMetaAccess metaAccess) { + Method invalidCodeMethod = MethodPointerInvalidHandlerFeature.getInvalidCodeAddressHandler(); + return (invalidCodeMethod == null) ? null : metaAccess.lookupJavaMethod(invalidCodeMethod); } public List> getOrderedCompilations() { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java index c57d66744b08..06117186ef67 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java @@ -288,12 +288,18 @@ public void addTrailingObjects() { * Bypass shadow heap reading for inlined fields. These fields are not actually present in the * image (their value is inlined) and are not present in the shadow heap either. */ - public Object readInlinedField(HostedField field, JavaConstant receiver) { + public JavaConstant readInlinedFieldAsConstant(HostedField field, JavaConstant receiver) { VMError.guarantee(HostedConfiguration.isInlinedField(field), "Expected an inlined field, found %s", field); JavaConstant hostedReceiver = ((ImageHeapInstance) receiver).getHostedObject(); /* Use the HostedValuesProvider to get direct access to hosted values. */ HostedValuesProvider hostedValuesProvider = aUniverse.getHostedValuesProvider(); - return hUniverse.getSnippetReflection().asObject(Object.class, hostedValuesProvider.readFieldValueWithReplacement(field.getWrapped(), hostedReceiver)); + return hostedValuesProvider.readFieldValueWithReplacement(field.getWrapped(), hostedReceiver); + } + + /** {@link #readInlinedFieldAsConstant}, extracting the object from the {@link JavaConstant}. */ + public Object readInlinedField(HostedField field, JavaConstant receiver) { + JavaConstant constant = readInlinedFieldAsConstant(field, receiver); + return hUniverse.getSnippetReflection().asObject(Object.class, constant); } private JavaConstant readConstantField(HostedField field, JavaConstant receiver) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java index d84d5046476a..36ed39869053 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java @@ -27,17 +27,16 @@ import static com.oracle.svm.core.util.VMError.shouldNotReachHere; import static com.oracle.svm.core.util.VMError.shouldNotReachHereUnexpectedInput; -import java.lang.reflect.Array; import java.lang.reflect.Modifier; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.stream.Stream; import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.c.function.RelocatedPointer; import org.graalvm.nativeimage.impl.CEntryPointLiteralCodePointer; import org.graalvm.word.WordBase; +import com.oracle.graal.pointsto.heap.HostedValuesProvider; import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.graal.pointsto.heap.ImageHeapPrimitiveArray; import com.oracle.graal.pointsto.heap.ImageHeapRelocatableConstant; @@ -56,6 +55,7 @@ import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; import com.oracle.svm.core.meta.MethodOffset; import com.oracle.svm.core.meta.MethodPointer; +import com.oracle.svm.core.meta.MethodRef; import com.oracle.svm.core.util.HostedByteBufferPointer; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.DeadlockWatchdog; @@ -66,6 +66,7 @@ import com.oracle.svm.hosted.imagelayer.CrossLayerConstantRegistryFeature; import com.oracle.svm.hosted.imagelayer.LayeredImageHooks; import com.oracle.svm.hosted.meta.HostedClass; +import com.oracle.svm.hosted.meta.HostedConstantReflectionProvider; import com.oracle.svm.hosted.meta.HostedField; import com.oracle.svm.hosted.meta.HostedInstanceClass; import com.oracle.svm.hosted.meta.HostedMetaAccess; @@ -91,13 +92,15 @@ public final class NativeImageHeapWriter { private final NativeImageHeap heap; private final ImageHeapLayoutInfo heapLayout; - private long sectionOffsetOfARelocatablePointer; private final boolean imageLayer = ImageLayerBuildingSupport.buildingImageLayer(); + private final LayeredImageHooks layerHooks = imageLayer ? LayeredImageHooks.singleton() : null; + private final CrossLayerConstantRegistryFeature layerConstantRegistry = imageLayer ? CrossLayerConstantRegistryFeature.singleton() : null; + private final JavaKind wordKind = ConfigurationValues.getWordKind(); + private long sectionOffsetOfARelocatablePointer = -1; public NativeImageHeapWriter(NativeImageHeap heap, ImageHeapLayoutInfo heapLayout) { this.heap = heap; this.heapLayout = heapLayout; - this.sectionOffsetOfARelocatablePointer = -1; } /** @@ -107,6 +110,7 @@ public NativeImageHeapWriter(NativeImageHeap heap, ImageHeapLayoutInfo heapLayou @SuppressWarnings("try") public long writeHeap(DebugContext debug, RelocatableBuffer buffer) { try (Indent perHeapIndent = debug.logAndIndent("NativeImageHeap.writeHeap:")) { + DeadlockWatchdog watchdog = DeadlockWatchdog.singleton(); for (ObjectInfo info : heap.getObjects()) { assert !heap.isBlacklisted(info.getObject()) : "Backlisted object: " + info.getObject(); if (info.getConstant().isWrittenInPreviousLayer()) { @@ -120,20 +124,19 @@ public long writeHeap(DebugContext debug, RelocatableBuffer buffer) { } writeObject(info, buffer); - DeadlockWatchdog.singleton().recordActivity(); + watchdog.recordActivity(); } // Only static fields that are writable get written to the native image heap, // the read-only static fields have been inlined into the code. - writeStaticFields(buffer); + writeStaticFields(buffer, watchdog); heap.getLayouter().writeMetadata(buffer.getByteBuffer(), 0); } return sectionOffsetOfARelocatablePointer; } - @SuppressWarnings("resource") - private void writeStaticFields(RelocatableBuffer buffer) { + private void writeStaticFields(RelocatableBuffer buffer, DeadlockWatchdog watchdog) { /* * Write the values of static fields. The arrays for primitive and object fields are empty * and just placeholders. This ensures we get the latest version, since there can be @@ -149,7 +152,7 @@ private void writeStaticFields(RelocatableBuffer buffer) { writeField(buffer, fields, field, null, null); } - DeadlockWatchdog.singleton().recordActivity(); + watchdog.recordActivity(); } } @@ -176,15 +179,8 @@ private void writeField(RelocatableBuffer buffer, ObjectInfo fields, HostedField throw NativeImageHeap.reportIllegalType(ex.getType(), info); } - if (value instanceof ImageHeapRelocatableConstant constant) { - int heapOffset = NumUtil.safeToInt(fields.getOffset() + field.getLocation()); - CrossLayerConstantRegistryFeature.singleton().markFutureHeapConstantPatchSite(constant, heapOffset); - fillReferenceWithGarbage(buffer, index); - } else if (value instanceof PatchedWordConstant) { - addWordConstantRelocation(buffer, index, prepareRelocatable(info, value)); - } else { - write(buffer, index, value, info != null ? info : field); - } + Object reason = (info != null) ? info : field; + writeConstant(buffer, index, value.getJavaKind(), value, info, reason); } private void fillReferenceWithGarbage(RelocatableBuffer buffer, int index) { @@ -224,33 +220,15 @@ void writeReference(RelocatableBuffer buffer, int index, JavaConstant target, Ob } } - private void writeConstant(RelocatableBuffer buffer, int index, JavaKind kind, JavaConstant constant, ObjectInfo info) { - if (constant instanceof PatchedWordConstant) { - addWordConstantRelocation(buffer, index, prepareRelocatable(info, constant)); - return; - } - - final JavaConstant con; - if (heap.hMetaAccess.isInstanceOf(constant, WordBase.class)) { - Object value = snippetReflection().asObject(Object.class, constant); - con = JavaConstant.forIntegerKind(ConfigurationValues.getWordKind(), ((WordBase) value).rawValue()); - } else if (constant.isNull() && kind == ConfigurationValues.getWordKind()) { - con = JavaConstant.forIntegerKind(ConfigurationValues.getWordKind(), 0); - } else { - con = constant; - } - write(buffer, index, con, info); - } - /** - * Ensure the pointer has been processed by {@link CEntryPointLiteralFeature}. The replacement - * done when the value is added to the shadow heap can miss the transformation from - * {@link CEntryPointLiteralCodePointer} to {@link MethodPointer} because this transformation - * can only happen late, during compilation. + * Ensure a {@link CEntryPointLiteralCodePointer} has been processed by + * {@link CEntryPointLiteralFeature}. The replacement done when the value is added to the shadow + * heap can miss the transformation to {@link MethodPointer} because this transformation can + * only happen late, during compilation. */ - private RelocatedPointer prepareRelocatable(ObjectInfo info, JavaConstant value) { + private WordBase prepareRelocatable(ObjectInfo info, WordBase word) { try { - return (RelocatedPointer) heap.aUniverse.replaceObject(snippetReflection().asObject(RelocatedPointer.class, value)); + return (WordBase) heap.aUniverse.replaceObject(word); } catch (AnalysisError.TypeNotFoundError ex) { throw NativeImageHeap.reportIllegalType(ex.getType(), info); } @@ -260,34 +238,47 @@ private RelocatedPointer prepareRelocatable(ObjectInfo info, JavaConstant value) * @see NativeImageHeap#isRelocatableValue * @see NativeImage#markSiteOfRelocationToCode */ - private void writeConstant(RelocatableBuffer buffer, int index, JavaKind kind, Object constantValue, ObjectInfo info) { - Object value = constantValue; - if (value instanceof MethodOffset methodOffset) { - HostedMetaAccess metaAccess = heap.hMetaAccess; - ResolvedJavaMethod method = methodOffset.getMethod(); - HostedMethod hMethod = (method instanceof HostedMethod hm) ? hm : metaAccess.getUniverse().lookup(method); - if (imageLayer && NativeImage.isInjectedNotCompiled(hMethod)) { - // Will be patched in a future layer (even if it ends up not being compiled at all) - addWordConstantRelocation(buffer, index, methodOffset); - return; + private void writeConstant(RelocatableBuffer buffer, int index, JavaKind kind, JavaConstant constant, ObjectInfo info, Object reason) { + int offsetInHeap = NumUtil.safeToInt(index + heapLayout.getStartOffset()); + + if (constant instanceof ImageHeapRelocatableConstant ihrc) { + layerConstantRegistry.markFutureHeapConstantPatchSite(ihrc, offsetInHeap); + fillReferenceWithGarbage(buffer, index); + return; + } + + HostedMetaAccess metaAccess = heap.hMetaAccess; + if (constant instanceof PatchedWordConstant pwc) { + if (pwc.getWord() instanceof MethodOffset methodOffset) { + ResolvedJavaMethod method = methodOffset.getMethod(); + HostedMethod hMethod = (method instanceof HostedMethod hm) ? hm : metaAccess.getUniverse().lookup(method); + if (imageLayer && NativeImage.isInjectedNotCompiled(hMethod)) { + // Will be patched in a future layer (even if it ends up not compiled at all) + addWordConstantRelocation(buffer, index, methodOffset); + } else { + HostedMethod target = NativeImage.getMethodRefTargetMethod(metaAccess, hMethod); + JavaConstant con = JavaConstant.forIntegerKind(wordKind, target.getCodeAddressOffset()); + write(buffer, index, con, reason); + } + } else { + addWordConstantRelocation(buffer, index, prepareRelocatable(info, pwc.getWord())); + } + if (imageLayer) { + layerHooks.processPatchedWordWritten(pwc.getWord(), offsetInHeap, heapLayout); } - HostedMethod target = NativeImage.getMethodRefTargetMethod(metaAccess, hMethod); - value = target.getCodeAddressOffset(); - } else if (value instanceof RelocatedPointer relocatedPointer) { - addWordConstantRelocation(buffer, index, relocatedPointer); return; } final JavaConstant con; - if (value instanceof WordBase) { - con = JavaConstant.forIntegerKind(ConfigurationValues.getWordKind(), ((WordBase) value).rawValue()); - } else if (value == null && kind == ConfigurationValues.getWordKind()) { - con = JavaConstant.forIntegerKind(ConfigurationValues.getWordKind(), 0); + if (metaAccess.isInstanceOf(constant, WordBase.class)) { + Object value = snippetReflection().asObject(Object.class, constant); + con = JavaConstant.forIntegerKind(wordKind, ((WordBase) value).rawValue()); + } else if (constant.isNull() && kind == wordKind) { + con = JavaConstant.forIntegerKind(wordKind, 0); } else { - assert kind == JavaKind.Object || value != null : "primitive value must not be null"; - con = snippetReflection().forBoxed(kind, value); + con = constant; } - write(buffer, index, con, info); + write(buffer, index, con, reason); } private void writeHubPointer(RelocatableBuffer buffer, int index, ObjectInfo obj) { @@ -329,12 +320,9 @@ private void addDirectRelocationWithAddend(RelocatableBuffer buffer, int index, } } - /** - * Adds a relocation for a word constant. - */ private void addWordConstantRelocation(RelocatableBuffer buffer, int index, WordBase word) { mustBeReferenceAligned(index); - assert word instanceof MethodOffset || word instanceof MethodPointer || word instanceof CGlobalDataBasePointer : "unknown relocatable " + word; + assert word instanceof MethodRef || word instanceof CGlobalDataBasePointer : "unknown relocatable " + word; int pointerSize = ConfigurationValues.getTarget().wordSize; addDirectRelocationWithoutAddend(buffer, index, pointerSize, word); } @@ -387,14 +375,17 @@ private static void writeValue(RelocatableBuffer buffer, int index, long value, private void writeObject(ObjectInfo info, RelocatableBuffer buffer) { VMError.guarantee(!(info.getConstant() instanceof ImageHeapRelocatableConstant), "ImageHeapRelocationConstants cannot be written to the heap %s", info.getConstant()); + + ObjectLayout objectLayout = heap.objectLayout; + DynamicHubLayout dynamicHubLayout = heap.dynamicHubLayout; + HostedConstantReflectionProvider constantReflection = heap.hConstantReflection; + HostedValuesProvider hostedValuesProvider = heap.aUniverse.getHostedValuesProvider(); + /* * Write a reference from the object to its hub. This lives at layout.getHubOffset() from * the object base. */ - ObjectLayout objectLayout = heap.objectLayout; - DynamicHubLayout dynamicHubLayout = heap.dynamicHubLayout; assert objectLayout.isAligned(getIndexInBuffer(info, 0)); - writeHubPointer(buffer, getIndexInBuffer(info, objectLayout.getHubOffset()), info); ByteBuffer bufferBytes = buffer.getByteBuffer(); @@ -431,39 +422,36 @@ private void writeObject(ObjectInfo info, RelocatableBuffer buffer) { } /* Write vtable slots and length. */ - Object vTable = heap.readInlinedField(dynamicHubLayout.vTableField, con); - int vtableLength = Array.getLength(vTable); + JavaConstant vTable = heap.readInlinedFieldAsConstant(dynamicHubLayout.vTableField, con); + int vtableLength = hostedValuesProvider.readArrayLength(vTable); bufferBytes.putInt(getIndexInBuffer(info, dynamicHubLayout.getVTableLengthOffset()), vtableLength); final JavaKind elementStorageKind = dynamicHubLayout.getVTableSlotStorageKind(); for (int i = 0; i < vtableLength; i++) { - Object vtableSlot = Array.get(vTable, i); + JavaConstant vtableSlot = hostedValuesProvider.readArrayElement(vTable, i); int elementIndex = getIndexInBuffer(info, dynamicHubLayout.getVTableSlotOffset(i)); - writeConstant(buffer, elementIndex, elementStorageKind, vtableSlot, info); + writeConstant(buffer, elementIndex, elementStorageKind, vtableSlot, info, info); } idHashOffset = dynamicHubLayout.getIdentityHashOffset(vtableLength); instanceFields = instanceFields.filter(field -> !dynamicHubLayout.isIgnoredField(field)); - if (imageLayer) { - int vTableOffsetInHeapRelocs = NumUtil.safeToInt(info.getOffset() + dynamicHubLayout.getVTableSlotOffset(0) - heapLayout.getReadOnlyRelocatableOffset()); - LayeredImageHooks.processWrittenDynamicHub( - new LayeredImageHooks.WrittenDynamicHubInfo((DynamicHub) info.getObject(), heap.aUniverse, heap.hUniverse, vTable, vTableOffsetInHeapRelocs)); + if (layerHooks != null) { + MethodRef[] vtableObject = hostedValuesProvider.asObject(MethodRef[].class, vTable); + layerHooks.processDynamicHubWritten((DynamicHub) info.getObject(), vtableObject); } } else if (heap.getHybridLayout(clazz) != null) { HybridLayout hybridLayout = heap.getHybridLayout(clazz); - /* - * write array and its length - */ + /* Write array and its length. */ HostedField hybridArrayField = hybridLayout.getArrayField(); - Object hybridArray = heap.readInlinedField(hybridArrayField, con); - int length = Array.getLength(hybridArray); + JavaConstant hybridArray = heap.readInlinedFieldAsConstant(hybridArrayField, con); + int length = hostedValuesProvider.readArrayLength(hybridArray); bufferBytes.putInt(getIndexInBuffer(info, objectLayout.getArrayLengthOffset()), length); for (int i = 0; i < length; i++) { final int elementIndex = getIndexInBuffer(info, hybridLayout.getArrayElementOffset(i)); final JavaKind elementStorageKind = hybridLayout.getArrayElementStorageKind(); - final Object array = Array.get(hybridArray, i); - writeConstant(buffer, elementIndex, elementStorageKind, array, info); + JavaConstant element = hostedValuesProvider.readArrayElement(hybridArray, i); + writeConstant(buffer, elementIndex, elementStorageKind, element, info, info); } idHashOffset = hybridLayout.getIdentityHashOffset(length); @@ -492,7 +480,7 @@ private void writeObject(ObjectInfo info, RelocatableBuffer buffer) { JavaKind kind = clazz.getComponentType().getStorageKind(); ImageHeapConstant constant = info.getConstant(); - int length = heap.hConstantReflection.readArrayLength(constant); + int length = constantReflection.readArrayLength(constant); bufferBytes.putInt(getIndexInBuffer(info, objectLayout.getArrayLengthOffset()), length); HostedByteBufferPointer identityHashPtr = getHashCodePtr(info, bufferBytes, objectLayout, kind, length); IdentityHashCodeSupport.writeIdentityHashCodeToImageHeap(identityHashPtr, info.getIdentityHashCode()); @@ -501,16 +489,10 @@ private void writeObject(ObjectInfo info, RelocatableBuffer buffer) { ImageHeapPrimitiveArray imageHeapArray = (ImageHeapPrimitiveArray) constant; writePrimitiveArray(info, buffer, objectLayout, kind, imageHeapArray.getArray(), length); } else { - heap.hConstantReflection.forEachArrayElement(constant, (element, index) -> { + constantReflection.forEachArrayElement(constant, (element, index) -> { long elementOffset = objectLayout.getArrayElementOffset(kind, index); final int elementIndex = getIndexInBuffer(info, elementOffset); - if (element instanceof ImageHeapRelocatableConstant ihcConstant) { - int heapOffset = NumUtil.safeToInt(info.getOffset() + elementOffset); - CrossLayerConstantRegistryFeature.singleton().markFutureHeapConstantPatchSite(ihcConstant, heapOffset); - fillReferenceWithGarbage(buffer, elementIndex); - } else { - writeConstant(buffer, elementIndex, kind, element, info); - } + writeConstant(buffer, elementIndex, kind, element, info, info); }); } } else { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/RelocatableBuffer.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/RelocatableBuffer.java index 24acef6f2843..e7d836cf55b8 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/RelocatableBuffer.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/RelocatableBuffer.java @@ -38,8 +38,7 @@ import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.objectfile.ObjectFile; import com.oracle.svm.core.graal.code.CGlobalDataBasePointer; -import com.oracle.svm.core.meta.MethodOffset; -import com.oracle.svm.core.meta.MethodPointer; +import com.oracle.svm.core.meta.MethodRef; import jdk.graal.compiler.core.common.NumUtil; import jdk.vm.ci.code.site.Reference; @@ -98,8 +97,7 @@ public static final class Info { this.targetObject = targetObject; /* Sanity check for allowed groups of target objects. */ - assert targetObject instanceof Reference || targetObject instanceof MethodPointer || targetObject instanceof MethodOffset || - targetObject instanceof CGlobalDataBasePointer || targetObject instanceof ImageHeapConstant : targetObject; + assert targetObject instanceof Reference || targetObject instanceof MethodRef || targetObject instanceof CGlobalDataBasePointer || targetObject instanceof ImageHeapConstant : targetObject; } public int getRelocationSize() { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedImageLayerBuildingSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedImageLayerBuildingSupport.java index b7f7c4839123..6278dc0d570d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedImageLayerBuildingSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedImageLayerBuildingSupport.java @@ -203,7 +203,7 @@ public static void processLayerOptions(EconomicMap, Object> values, */ SubstrateOptions.ApplicationLayerInitializedClasses.update(values, Module.class.getName()); - setOptionIfHasNotBeenSet(values, SubstrateOptions.RelativeCodePointers, true); + setOptionIfHasNotBeenSet(values, SubstrateOptions.ConcealedOptions.RelativeCodePointers, true); } if (isLayerUseOptionEnabled(hostedOptions)) { @@ -214,7 +214,7 @@ public static void processLayerOptions(EconomicMap, Object> values, } enableConservativeUnsafeAccess(values); SubstrateOptions.ApplicationLayerInitializedClasses.update(values, Module.class.getName()); - setOptionIfHasNotBeenSet(values, SubstrateOptions.RelativeCodePointers, true); + setOptionIfHasNotBeenSet(values, SubstrateOptions.ConcealedOptions.RelativeCodePointers, true); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredDispatchTableFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredDispatchTableFeature.java index 719aba28e287..b5f0231eff11 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredDispatchTableFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredDispatchTableFeature.java @@ -24,7 +24,6 @@ */ package com.oracle.svm.hosted.imagelayer; -import java.lang.reflect.Array; import java.util.Arrays; import java.util.BitSet; import java.util.Collections; @@ -55,23 +54,27 @@ import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.graal.snippets.OpenTypeWorldDispatchTableSnippets; +import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.image.ImageHeapLayoutInfo; import com.oracle.svm.core.imagelayer.DynamicImageLayerInfo; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; import com.oracle.svm.core.layeredimagesingleton.FeatureSingleton; import com.oracle.svm.core.meta.MethodOffset; +import com.oracle.svm.core.meta.MethodRef; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl; import com.oracle.svm.hosted.FeatureImpl.BeforeCompilationAccessImpl; -import com.oracle.svm.hosted.SVMHost; import com.oracle.svm.hosted.image.NativeImage; import com.oracle.svm.hosted.image.NativeImageCodeCache; import com.oracle.svm.hosted.meta.HostedMetaAccess; import com.oracle.svm.hosted.meta.HostedMethod; import com.oracle.svm.hosted.meta.HostedType; +import com.oracle.svm.hosted.meta.HostedUniverse; import com.oracle.svm.hosted.meta.VTableBuilder; import jdk.graal.compiler.code.CompilationResult; +import jdk.graal.compiler.core.common.NumUtil; import jdk.graal.compiler.debug.Assertions; import jdk.graal.compiler.options.Option; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -108,6 +111,9 @@ public static final class Options { private final boolean buildingInitialLayer = buildingSharedLayer && ImageLayerBuildingSupport.buildingInitialLayer(); private final boolean buildingExtensionLayer = ImageLayerBuildingSupport.buildingExtensionLayer(); + private int wordSize; + private HostedUniverse hUniverse; + final Map priorDispatchTableCache = buildingExtensionLayer ? new ConcurrentHashMap<>() : null; final Map priorDispatchMethodCache = buildingExtensionLayer ? new ConcurrentHashMap<>() : null; @@ -115,7 +121,7 @@ public static final class Options { final boolean generateUnresolvedSymbolNames = buildingSharedLayer; Map persistedHostedMethodIndexMap = buildingSharedLayer ? new ConcurrentHashMap<>() : null; - final Map vtableWordToDispatchSlot = new IdentityHashMap<>(); + final Map vtableWordToDispatchSlot = new IdentityHashMap<>(); final Map typeToDispatchTable = new HashMap<>(); /** * Bitmap relative to the start of the current layer's image heap relocatables partition where @@ -146,18 +152,21 @@ public boolean isInConfiguration(Feature.IsInConfigurationAccess access) { @Override public void beforeAnalysis(Feature.BeforeAnalysisAccess access) { + wordSize = ConfigurationValues.getTarget().wordSize; if (ImageLayerBuildingSupport.buildingExtensionLayer()) { var config = (FeatureImpl.BeforeAnalysisAccessImpl) access; getPriorVirtualCallTargets().forEach(aMethod -> { config.registerAsRoot(aMethod, false, "in prior layer dispatch table"); }); } - LayeredImageHooks.registerDynamicHubWrittenCallback(this::onDynamicHubWritten); + LayeredImageHooks.singleton().registerDynamicHubWrittenCallback(this::onDynamicHubWritten); + LayeredImageHooks.singleton().registerPatchedWordWrittenCallback(this::onPatchedWordWritten); } @Override public void beforeCompilation(Feature.BeforeCompilationAccess a) { BeforeCompilationAccessImpl access = (BeforeCompilationAccessImpl) a; + hUniverse = access.getUniverse(); installBuilderModules(access.getImageClassLoader().getBuilderModules()); } @@ -412,19 +421,14 @@ private static void compareTypeInfo(HostedDispatchTable curInfo, PriorDispatchTa } } - /* - * Recording a hub was written to the heap - */ - public void onDynamicHubWritten(LayeredImageHooks.WrittenDynamicHubInfo hubInfo) { - AnalysisType aType = ((SVMHost) hubInfo.aUniverse().hostVM()).lookupType(hubInfo.hub()); - HostedType hType = hubInfo.hUniverse().lookup(aType); + private void onDynamicHubWritten(DynamicHub hub, MethodRef[] vtable) { + AnalysisType aType = hUniverse.hostVM().lookupType(hub); + HostedType hType = hUniverse.lookup(aType); assert hType.getWrapped().isReachable() : "All installed hubs should be reachable " + hType; - Object vTable = hubInfo.vTable(); - int vtableLength = Array.getLength(vTable); if (VTableBuilder.hasEmptyDispatchTable(hType)) { - assert vtableLength == 0 : hType; + assert vtable.length == 0 : hType; return; } @@ -434,45 +438,53 @@ public void onDynamicHubWritten(LayeredImageHooks.WrittenDynamicHubInfo hubInfo) assert dispatchTable.status == HubStatus.DISPATCH_INFO_CALCULATED || dispatchTable.status == HubStatus.COMPUTED_PRIOR_LAYER : dispatchTable; dispatchTable.status = HubStatus.INSTALLED_CURRENT_LAYER; - assert dispatchTable.slots.length == vtableLength : Assertions.errorMessage(vTable, dispatchTable.slots); + assert dispatchTable.slots.length == vtable.length : Assertions.errorMessage(vtable, dispatchTable.slots); - int wordSize = ConfigurationValues.getTarget().wordSize; - for (int i = 0; i < vtableLength; i++) { - WordBase methodRef = (WordBase) Array.get(vTable, i); + for (int i = 0; i < vtable.length; i++) { var slot = dispatchTable.slots[i]; - if (methodRef instanceof MethodOffset methodOffset) { - int slotBit = hubInfo.vTableOffsetInHeapRelocs() / wordSize + i; - HostedMethod target = (HostedMethod) methodOffset.getMethod(); - if (target.isCompiledInPriorLayer()) { - /* - * Method compiled in the initial layer: we can use its offset without patching - * because it is relative to the initial layer's text section, which becomes the - * global code base. - */ - assert DynamicImageLayerInfo.getCurrentLayerNumber() == 1 : "Currently cannot patch references to code in a middle layer"; - } else if (target.isCompiled()) { - if (!buildingInitialLayer) { - /* - * Method compiled in the current (non-base) layer: the offset is relative - * to the current layer's text section and must be patched to account for - * its displacement from the global code base at runtime. - */ - offsetsToPatchInHeapRelocs.set(slotBit); - } - } else { + var prev = vtableWordToDispatchSlot.put(vtable[i], slot); + assert prev == null : prev; + } + } + + private void onPatchedWordWritten(WordBase word, int offsetInHeap, ImageHeapLayoutInfo heapLayout) { + if (word instanceof MethodOffset methodOffset) { + ResolvedJavaMethod method = methodOffset.getMethod(); + HostedMethod target = (method instanceof HostedMethod hm) ? hm : hUniverse.lookup(method); + if (target.isCompiledInPriorLayer()) { + /* + * Method compiled in the initial layer: we can use its offset without patching + * because it is relative to the initial layer's text section, which becomes the + * global code base. + */ + assert DynamicImageLayerInfo.getCurrentLayerNumber() == 1 : "Currently cannot patch references to code in a middle layer"; + } else if (target.isCompiled()) { + if (!buildingInitialLayer) { /* - * Method compiled in a future layer, so the target will be resolved to an - * address via a symbol reference by the runtime linker and we must subsequently - * patch the word to turn the address into an offset relative to the code base. + * Method compiled in the current (non-base) layer: the offset is relative to + * the current layer's text section and must be patched to account for its + * displacement from the global code base at runtime. */ - addressesToPatchInHeapRelocs.set(slotBit); + markRelocsWordInBitmap(offsetsToPatchInHeapRelocs, offsetInHeap, heapLayout); } + } else { + /* + * Method compiled in a future layer, so the target will be resolved to an address + * via a symbol reference by the runtime linker and we must subsequently patch the + * word to turn the address into an offset relative to the code base. + */ + markRelocsWordInBitmap(addressesToPatchInHeapRelocs, offsetInHeap, heapLayout); } - var prev = vtableWordToDispatchSlot.put(methodRef, slot); - assert prev == null : prev; } } + private void markRelocsWordInBitmap(BitSet bitmap, int offsetInHeap, ImageHeapLayoutInfo heapLayout) { + assert heapLayout.isReadOnlyRelocatable(offsetInHeap) : offsetInHeap; + int offsetInHeapRelocs = NumUtil.safeToInt(offsetInHeap - heapLayout.getReadOnlyRelocatableOffset()); + assert offsetInHeapRelocs % wordSize == 0 : offsetInHeap; + bitmap.set(offsetInHeapRelocs / wordSize); + } + private static String computeUnresolvedMethodSymbol(HostedDispatchSlot slotInfo, Map methodToSymbolMap) { /* * First try to determine the resolved method. This is useful for deduplication. @@ -594,7 +606,7 @@ public void defineDispatchTableSlotSymbols(ObjectFile objectFile, ObjectFile.Sec } // GR-58588 use injectedNotCompiled to track status of all MethodPointers - public String getSymbolName(WordBase methodRef, HostedMethod target, @SuppressWarnings("unused") boolean injectedNotCompiled) { + public String getSymbolName(MethodRef methodRef, HostedMethod target, @SuppressWarnings("unused") boolean injectedNotCompiled) { var slotInfo = vtableWordToDispatchSlot.get(methodRef); String symbol = NativeImage.localSymbolNameForMethod(target); if (slotInfo != null) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredDynamicHubFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredDynamicHubFeature.java index 4169e4518621..8cb840c5c30a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredDynamicHubFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredDynamicHubFeature.java @@ -42,6 +42,7 @@ import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter; import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton; import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags; +import com.oracle.svm.core.meta.MethodRef; import com.oracle.svm.hosted.FeatureImpl; import com.oracle.svm.hosted.meta.HostedType; import com.oracle.svm.util.LogUtils; @@ -62,12 +63,11 @@ public boolean isInConfiguration(Feature.IsInConfigurationAccess access) { @Override public void duringSetup(DuringSetupAccess access) { if (ImageLayerBuildingSupport.buildingSharedLayer()) { - LayeredImageHooks.registerDynamicHubWrittenCallback(this::onDynamicHubWritten); + LayeredImageHooks.singleton().registerDynamicHubWrittenCallback(this::onDynamicHubWritten); } } - void onDynamicHubWritten(LayeredImageHooks.WrittenDynamicHubInfo info) { - DynamicHub hub = info.hub(); + private void onDynamicHubWritten(DynamicHub hub, @SuppressWarnings("unused") MethodRef[] vTable) { if (hub.getArrayHub() == null) { DynamicHubMetadataTracking.singleton().recordMissingArrayHub(hub); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredImageHooks.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredImageHooks.java index b0075760d183..594ceff9ce9e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredImageHooks.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredImageHooks.java @@ -24,49 +24,69 @@ */ package com.oracle.svm.hosted.imagelayer; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Consumer; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature; +import org.graalvm.word.WordBase; -import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.image.ImageHeapLayoutInfo; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; import com.oracle.svm.core.layeredimagesingleton.FeatureSingleton; -import com.oracle.svm.hosted.meta.HostedUniverse; +import com.oracle.svm.core.meta.MethodRef; + +import jdk.graal.compiler.api.replacements.Fold; /** * Class containing hooks which can only be registered and executed during layered image builds. */ @AutomaticallyRegisteredFeature public class LayeredImageHooks implements InternalFeature, FeatureSingleton { - private final Set> hubWrittenCallbacks = ConcurrentHashMap.newKeySet(); + private final Set hubWrittenCallbacks = ConcurrentHashMap.newKeySet(); + private final Set patchedWordWrittenCallbacks = ConcurrentHashMap.newKeySet(); @Override public boolean isInConfiguration(Feature.IsInConfigurationAccess access) { return ImageLayerBuildingSupport.buildingImageLayer(); } - private static LayeredImageHooks singleton() { + @Fold + public static LayeredImageHooks singleton() { return ImageSingletons.lookup(LayeredImageHooks.class); } - public record WrittenDynamicHubInfo(DynamicHub hub, AnalysisUniverse aUniverse, HostedUniverse hUniverse, Object vTable, int vTableOffsetInHeapRelocs) { + @FunctionalInterface + public interface DynamicHubWrittenCallback { + void afterDynamicHubWritten(DynamicHub hub, MethodRef[] vtable); + } + + public void registerDynamicHubWrittenCallback(DynamicHubWrittenCallback callback) { + hubWrittenCallbacks.add(Objects.requireNonNull(callback)); + } + + public void processDynamicHubWritten(DynamicHub object, MethodRef[] vTable) { + for (var callback : hubWrittenCallbacks) { + callback.afterDynamicHubWritten(object, vTable); + } + } + + @FunctionalInterface + public interface PatchedWordWrittenCallback { + void afterPatchedWordWritten(WordBase word, int offsetInHeap, ImageHeapLayoutInfo heapLayout); } - /** - * Register a callback which will execute each time a new {@link DynamicHub} is installed in the - * image heap. - */ - public static void registerDynamicHubWrittenCallback(Consumer consumer) { - singleton().hubWrittenCallbacks.add(consumer); + public void registerPatchedWordWrittenCallback(PatchedWordWrittenCallback callback) { + patchedWordWrittenCallbacks.add(Objects.requireNonNull(callback)); } - public static void processWrittenDynamicHub(WrittenDynamicHubInfo info) { - singleton().hubWrittenCallbacks.forEach(callback -> callback.accept(info)); + public void processPatchedWordWritten(WordBase word, int offsetInHeap, ImageHeapLayoutInfo heapLayout) { + for (var callback : patchedWordWrittenCallbacks) { + callback.afterPatchedWordWritten(word, offsetInHeap, heapLayout); + } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerLoader.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerLoader.java index 8fcb44df7d7b..706293e6fb00 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerLoader.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerLoader.java @@ -87,7 +87,9 @@ import com.oracle.svm.core.graal.code.CGlobalDataInfo; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; +import com.oracle.svm.core.meta.MethodOffset; import com.oracle.svm.core.meta.MethodPointer; +import com.oracle.svm.core.meta.MethodRef; import com.oracle.svm.core.reflect.serialize.SerializationSupport; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl; @@ -1506,9 +1508,6 @@ private Object[] getReferencedValues(ImageHeapConstant parentConstant, StructLis case NULL_POINTER -> JavaConstant.NULL_POINTER; case NOT_MATERIALIZED -> unsupportedReferencedConstant("Reading the value of a base layer constant which was not materialized in the base image", parentConstant, finalPosition); - case METHOD_OFFSET -> - unsupportedReferencedConstant("Reading the value of a code offset constant in a base image, which is not supported. Offsets should be accessed via PersistedHostedMethod", - parentConstant, finalPosition); case PRIMITIVE_VALUE -> { PrimitiveValue.Reader pv = constantData.getPrimitiveValue(); yield JavaConstant.forPrimitive((char) pv.getTypeChar(), pv.getRawValue()); @@ -1533,12 +1532,18 @@ private static AnalysisFuture unsupportedReferencedConstant(String message, I } private boolean delegateProcessing(ConstantReference.Reader constantRef, Object[] values, int i) { - if (constantRef.isMethodPointer()) { + if (constantRef.isMethodPointer() || constantRef.isMethodOffset()) { AnalysisFuture task = new AnalysisFuture<>(() -> { - AnalysisType methodPointerType = metaAccess.lookupJavaType(MethodPointer.class); - int mid = constantRef.getMethodPointer().getMethodId(); - AnalysisMethod method = getAnalysisMethodForBaseLayerId(mid); - PatchedWordConstant constant = new PatchedWordConstant(new MethodPointer(method), methodPointerType); + MethodRef ref; + if (constantRef.isMethodPointer()) { + int mid = constantRef.getMethodPointer().getMethodId(); + ref = new MethodPointer(getAnalysisMethodForBaseLayerId(mid)); + } else { + int mid = constantRef.getMethodOffset().getMethodId(); + ref = new MethodOffset(getAnalysisMethodForBaseLayerId(mid)); + } + AnalysisType refType = metaAccess.lookupJavaType(ref.getClass()); + PatchedWordConstant constant = new PatchedWordConstant(ref, refType); values[i] = constant; return constant; }); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java index 0d470f5d6924..12167598d355 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java @@ -108,6 +108,7 @@ import com.oracle.svm.core.layeredimagesingleton.RuntimeOnlyWrapper; import com.oracle.svm.core.meta.MethodOffset; import com.oracle.svm.core.meta.MethodPointer; +import com.oracle.svm.core.meta.MethodRef; import com.oracle.svm.core.reflect.serialize.SerializationSupport; import com.oracle.svm.core.threadlocal.FastThreadLocal; import com.oracle.svm.core.util.VMError; @@ -959,16 +960,13 @@ private boolean maybeWriteConstant(JavaConstant constant, ConstantReference.Buil private static boolean delegateProcessing(ConstantReference.Builder builder, Object constant) { if (constant instanceof PatchedWordConstant patchedWordConstant) { WordBase word = patchedWordConstant.getWord(); - if (word instanceof MethodOffset) { - /* - * Such constants are not supposed to be used in another layer. Any method code - * offsets should be accessed via PersistedHostedMethod. - */ - builder.setMethodOffset(Void.VOID); - return true; - } else if (word instanceof MethodPointer methodPointer) { - AnalysisMethod method = getRelocatableConstantMethod(methodPointer); - builder.initMethodPointer().setMethodId(method.getId()); + if (word instanceof MethodRef methodRef) { + AnalysisMethod method = getRelocatableConstantMethod(methodRef); + switch (methodRef) { + case MethodOffset mo -> builder.initMethodOffset().setMethodId(method.getId()); + case MethodPointer mp -> builder.initMethodPointer().setMethodId(method.getId()); + default -> throw VMError.shouldNotReachHere("Unsupported method ref: " + methodRef); + } return true; } else if (word instanceof CEntryPointLiteralCodePointer cp) { CEntryPointLiteralReference.Builder b = builder.initCEntryPointLiteralCodePointer(); @@ -1010,14 +1008,14 @@ private void scanConstantReferencedObjects(ImageHeapConstant constant, IntFuncti discoveredConstants.add(con); constantsMap.put(con, parent); - } else if (obj instanceof MethodPointer mp) { - getRelocatableConstantMethod(mp).registerAsTrackedAcrossLayers("In method pointer"); + } else if (obj instanceof MethodRef mr) { + getRelocatableConstantMethod(mr).registerAsTrackedAcrossLayers("In method ref"); } } } - private static AnalysisMethod getRelocatableConstantMethod(MethodPointer methodPointer) { - ResolvedJavaMethod method = methodPointer.getMethod(); + private static AnalysisMethod getRelocatableConstantMethod(MethodRef methodRef) { + ResolvedJavaMethod method = methodRef.getMethod(); if (method instanceof HostedMethod hostedMethod) { return hostedMethod.wrapped; } else { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java index a83fe5d68ec3..710984b99ec6 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java @@ -2453,9 +2453,9 @@ public Which which() { case 2 : return Which.NOT_MATERIALIZED; case 3 : return Which.PRIMITIVE_VALUE; case 4 : return Which.METHOD_POINTER; - case 5 : return Which.C_ENTRY_POINT_LITERAL_CODE_POINTER; - case 6 : return Which.C_GLOBAL_DATA_BASE_POINTER; - case 7 : return Which.METHOD_OFFSET; + case 5 : return Which.METHOD_OFFSET; + case 6 : return Which.C_ENTRY_POINT_LITERAL_CODE_POINTER; + case 7 : return Which.C_GLOBAL_DATA_BASE_POINTER; default: return Which._NOT_IN_SCHEMA; } } @@ -2526,6 +2526,18 @@ public final MethodPointer.Builder initMethodPointer() { return new ConstantReference.MethodPointer.Builder(segment, data, pointers, dataSize, pointerCount); } + public final boolean isMethodOffset() { + return which() == ConstantReference.Which.METHOD_OFFSET; + } + public final MethodOffset.Builder getMethodOffset() { + return new ConstantReference.MethodOffset.Builder(segment, data, pointers, dataSize, pointerCount); + } + public final MethodOffset.Builder initMethodOffset() { + _setShortField(2, (short)ConstantReference.Which.METHOD_OFFSET.ordinal()); + _setIntField(0,0); + return new ConstantReference.MethodOffset.Builder(segment, data, pointers, dataSize, pointerCount); + } + public final boolean isCEntryPointLiteralCodePointer() { return which() == ConstantReference.Which.C_ENTRY_POINT_LITERAL_CODE_POINTER; } @@ -2554,18 +2566,6 @@ public final void setCGlobalDataBasePointer(com.oracle.svm.shaded.org.capnproto. _setShortField(2, (short)ConstantReference.Which.C_GLOBAL_DATA_BASE_POINTER.ordinal()); } - public final boolean isMethodOffset() { - return which() == ConstantReference.Which.METHOD_OFFSET; - } - public final com.oracle.svm.shaded.org.capnproto.Void getMethodOffset() { - assert which() == ConstantReference.Which.METHOD_OFFSET: - "Must check which() before get()ing a union member."; - return com.oracle.svm.shaded.org.capnproto.Void.VOID; - } - public final void setMethodOffset(com.oracle.svm.shaded.org.capnproto.Void value) { - _setShortField(2, (short)ConstantReference.Which.METHOD_OFFSET.ordinal()); - } - } public static final class Reader extends com.oracle.svm.shaded.org.capnproto.StructReader { @@ -2580,9 +2580,9 @@ public Which which() { case 2 : return Which.NOT_MATERIALIZED; case 3 : return Which.PRIMITIVE_VALUE; case 4 : return Which.METHOD_POINTER; - case 5 : return Which.C_ENTRY_POINT_LITERAL_CODE_POINTER; - case 6 : return Which.C_GLOBAL_DATA_BASE_POINTER; - case 7 : return Which.METHOD_OFFSET; + case 5 : return Which.METHOD_OFFSET; + case 6 : return Which.C_ENTRY_POINT_LITERAL_CODE_POINTER; + case 7 : return Which.C_GLOBAL_DATA_BASE_POINTER; default: return Which._NOT_IN_SCHEMA; } } @@ -2630,6 +2630,13 @@ public MethodPointer.Reader getMethodPointer() { return new ConstantReference.MethodPointer.Reader(segment, data, pointers, dataSize, pointerCount, nestingLimit); } + public final boolean isMethodOffset() { + return which() == ConstantReference.Which.METHOD_OFFSET; + } + public MethodOffset.Reader getMethodOffset() { + return new ConstantReference.MethodOffset.Reader(segment, data, pointers, dataSize, pointerCount, nestingLimit); + } + public final boolean isCEntryPointLiteralCodePointer() { return which() == ConstantReference.Which.C_ENTRY_POINT_LITERAL_CODE_POINTER; } @@ -2651,15 +2658,6 @@ assert which() == ConstantReference.Which.C_GLOBAL_DATA_BASE_POINTER: return com.oracle.svm.shaded.org.capnproto.Void.VOID; } - public final boolean isMethodOffset() { - return which() == ConstantReference.Which.METHOD_OFFSET; - } - public final com.oracle.svm.shaded.org.capnproto.Void getMethodOffset() { - assert which() == ConstantReference.Which.METHOD_OFFSET: - "Must check which() before get()ing a union member."; - return com.oracle.svm.shaded.org.capnproto.Void.VOID; - } - } public enum Which { @@ -2668,9 +2666,9 @@ public enum Which { NOT_MATERIALIZED, PRIMITIVE_VALUE, METHOD_POINTER, + METHOD_OFFSET, C_ENTRY_POINT_LITERAL_CODE_POINTER, C_GLOBAL_DATA_BASE_POINTER, - METHOD_OFFSET, _NOT_IN_SCHEMA, } public static class ObjectConstant { @@ -2775,6 +2773,57 @@ public final int getMethodId() { } + public static class MethodOffset { + public static final com.oracle.svm.shaded.org.capnproto.StructSize STRUCT_SIZE = new com.oracle.svm.shaded.org.capnproto.StructSize((short)1,(short)1); + public static final class Factory extends com.oracle.svm.shaded.org.capnproto.StructFactory { + public Factory() { + } + public final Reader constructReader(com.oracle.svm.shaded.org.capnproto.SegmentReader segment, int data,int pointers, int dataSize, short pointerCount, int nestingLimit) { + return new Reader(segment,data,pointers,dataSize,pointerCount,nestingLimit); + } + public final Builder constructBuilder(com.oracle.svm.shaded.org.capnproto.SegmentBuilder segment, int data,int pointers, int dataSize, short pointerCount) { + return new Builder(segment, data, pointers, dataSize, pointerCount); + } + public final com.oracle.svm.shaded.org.capnproto.StructSize structSize() { + return ConstantReference.MethodOffset.STRUCT_SIZE; + } + public final Reader asReader(Builder builder) { + return builder.asReader(); + } + } + public static final Factory factory = new Factory(); + public static final com.oracle.svm.shaded.org.capnproto.StructList.Factory listFactory = + new com.oracle.svm.shaded.org.capnproto.StructList.Factory(factory); + public static final class Builder extends com.oracle.svm.shaded.org.capnproto.StructBuilder { + Builder(com.oracle.svm.shaded.org.capnproto.SegmentBuilder segment, int data, int pointers,int dataSize, short pointerCount){ + super(segment, data, pointers, dataSize, pointerCount); + } + public final Reader asReader() { + return new Reader(segment, data, pointers, dataSize, pointerCount, 0x7fffffff); + } + public final int getMethodId() { + return _getIntField(0); + } + public final void setMethodId(int value) { + _setIntField(0, value); + } + + } + + public static final class Reader extends com.oracle.svm.shaded.org.capnproto.StructReader { + Reader(com.oracle.svm.shaded.org.capnproto.SegmentReader segment, int data, int pointers,int dataSize, short pointerCount, int nestingLimit){ + super(segment, data, pointers, dataSize, pointerCount, nestingLimit); + } + + public final int getMethodId() { + return _getIntField(0); + } + + } + + } + + } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/SharedConstantFieldProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/SharedConstantFieldProvider.java index add18dc9504d..a8c3717aa1d2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/SharedConstantFieldProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/SharedConstantFieldProvider.java @@ -31,8 +31,7 @@ import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.svm.core.graal.code.CGlobalDataBasePointer; -import com.oracle.svm.core.meta.MethodOffset; -import com.oracle.svm.core.meta.MethodPointer; +import com.oracle.svm.core.meta.MethodRef; import com.oracle.svm.hosted.SVMHost; import com.oracle.svm.hosted.ameta.FieldValueInterceptionSupport; @@ -123,8 +122,7 @@ protected boolean isClassInitialized(ResolvedJavaField field) { @Override protected boolean isFinalFieldValueConstant(ResolvedJavaField field, JavaConstant value, ConstantFieldTool tool) { - if (value.getJavaKind() == JavaKind.Object && (metaAccess.isInstanceOf(value, MethodPointer.class) || - metaAccess.isInstanceOf(value, MethodOffset.class) || metaAccess.isInstanceOf(value, CGlobalDataBasePointer.class))) { + if (value.getJavaKind() == JavaKind.Object && (metaAccess.isInstanceOf(value, MethodRef.class) || metaAccess.isInstanceOf(value, CGlobalDataBasePointer.class))) { /* * Prevent constant folding of placeholder objects for patched words (such as relocated * pointers). These are "hosted" types and so cannot be present in compiler graphs. diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java index b4e494c7db7d..3a8074e5994f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java @@ -49,7 +49,6 @@ import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.function.CEntryPointLiteral; import org.graalvm.nativeimage.c.function.CFunction; -import org.graalvm.word.WordBase; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.constraints.UnsupportedFeatures; @@ -91,6 +90,7 @@ import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; import com.oracle.svm.core.meta.MethodOffset; import com.oracle.svm.core.meta.MethodPointer; +import com.oracle.svm.core.meta.MethodRef; import com.oracle.svm.core.reflect.SubstrateConstructorAccessor; import com.oracle.svm.core.reflect.SubstrateMethodAccessor; import com.oracle.svm.core.util.VMError; @@ -123,7 +123,7 @@ public class UniverseBuilder { @Platforms(Platform.HOSTED_ONLY.class) // - private static final WordBase[] EMPTY_VTABLE = new WordBase[0]; + private static final MethodRef[] EMPTY_VTABLE = new MethodRef[0]; private final AnalysisUniverse aUniverse; private final AnalysisMetaAccess aMetaAccess; @@ -921,6 +921,8 @@ private void buildHubs() { ObjectLayout ol = ConfigurationValues.getObjectLayout(); DynamicHubLayout dynamicHubLayout = DynamicHubLayout.singleton(); + boolean closedTypeWorldHubLayout = SubstrateOptions.useClosedTypeWorldHubLayout(); + boolean useOffsets = SubstrateOptions.useRelativeCodePointers(); for (HostedType type : hUniverse.getTypes()) { hUniverse.hostVM().recordActivity(); @@ -972,8 +974,8 @@ private void buildHubs() { DynamicHub hub = type.getHub(); hub.setSharedData(layoutHelper, monitorOffset, identityHashOffset, referenceMapIndex, type.isInstantiated()); - if (SubstrateOptions.useClosedTypeWorldHubLayout()) { - WordBase[] vtable = createVTable(type.closedTypeWorldVTable); + if (closedTypeWorldHubLayout) { + MethodRef[] vtable = createVTable(type.closedTypeWorldVTable, useOffsets); hub.setClosedTypeWorldData(vtable, type.getTypeID(), type.getTypeCheckStart(), type.getTypeCheckRange(), type.getTypeCheckSlot(), type.getClosedTypeWorldTypeCheckSlots()); } else { @@ -1005,20 +1007,20 @@ private void buildHubs() { typeSlotIdx += 2; } - WordBase[] vtable = createVTable(type.openTypeWorldDispatchTables); + MethodRef[] vtable = createVTable(type.openTypeWorldDispatchTables, useOffsets); hub.setOpenTypeWorldData(vtable, type.getTypeID(), type.getTypeIDDepth(), type.getNumClassTypes(), type.getNumInterfaceTypes(), openTypeWorldTypeCheckSlots); } } } - private static WordBase[] createVTable(HostedMethod[] methods) { + private static MethodRef[] createVTable(HostedMethod[] methods, boolean useOffsets) { if (methods.length == 0) { return EMPTY_VTABLE; } - WordBase[] vtable = new WordBase[methods.length]; + MethodRef[] vtable = new MethodRef[methods.length]; for (int i = 0; i < methods.length; i++) { HostedMethod method = methods[i]; - if (SubstrateOptions.useRelativeCodePointers()) { + if (useOffsets) { vtable[i] = new MethodOffset(method); } else { /* diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java index 0e2b6a458173..276580157ec1 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java @@ -66,7 +66,9 @@ import com.oracle.svm.core.hub.ClassForNameSupport; import com.oracle.svm.core.hub.ClassForNameSupportFeature; import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.meta.MethodOffset; import com.oracle.svm.core.meta.MethodPointer; +import com.oracle.svm.core.meta.MethodRef; import com.oracle.svm.core.reflect.ReflectionAccessorHolder; import com.oracle.svm.core.reflect.SubstrateAccessor; import com.oracle.svm.core.reflect.SubstrateConstructorAccessor; @@ -130,7 +132,7 @@ private record AccessorKey(Executable member, Class targetClass) { } final Map accessors = new ConcurrentHashMap<>(); - private final Map expandSignatureMethods = new ConcurrentHashMap<>(); + private final Map expandSignatureMethods = new ConcurrentHashMap<>(); private static final Method invokePrototype = ReflectionUtil.lookupMethod(ReflectionAccessorHolder.class, "invokePrototype", Object.class, Object[].class, CFunctionPointer.class); @@ -182,8 +184,8 @@ public SubstrateAccessor getOrCreateConstructorAccessor(Class targetClass, Ex private SubstrateAccessor createAccessor(AccessorKey key) { Executable member = key.member; Class targetClass = key.targetClass; - MethodPointer expandSignature; - MethodPointer directTarget = null; + MethodRef expandSignature; + MethodRef directTarget = null; AnalysisMethod targetMethod = null; DynamicHub initializeBeforeInvoke = null; if (member instanceof Method) { @@ -193,7 +195,7 @@ private SubstrateAccessor createAccessor(AccessorKey key) { if (member.getDeclaringClass() == MethodHandle.class && (member.getName().equals("invoke") || member.getName().equals("invokeExact"))) { /* Method handles must not be invoked via reflection. */ - expandSignature = asMethodPointer(analysisAccess.getMetaAccess().lookupJavaMethod(methodHandleInvokeErrorMethod)); + expandSignature = asMethodRef(analysisAccess.getMetaAccess().lookupJavaMethod(methodHandleInvokeErrorMethod)); } else { Method target = (Method) member; try { @@ -213,7 +215,7 @@ private SubstrateAccessor createAccessor(AccessorKey key) { * both a directTarget and a vtableIndex. */ if (!targetMethod.isAbstract()) { - directTarget = asMethodPointer(targetMethod); + directTarget = asMethodRef(targetMethod); } if (!targetMethod.canBeStaticallyBound()) { vtableIndex = SubstrateMethodAccessor.VTABLE_INDEX_NOT_YET_COMPUTED; @@ -233,7 +235,7 @@ private SubstrateAccessor createAccessor(AccessorKey key) { } else { Class holder = targetClass; - CFunctionPointer factoryMethodTarget = null; + MethodRef factoryMethodTarget = null; ResolvedJavaMethod factoryMethod = null; if (Modifier.isAbstract(holder.getModifiers()) || holder.isInterface() || holder.isPrimitive() || holder.isArray()) { /* @@ -242,14 +244,14 @@ private SubstrateAccessor createAccessor(AccessorKey key) { * an interface, array, or primitive type, but we are defensive and throw the * exception in that case too. */ - expandSignature = asMethodPointer(analysisAccess.getMetaAccess().lookupJavaMethod(newInstanceErrorMethod)); + expandSignature = asMethodRef(analysisAccess.getMetaAccess().lookupJavaMethod(newInstanceErrorMethod)); } else { expandSignature = createExpandSignatureMethod(member, false); targetMethod = analysisAccess.getMetaAccess().lookupJavaMethod(member); var aTargetClass = analysisAccess.getMetaAccess().lookupJavaType(targetClass); - directTarget = asMethodPointer(targetMethod); + directTarget = asMethodRef(targetMethod); factoryMethod = FactoryMethodSupport.singleton().lookup(analysisAccess.getMetaAccess(), targetMethod, aTargetClass, false); - factoryMethodTarget = asMethodPointer(factoryMethod); + factoryMethodTarget = asMethodRef(factoryMethod); if (!targetMethod.getDeclaringClass().isInitialized()) { initializeBeforeInvoke = analysisAccess.getHostVM().dynamicHub(targetMethod.getDeclaringClass()); } @@ -258,17 +260,21 @@ private SubstrateAccessor createAccessor(AccessorKey key) { } } - private MethodPointer createExpandSignatureMethod(Executable member, boolean callerSensitiveAdapter) { + private MethodRef createExpandSignatureMethod(Executable member, boolean callerSensitiveAdapter) { return expandSignatureMethods.computeIfAbsent(new SignatureKey(member, callerSensitiveAdapter), signatureKey -> { ResolvedJavaMethod prototype = analysisAccess.getMetaAccess().lookupJavaMethod(callerSensitiveAdapter ? invokePrototypeForCallerSensitiveAdapter : invokePrototype).getWrapped(); - return asMethodPointer(new ReflectionExpandSignatureMethod("invoke_" + signatureKey.uniqueShortName(), prototype, signatureKey.isStatic, signatureKey.argTypes, signatureKey.returnKind, + return asMethodRef(new ReflectionExpandSignatureMethod("invoke_" + signatureKey.uniqueShortName(), prototype, signatureKey.isStatic, signatureKey.argTypes, signatureKey.returnKind, signatureKey.callerSensitiveAdapter, member)); }); } - private MethodPointer asMethodPointer(ResolvedJavaMethod method) { - AnalysisMethod aMethod = method instanceof AnalysisMethod ? (AnalysisMethod) method : analysisAccess.getUniverse().lookup(method); - return new MethodPointer(aMethod); + private MethodRef asMethodRef(ResolvedJavaMethod method) { + AnalysisMethod aMethod = (method instanceof AnalysisMethod) ? (AnalysisMethod) method : analysisAccess.getUniverse().lookup(method); + if (SubstrateOptions.useRelativeCodePointers()) { + return new MethodOffset(aMethod); + } else { + return new MethodPointer(aMethod); + } } @Override @@ -335,7 +341,7 @@ public void duringSetup(DuringSetupAccess a) { private static void onAccessorReachable(DuringAnalysisAccess a, SubstrateAccessor accessor, ObjectScanner.ScanReason reason) { DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; - ResolvedJavaMethod expandSignatureMethod = ((MethodPointer) accessor.getExpandSignature()).getMethod(); + ResolvedJavaMethod expandSignatureMethod = accessor.getExpandSignatureMethod(); access.registerAsRoot((AnalysisMethod) expandSignatureMethod, true, reason); ResolvedJavaMethod targetMethod = accessor.getTargetMethod(); diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/InterpreterToVM.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/InterpreterToVM.java index 767b699d23d6..52b5a2d69d4e 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/InterpreterToVM.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/InterpreterToVM.java @@ -37,7 +37,7 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.MissingReflectionRegistrationError; import org.graalvm.nativeimage.c.function.CFunctionPointer; -import org.graalvm.word.UnsignedWord; +import org.graalvm.word.Pointer; import org.graalvm.word.WordBase; import com.oracle.svm.core.SubstrateOptions; @@ -48,6 +48,7 @@ import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.RuntimeClassLoading; import com.oracle.svm.core.jdk.InternalVMMethod; +import com.oracle.svm.core.meta.MethodRef; import com.oracle.svm.core.monitor.MonitorInflationCause; import com.oracle.svm.core.monitor.MonitorSupport; import com.oracle.svm.core.snippets.KnownIntrinsics; @@ -693,14 +694,14 @@ static CFunctionPointer peekAtSVMVTable(Class seedClass, Class thisClass, vtableOffset += (int) OpenTypeWorldDispatchTableSnippets.determineITableStartingOffset(thisHub, seedHub.getTypeID()); } } - WordBase vtableEntry = Word.objectToTrackedPointer(thisHub).readWord(vtableOffset); + MethodRef vtableEntry = Word.objectToTrackedPointer(thisHub).readWord(vtableOffset); return getSVMVTableCodePointer(vtableEntry); } - private static CFunctionPointer getSVMVTableCodePointer(WordBase vtableEntry) { - WordBase codePointer = vtableEntry; + private static CFunctionPointer getSVMVTableCodePointer(MethodRef vtableEntry) { + Pointer codePointer = (Pointer) vtableEntry; if (SubstrateOptions.useRelativeCodePointers()) { - codePointer = KnownIntrinsics.codeBase().add((UnsignedWord) codePointer); + codePointer = codePointer.add(KnownIntrinsics.codeBase()); } return (CFunctionPointer) codePointer; } diff --git a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasmgc/codegen/WasmGCHeapWriter.java b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasmgc/codegen/WasmGCHeapWriter.java index fd1581ac0198..da17bccc3f85 100644 --- a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasmgc/codegen/WasmGCHeapWriter.java +++ b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasmgc/codegen/WasmGCHeapWriter.java @@ -39,8 +39,6 @@ import java.util.function.Consumer; import java.util.stream.Stream; -import org.graalvm.word.WordBase; - import com.oracle.graal.pointsto.heap.ImageHeapArray; import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.graal.pointsto.heap.ImageHeapInstance; @@ -51,6 +49,7 @@ import com.oracle.svm.core.hub.Hybrid; import com.oracle.svm.core.image.ImageHeapLayouter.ImageHeapLayouterCallback; import com.oracle.svm.core.meta.MethodPointer; +import com.oracle.svm.core.meta.MethodRef; import com.oracle.svm.core.meta.SubstrateMethodPointerConstant; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.config.DynamicHubLayout; @@ -836,7 +835,7 @@ private Instruction createAccessDispatchArray(List allFields, Consu private Instruction createHubVtableArray(ImageHeapInstance instance) { WasmId.ArrayType vtableFieldType = providers.knownIds().vtableFieldType; DynamicHubLayout dynamicHubLayout = DynamicHubLayout.singleton(); - WordBase[] vtable = (WordBase[]) heap.readInlinedField(dynamicHubLayout.vTableField, instance); + MethodRef[] vtable = (MethodRef[]) heap.readInlinedField(dynamicHubLayout.vTableField, instance); int vtableLength = vtable.length; diff --git a/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/WebImageInvalidMethodPointerHandler.java b/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/WebImageInvalidMethodPointerHandler.java index 0235a84eed8a..bbf577f2aed9 100644 --- a/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/WebImageInvalidMethodPointerHandler.java +++ b/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/WebImageInvalidMethodPointerHandler.java @@ -37,6 +37,11 @@ @TargetClass(InvalidMethodPointerHandler.class) public final class WebImageInvalidMethodPointerHandler { + @Substitute + private static void invalidCodeAddressHandler() { + throw new RuntimeException("invalid address"); + } + @Substitute private static void invalidVTableEntryHandler() { throw new RuntimeException("invalid VTable entry");