Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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();
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1252,6 +1252,10 @@ protected void onValueUpdate(EconomicMap<OptionKey<?>, 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<Boolean> 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.")//
Expand Down Expand Up @@ -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<Boolean> RelativeCodePointers = new HostedOptionKey<>(false, SubstrateOptions::validateRelativeCodePointers);

@Fold
public static boolean useRelativeCodePointers() {
return RelativeCodePointers.getValue();
return ConcealedOptions.RelativeCodePointers.getValue();
}

private static void validateRelativeCodePointers(HostedOptionKey<Boolean> optionKey) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -50,6 +48,7 @@ public MethodPointer(ResolvedJavaMethod method) {
this(method, true);
}

@Override
public ResolvedJavaMethod getMethod() {
return method;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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();

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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
Expand All @@ -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;
Expand All @@ -84,20 +88,38 @@ 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)
public ResolvedJavaMethod getTargetMethod() {
return targetMethod;
}

@SuppressWarnings("unchecked")
protected static <T extends CFunctionPointer> 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 extends CFunctionPointer> T getDirectTarget() {
return getCodePointer(directTarget);
}

protected final <T extends CFunctionPointer> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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
Expand Down
Loading