Skip to content

Commit 5b3ee38

Browse files
committed
[GR-64831] Base-relative code pointers in reflection accessors.
PullRequest: graal/20747
2 parents ce375c5 + 0d71b85 commit 5b3ee38

32 files changed

+527
-322
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/InvalidMethodPointerHandler.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@
5050
* the stubs provide full diagnostic output with a stack trace.
5151
*/
5252
public final class InvalidMethodPointerHandler {
53+
@Platforms(Platform.HOSTED_ONLY.class) //
54+
public static final Method INVALID_CODE_ADDRESS_HANDLER_METHOD = ReflectionUtil.lookupMethod(InvalidMethodPointerHandler.class, "invalidCodeAddressHandler");
55+
public static final String INVALID_CODE_ADDRESS_MSG = "Fatal error: The invoked code address is invalid and not supposed to be called";
56+
5357
@Platforms(Platform.HOSTED_ONLY.class) //
5458
public static final Method INVALID_VTABLE_ENTRY_HANDLER_METHOD = ReflectionUtil.lookupMethod(InvalidMethodPointerHandler.class, "invalidVTableEntryHandler");
5559
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 {
5862
public static final Method METHOD_POINTER_NOT_COMPILED_HANDLER_METHOD = ReflectionUtil.lookupMethod(InvalidMethodPointerHandler.class, "methodPointerNotCompiledHandler");
5963
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";
6064

65+
/**
66+
* This method is a placeholder that is put at the beginning of the code section, so that code
67+
* offset 0 and the resulting address become invalid and can be tested for as such. For this to
68+
* work, this method should never be intentionally called or referenced anywhere.
69+
*/
70+
@StubCallingConvention
71+
@NeverInline("We need a separate frame that stores all registers")
72+
@Uninterruptible(reason = "Precaution.")
73+
private static void invalidCodeAddressHandler() {
74+
Pointer callerSP = KnownIntrinsics.readCallerStackPointer();
75+
CodePointer callerIP = KnownIntrinsics.readReturnAddress();
76+
failFatally(callerSP, callerIP, INVALID_CODE_ADDRESS_MSG);
77+
}
78+
6179
@StubCallingConvention
6280
@NeverInline("We need a separate frame that stores all registers")
81+
@Uninterruptible(reason = "Precaution.")
6382
private static void invalidVTableEntryHandler() {
6483
Pointer callerSP = KnownIntrinsics.readCallerStackPointer();
6584
CodePointer callerIP = KnownIntrinsics.readReturnAddress();
@@ -68,6 +87,7 @@ private static void invalidVTableEntryHandler() {
6887

6988
@StubCallingConvention
7089
@NeverInline("We need a separate frame that stores all registers")
90+
@Uninterruptible(reason = "Precaution.")
7191
private static void methodPointerNotCompiledHandler() {
7292
Pointer callerSP = KnownIntrinsics.readCallerStackPointer();
7393
CodePointer callerIP = KnownIntrinsics.readReturnAddress();

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateDiagnostics.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -892,7 +892,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev
892892
log.string("Runtime information:").indent(true);
893893
log.string("Isolate id: ").signed(Isolates.getIsolateId()).newline();
894894
log.string("Heap base: ").zhex(KnownIntrinsics.heapBase()).newline();
895-
if (SubstrateOptions.RelativeCodePointers.getValue()) {
895+
if (SubstrateOptions.useRelativeCodePointers()) {
896896
log.string("Code base: ").zhex(KnownIntrinsics.codeBase()).newline();
897897
}
898898
log.string("CGlobalData base: ").zhex(CGlobalDataInfo.CGLOBALDATA_RUNTIME_BASE_ADDRESS.getPointer()).newline();
@@ -1146,7 +1146,7 @@ private static final class ImageCodeLocationInfoPrinter {
11461146
* NOTE: this method may only be called by a single thread.
11471147
*/
11481148
public boolean printLocationInfo(Log log, UnsignedWord value) {
1149-
if (SubstrateOptions.RelativeCodePointers.getValue() && KnownIntrinsics.codeBase().equal(value)) {
1149+
if (SubstrateOptions.useRelativeCodePointers() && KnownIntrinsics.codeBase().equal(value)) {
11501150
log.string("is the code base");
11511151
return true;
11521152
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,6 +1252,10 @@ protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Integer o
12521252
throw UserError.invalidOptionValue(key, key.getValue(), "Dumping runtime compiled code is not supported on Windows.");
12531253
}
12541254
});
1255+
1256+
@Option(help = "Avoid linker relocations for code and instead emit address computations.", type = OptionType.Expert) //
1257+
@LayerVerifiedOption(severity = Severity.Error, kind = Kind.Changed, positional = false) //
1258+
public static final HostedOptionKey<Boolean> RelativeCodePointers = new HostedOptionKey<>(false, SubstrateOptions::validateRelativeCodePointers);
12551259
}
12561260

12571261
@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() {
15471551
return PrintClosedArenaUponThrow.getValue();
15481552
}
15491553

1550-
@Option(help = "Avoid linker relocations for code and instead emit address computations.", type = OptionType.Expert) //
1551-
@LayerVerifiedOption(severity = Severity.Error, kind = Kind.Changed, positional = false) //
1552-
public static final HostedOptionKey<Boolean> RelativeCodePointers = new HostedOptionKey<>(false, SubstrateOptions::validateRelativeCodePointers);
1553-
15541554
@Fold
15551555
public static boolean useRelativeCodePointers() {
1556-
return RelativeCodePointers.getValue();
1556+
return ConcealedOptions.RelativeCodePointers.getValue();
15571557
}
15581558

15591559
private static void validateRelativeCodePointers(HostedOptionKey<Boolean> optionKey) {

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@
9090
import org.graalvm.nativeimage.ImageSingletons;
9191
import org.graalvm.nativeimage.Platform;
9292
import org.graalvm.nativeimage.Platforms;
93-
import org.graalvm.word.WordBase;
9493

9594
import com.oracle.svm.configure.config.ConfigurationType;
9695
import com.oracle.svm.configure.config.SignatureUtil;
@@ -124,6 +123,7 @@
124123
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
125124
import com.oracle.svm.core.jdk.ProtectionDomainSupport;
126125
import com.oracle.svm.core.jdk.Resources;
126+
import com.oracle.svm.core.meta.MethodRef;
127127
import com.oracle.svm.core.meta.SharedType;
128128
import com.oracle.svm.core.metadata.MetadataTracer;
129129
import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils;
@@ -365,7 +365,7 @@ public final class DynamicHub implements AnnotatedElement, java.lang.reflect.Typ
365365
private final byte layerId;
366366

367367
@UnknownObjectField(availability = AfterHostedUniverse.class)//
368-
private WordBase[] vtable;
368+
private MethodRef[] vtable;
369369

370370
private final DynamicHubCompanion companion;
371371

@@ -650,7 +650,7 @@ public void setSharedData(int layoutEncoding, int monitorOffset, int identityHas
650650
}
651651

652652
@Platforms(Platform.HOSTED_ONLY.class)
653-
public void setClosedTypeWorldData(WordBase[] vtable, int typeID, short typeCheckStart, short typeCheckRange, short typeCheckSlot, short[] typeCheckSlots) {
653+
public void setClosedTypeWorldData(MethodRef[] vtable, int typeID, short typeCheckStart, short typeCheckRange, short typeCheckSlot, short[] typeCheckSlots) {
654654
assert this.vtable == null : "Initialization must be called only once";
655655

656656
this.typeID = typeID;
@@ -662,7 +662,7 @@ public void setClosedTypeWorldData(WordBase[] vtable, int typeID, short typeChec
662662
}
663663

664664
@Platforms(Platform.HOSTED_ONLY.class)
665-
public void setOpenTypeWorldData(WordBase[] vtable, int typeID, int typeCheckDepth, int numClassTypes, int numInterfaceTypes, int[] typeCheckSlots) {
665+
public void setOpenTypeWorldData(MethodRef[] vtable, int typeID, int typeCheckDepth, int numClassTypes, int numInterfaceTypes, int[] typeCheckSlots) {
666666
assert this.vtable == null : "Initialization must be called only once";
667667

668668
this.typeID = typeID;

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/MethodOffset.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,23 @@
2828

2929
import java.util.Objects;
3030

31-
import org.graalvm.word.WordBase;
32-
31+
import com.oracle.svm.core.snippets.KnownIntrinsics;
3332
import com.oracle.svm.core.util.VMError;
3433

3534
import jdk.vm.ci.meta.ResolvedJavaMethod;
3635

37-
/** The offset of the compiled code of a method from the code base. */
38-
public final class MethodOffset implements WordBase {
36+
/**
37+
* The offset of the compiled code of a method from the {@linkplain KnownIntrinsics#codeBase() code
38+
* base}.
39+
*/
40+
public final class MethodOffset implements MethodRef {
3941
private final ResolvedJavaMethod method;
4042

4143
public MethodOffset(ResolvedJavaMethod method) {
4244
this.method = Objects.requireNonNull(method);
4345
}
4446

47+
@Override
4548
public ResolvedJavaMethod getMethod() {
4649
return method;
4750
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/MethodPointer.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,8 @@
3333

3434
import jdk.vm.ci.meta.ResolvedJavaMethod;
3535

36-
/**
37-
* A pointer to the compiled code of a method.
38-
*/
39-
public final class MethodPointer implements CFunctionPointer {
36+
/** The absolute address of the compiled code of a method. */
37+
public final class MethodPointer implements CFunctionPointer, MethodRef {
4038
private final ResolvedJavaMethod method;
4139
private final boolean permitsRewriteToPLT;
4240

@@ -50,6 +48,7 @@ public MethodPointer(ResolvedJavaMethod method) {
5048
this(method, true);
5149
}
5250

51+
@Override
5352
public ResolvedJavaMethod getMethod() {
5453
return method;
5554
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core.meta;
26+
27+
import org.graalvm.nativeimage.Platform;
28+
import org.graalvm.nativeimage.Platforms;
29+
import org.graalvm.word.WordBase;
30+
31+
import com.oracle.svm.core.hub.DynamicHub;
32+
33+
import jdk.vm.ci.meta.ResolvedJavaMethod;
34+
35+
/**
36+
* A reference to a {@linkplain ResolvedJavaMethod method}. Subtypes are instantiated for specific
37+
* target methods during the image build and embedded in image heap objects, for example, in
38+
* dispatch tables of {@link DynamicHub} objects. For the image, the references are turned into
39+
* addresses or offsets which can be used to invoke the target method.
40+
*/
41+
public interface MethodRef extends WordBase {
42+
43+
@Platforms(Platform.HOSTED_ONLY.class)
44+
ResolvedJavaMethod getMethod();
45+
46+
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateAccessor.java

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,13 @@
2929
import org.graalvm.nativeimage.Platform;
3030
import org.graalvm.nativeimage.Platforms;
3131
import org.graalvm.nativeimage.c.function.CFunctionPointer;
32+
import org.graalvm.word.Pointer;
3233

34+
import com.oracle.svm.core.SubstrateOptions;
3335
import com.oracle.svm.core.hub.DynamicHub;
3436
import com.oracle.svm.core.jdk.InternalVMMethod;
37+
import com.oracle.svm.core.meta.MethodRef;
38+
import com.oracle.svm.core.snippets.KnownIntrinsics;
3539

3640
import jdk.vm.ci.meta.ResolvedJavaMethod;
3741

@@ -57,13 +61,13 @@ public abstract class SubstrateAccessor {
5761
* The first-level function that is invoked. It expands the boxed Object[] signature to the
5862
* expanded real signature.
5963
*/
60-
final CFunctionPointer expandSignature;
64+
private final MethodRef expandSignature;
6165
/**
6266
* The direct call target, if there is any. For non-virtual invokes, this is the second-level
6367
* function that is invoked. For virtual invokes, this value is ignored and the actual target
6468
* function is loaded from the vtable.
6569
*/
66-
final CFunctionPointer directTarget;
70+
private final MethodRef directTarget;
6771
/**
6872
* Class that needs to be initialized before invoking the target method. Null when no
6973
* initialization is necessary, i.e., when invoking non-static methods or when the class is
@@ -72,7 +76,7 @@ public abstract class SubstrateAccessor {
7276
final DynamicHub initializeBeforeInvoke;
7377

7478
@Platforms(Platform.HOSTED_ONLY.class)
75-
SubstrateAccessor(Executable member, CFunctionPointer expandSignature, CFunctionPointer directTarget, ResolvedJavaMethod targetMethod, DynamicHub initializeBeforeInvoke) {
79+
SubstrateAccessor(Executable member, MethodRef expandSignature, MethodRef directTarget, ResolvedJavaMethod targetMethod, DynamicHub initializeBeforeInvoke) {
7680
this.member = member;
7781
this.expandSignature = expandSignature;
7882
this.directTarget = directTarget;
@@ -84,20 +88,38 @@ public Executable getMember() {
8488
return member;
8589
}
8690

87-
public CFunctionPointer getExpandSignature() {
88-
return expandSignature;
91+
@Platforms(Platform.HOSTED_ONLY.class)
92+
public ResolvedJavaMethod getExpandSignatureMethod() {
93+
return expandSignature.getMethod();
8994
}
9095

9196
@Platforms(Platform.HOSTED_ONLY.class)
9297
public ResolvedJavaMethod getTargetMethod() {
9398
return targetMethod;
9499
}
95100

101+
@SuppressWarnings("unchecked")
102+
protected static <T extends CFunctionPointer> T getCodePointer(MethodRef ref) {
103+
Pointer p = (Pointer) ref;
104+
if (SubstrateOptions.useRelativeCodePointers() && p.notEqual(0)) {
105+
p = p.add(KnownIntrinsics.codeBase());
106+
}
107+
return (T) p;
108+
}
109+
110+
protected final <T extends CFunctionPointer> T getDirectTarget() {
111+
return getCodePointer(directTarget);
112+
}
113+
114+
protected final <T extends CFunctionPointer> T getExpandSignature() {
115+
return getCodePointer(expandSignature);
116+
}
117+
96118
public Object invokeSpecial(Object obj, Object[] args) {
97-
CFunctionPointer target = directTarget;
119+
CFunctionPointer target = getDirectTarget();
98120
if (target.isNull()) {
99121
throw new IllegalArgumentException("Cannot do invokespecial for an abstract method");
100122
}
101-
return ((ReflectionAccessorHolder.MethodInvokeFunctionPointer) expandSignature).invoke(obj, args, target);
123+
return ((ReflectionAccessorHolder.MethodInvokeFunctionPointer) getExpandSignature()).invoke(obj, args, target);
102124
}
103125
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateConstructorAccessor.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@
2828

2929
import org.graalvm.nativeimage.Platform;
3030
import org.graalvm.nativeimage.Platforms;
31-
import org.graalvm.nativeimage.c.function.CFunctionPointer;
3231

3332
import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode;
3433
import com.oracle.svm.core.hub.DynamicHub;
3534
import com.oracle.svm.core.jdk.InternalVMMethod;
35+
import com.oracle.svm.core.meta.MethodRef;
3636
import com.oracle.svm.core.reflect.ReflectionAccessorHolder.MethodInvokeFunctionPointer;
3737

3838
import jdk.internal.reflect.ConstructorAccessor;
@@ -41,12 +41,12 @@
4141
@InternalVMMethod
4242
public final class SubstrateConstructorAccessor extends SubstrateAccessor implements ConstructorAccessor {
4343

44-
private final CFunctionPointer factoryMethodTarget;
44+
private final MethodRef factoryMethodTarget;
4545

4646
@Platforms(Platform.HOSTED_ONLY.class) //
4747
private final ResolvedJavaMethod factoryMethod;
4848

49-
public SubstrateConstructorAccessor(Executable member, CFunctionPointer expandSignature, CFunctionPointer directTarget, ResolvedJavaMethod targetMethod, CFunctionPointer factoryMethodTarget,
49+
public SubstrateConstructorAccessor(Executable member, MethodRef expandSignature, MethodRef directTarget, ResolvedJavaMethod targetMethod, MethodRef factoryMethodTarget,
5050
ResolvedJavaMethod factoryMethod, DynamicHub initializeBeforeInvoke) {
5151
super(member, expandSignature, directTarget, targetMethod, initializeBeforeInvoke);
5252
this.factoryMethodTarget = factoryMethodTarget;
@@ -63,7 +63,7 @@ public Object newInstance(Object[] args) {
6363
if (initializeBeforeInvoke != null) {
6464
EnsureClassInitializedNode.ensureClassInitialized(DynamicHub.toClass(initializeBeforeInvoke));
6565
}
66-
return ((MethodInvokeFunctionPointer) expandSignature).invoke(null, args, factoryMethodTarget);
66+
return ((MethodInvokeFunctionPointer) getExpandSignature()).invoke(null, args, getCodePointer(factoryMethodTarget));
6767
}
6868

6969
@Override

0 commit comments

Comments
 (0)