Skip to content

Commit df71e50

Browse files
committed
[GR-68174] Use correct assembler in FFM API upcall stub generation.
PullRequest: graal/21988
2 parents 1beecc5 + 328f7f9 commit df71e50

File tree

9 files changed

+148
-30
lines changed

9 files changed

+148
-30
lines changed

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/asm/Assembler.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,24 @@ public void emitString(String s, int pos) {
198198
* including) {@code position()} is returned
199199
* @return the data in this buffer or a trimmed copy if {@code trimmedCopy} is {@code true}
200200
*/
201-
public byte[] close(boolean trimmedCopy) {
201+
public final byte[] close(boolean trimmedCopy) {
202+
return closeAligned(trimmedCopy, 0);
203+
}
204+
205+
/**
206+
* Closes this assembler. No extra data can be written to this assembler after this call.
207+
*
208+
* @param trimmedCopy if {@code true}, then a copy of the underlying byte array up to (but not
209+
* including) {@code position()} is returned
210+
* @param alignment if {@code > 0}, then align the end of the code buffer with NOPs to the
211+
* specified alignment
212+
* @return the data in this buffer or a trimmed copy if {@code trimmedCopy} is {@code true}
213+
*/
214+
public byte[] closeAligned(boolean trimmedCopy, int alignment) {
202215
checkAndClearLabelsWithPatches();
216+
if (alignment > 0 && position() % alignment != 0) {
217+
this.align(alignment);
218+
}
203219
finalCodeSize = position();
204220
return codeBuffer.close(trimmedCopy);
205221
}

substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/AbiUtils.java

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,17 @@
5252
import org.graalvm.nativeimage.Platforms;
5353
import org.graalvm.word.Pointer;
5454

55+
import com.oracle.svm.core.BuildPhaseProvider.AfterAnalysis;
56+
import com.oracle.svm.core.SubstrateControlFlowIntegrity;
5557
import com.oracle.svm.core.SubstrateTargetDescription;
5658
import com.oracle.svm.core.aarch64.SubstrateAArch64MacroAssembler;
5759
import com.oracle.svm.core.config.ConfigurationValues;
5860
import com.oracle.svm.core.foreign.AbiUtils.Adapter.Adaptation;
5961
import com.oracle.svm.core.graal.code.AssignedLocation;
62+
import com.oracle.svm.core.graal.code.SubstrateBackendWithAssembler;
6063
import com.oracle.svm.core.headers.LibC;
6164
import com.oracle.svm.core.headers.WindowsAPIs;
65+
import com.oracle.svm.core.heap.UnknownPrimitiveField;
6266
import com.oracle.svm.core.util.BasedOnJDKClass;
6367
import com.oracle.svm.core.util.BasedOnJDKFile;
6468
import com.oracle.svm.core.util.VMError;
@@ -69,8 +73,8 @@
6973
import jdk.graal.compiler.asm.aarch64.AArch64Address;
7074
import jdk.graal.compiler.asm.aarch64.AArch64MacroAssembler;
7175
import jdk.graal.compiler.asm.amd64.AMD64Address;
72-
import jdk.graal.compiler.asm.amd64.AMD64Assembler;
7376
import jdk.graal.compiler.asm.amd64.AMD64BaseAssembler;
77+
import jdk.graal.compiler.asm.amd64.AMD64MacroAssembler;
7478
import jdk.graal.compiler.graph.Node;
7579
import jdk.graal.compiler.nodes.ValueNode;
7680
import jdk.graal.compiler.nodes.calc.AddNode;
@@ -671,11 +675,40 @@ public record Registers(Register methodHandle, Register isolate) {
671675

672676
public abstract int trampolineSize();
673677

674-
record TrampolineTemplate(byte[] assemblyTemplate, int isolateOffset, int methodHandleOffset, int stubOffset) {
678+
public static class TrampolineTemplate {
679+
680+
private final byte[] assemblyTemplate;
681+
682+
/*
683+
* These fields will only be filled after the analysis, when an assembler is available.
684+
* Prevent optimizations that constant-fold these fields already during analysis.
685+
*/
686+
687+
@UnknownPrimitiveField(availability = AfterAnalysis.class) //
688+
private int isolateOffset;
689+
@UnknownPrimitiveField(availability = AfterAnalysis.class) //
690+
private int methodHandleOffset;
691+
@UnknownPrimitiveField(availability = AfterAnalysis.class) //
692+
private int stubOffset;
693+
694+
public TrampolineTemplate(byte[] assemblyTemplate) {
695+
this.assemblyTemplate = assemblyTemplate;
696+
}
697+
698+
@Platforms(Platform.HOSTED_ONLY.class)
699+
public void setTemplate(byte[] code, int isolateOff, int methodHandleOff, int stubOff) {
700+
assert code.length == this.assemblyTemplate.length;
701+
System.arraycopy(code, 0, this.assemblyTemplate, 0, this.assemblyTemplate.length);
702+
this.isolateOffset = isolateOff;
703+
this.methodHandleOffset = methodHandleOff;
704+
this.stubOffset = stubOff;
705+
}
706+
675707
public Pointer write(Pointer at, Isolate isolate, Word methodHandle, Word stubPointer) {
676708
for (int i = 0; i < assemblyTemplate.length; ++i) {
677709
at.writeByte(i, assemblyTemplate[i]);
678710
}
711+
679712
at.writeWord(isolateOffset, isolate);
680713
at.writeWord(methodHandleOffset, methodHandle);
681714
at.writeWord(stubOffset, stubPointer);
@@ -685,7 +718,7 @@ public Pointer write(Pointer at, Isolate isolate, Word methodHandle, Word stubPo
685718
}
686719

687720
@Platforms(Platform.HOSTED_ONLY.class)
688-
abstract TrampolineTemplate generateTrampolineTemplate();
721+
abstract void generateTrampolineTemplate(SubstrateBackendWithAssembler<?> backend, TrampolineTemplate template);
689722
}
690723

691724
class ABIs {
@@ -740,8 +773,8 @@ public int trampolineSize() {
740773
}
741774

742775
@Override
743-
public TrampolineTemplate generateTrampolineTemplate() {
744-
return null;
776+
public void generateTrampolineTemplate(SubstrateBackendWithAssembler<?> backend, TrampolineTemplate template) {
777+
fail();
745778
}
746779

747780
@Override
@@ -812,8 +845,8 @@ public int trampolineSize() {
812845

813846
@Platforms(Platform.HOSTED_ONLY.class)
814847
@Override
815-
public TrampolineTemplate generateTrampolineTemplate() {
816-
AArch64MacroAssembler masm = new SubstrateAArch64MacroAssembler(ConfigurationValues.getTarget());
848+
public void generateTrampolineTemplate(SubstrateBackendWithAssembler<?> backend, TrampolineTemplate template) {
849+
AArch64MacroAssembler masm = (AArch64MacroAssembler) backend.createAssemblerNoOptions();
817850

818851
Register mhRegister = upcallSpecialArgumentsRegisters().methodHandle();
819852
Register isolateRegister = upcallSpecialArgumentsRegisters().isolate();
@@ -849,12 +882,11 @@ public TrampolineTemplate generateTrampolineTemplate() {
849882
masm.jmp(scratch);
850883

851884
assert trampolineSize() >= masm.position();
852-
masm.align(trampolineSize());
853885

854-
byte[] assembly = masm.close(true);
886+
byte[] assembly = masm.closeAligned(true, trampolineSize());
855887
assert assembly.length == trampolineSize();
856888

857-
return new TrampolineTemplate(assembly, posIsolate, posMHArray, posCallTarget);
889+
template.setTemplate(assembly, posIsolate, posMHArray, posCallTarget);
858890
}
859891

860892
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+13/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java#L195")
@@ -974,9 +1006,9 @@ public int trampolineSize() {
9741006

9751007
@Platforms(Platform.HOSTED_ONLY.class)
9761008
@Override
977-
public TrampolineTemplate generateTrampolineTemplate() {
1009+
public void generateTrampolineTemplate(SubstrateBackendWithAssembler<?> backend, TrampolineTemplate template) {
9781010
// Generate the trampoline
979-
AMD64Assembler asm = new AMD64Assembler(ConfigurationValues.getTarget());
1011+
AMD64MacroAssembler asm = (AMD64MacroAssembler) backend.createAssemblerNoOptions();
9801012
var odas = new ArrayList<AMD64BaseAssembler.OperandDataAnnotation>(3);
9811013
// Collect the positions of the address in the movq instructions.
9821014
asm.setCodePatchingAnnotationConsumer(ca -> {
@@ -988,6 +1020,7 @@ public TrampolineTemplate generateTrampolineTemplate() {
9881020
Register mhRegister = upcallSpecialArgumentsRegisters().methodHandle();
9891021
Register isolateRegister = upcallSpecialArgumentsRegisters().isolate();
9901022

1023+
asm.maybeEmitIndirectTargetMarker();
9911024
/* Store isolate in the assigned register */
9921025
asm.movq(isolateRegister, 0L, true);
9931026
/* r10 points in the mh array */
@@ -997,17 +1030,21 @@ public TrampolineTemplate generateTrampolineTemplate() {
9971030
/* rax contains the stub address */
9981031
asm.movq(rax, 0L, true);
9991032
/* executes the stub */
1000-
asm.jmp(new AMD64Address(rax, 0));
1033+
if (SubstrateControlFlowIntegrity.useSoftwareCFI()) {
1034+
asm.movq(rax, new AMD64Address(rax, 0));
1035+
asm.jmp(rax);
1036+
} else {
1037+
asm.jmp(new AMD64Address(rax, 0));
1038+
}
10011039

10021040
assert trampolineSize() - asm.position() >= 0;
1003-
asm.nop(trampolineSize() - asm.position());
10041041

1005-
byte[] assembly = asm.close(true);
1042+
byte[] assembly = asm.closeAligned(true, trampolineSize());
10061043
assert assembly.length == trampolineSize();
10071044
assert odas.size() == 3;
10081045
assert odas.stream().allMatch(oda -> oda.operandSize == 8);
10091046

1010-
return new TrampolineTemplate(assembly, odas.get(0).operandPosition, odas.get(1).operandPosition, odas.get(2).operandPosition);
1047+
template.setTemplate(assembly, odas.get(0).operandPosition, odas.get(1).operandPosition, odas.get(2).operandPosition);
10111048
}
10121049
}
10131050

substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/ForeignFunctionsRuntime.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@
5858
import com.oracle.svm.core.SubstrateUtil;
5959
import com.oracle.svm.core.Uninterruptible;
6060
import com.oracle.svm.core.c.InvokeJavaFunctionPointer;
61+
import com.oracle.svm.core.foreign.AbiUtils.TrampolineTemplate;
6162
import com.oracle.svm.core.foreign.phases.SubstrateOptimizeSharedArenaAccessPhase.OptimizeSharedArenaConfig;
63+
import com.oracle.svm.core.graal.code.SubstrateBackendWithAssembler;
6264
import com.oracle.svm.core.headers.LibC;
6365
import com.oracle.svm.core.headers.WindowsAPIs;
6466
import com.oracle.svm.core.image.DisallowedImageHeapObjects.DisallowedObjectReporter;
@@ -99,7 +101,12 @@ public static ForeignFunctionsRuntime singleton() {
99101

100102
@Platforms(Platform.HOSTED_ONLY.class)
101103
public ForeignFunctionsRuntime(AbiUtils abiUtils) {
102-
this.trampolineTemplate = abiUtils.generateTrampolineTemplate();
104+
this.trampolineTemplate = new TrampolineTemplate(new byte[abiUtils.trampolineSize()]);
105+
}
106+
107+
@Platforms(Platform.HOSTED_ONLY.class)
108+
public void generateTrampolineTemplate(SubstrateBackendWithAssembler<?> backend) {
109+
AbiUtils.singleton().generateTrampolineTemplate(backend, this.trampolineTemplate);
103110
}
104111

105112
public static boolean areFunctionCallsSupported() {

substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/TrampolineSet.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ private Pointer prepareTrampolines(PinnedObject mhsArray, PinnedObject stubsArra
159159
VMError.guarantee(it.belowOrEqual(end), "Not enough memory was allocated to hold trampolines");
160160
}
161161

162-
VMError.guarantee(VirtualMemoryProvider.get().protect(page, pageSize, VirtualMemoryProvider.Access.EXECUTE) == 0,
162+
VMError.guarantee(VirtualMemoryProvider.get().protect(page, pageSize, VirtualMemoryProvider.Access.READ | VirtualMemoryProvider.Access.EXECUTE) == 0,
163163
"Error when making the trampoline allocation executable");
164164

165165
/*

substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
import com.oracle.svm.core.graal.code.AssignedLocation;
5656
import com.oracle.svm.core.graal.code.PatchConsumerFactory;
5757
import com.oracle.svm.core.graal.code.SharedCompilationResult;
58-
import com.oracle.svm.core.graal.code.SubstrateBackend;
58+
import com.oracle.svm.core.graal.code.SubstrateBackendWithAssembler;
5959
import com.oracle.svm.core.graal.code.SubstrateCallingConvention;
6060
import com.oracle.svm.core.graal.code.SubstrateCallingConventionKind;
6161
import com.oracle.svm.core.graal.code.SubstrateCallingConventionType;
@@ -194,7 +194,7 @@
194194
import jdk.vm.ci.meta.ResolvedJavaMethod;
195195
import jdk.vm.ci.meta.Value;
196196

197-
public class SubstrateAArch64Backend extends SubstrateBackend implements LIRGenerationProvider {
197+
public class SubstrateAArch64Backend extends SubstrateBackendWithAssembler<SubstrateAArch64MacroAssembler> implements LIRGenerationProvider {
198198

199199
protected static CompressEncoding getCompressEncoding() {
200200
return ImageSingletons.lookup(CompressEncoding.class);
@@ -1564,4 +1564,9 @@ public LIRGenerationResult newLIRGenerationResult(CompilationIdentifier compilat
15641564
public BasePhase<CoreProviders> newAddressLoweringPhase(CodeCacheProvider codeCache) {
15651565
return new AddressLoweringByUsePhase(new AArch64AddressLoweringByUse(createLirKindTool(), false));
15661566
}
1567+
1568+
@Override
1569+
public SubstrateAArch64MacroAssembler createAssembler(OptionValues options) {
1570+
return new SubstrateAArch64MacroAssembler(getTarget());
1571+
}
15671572
}

substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/SubstrateAMD64Backend.java

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
import java.util.List;
4848
import java.util.function.BiConsumer;
4949

50-
import org.graalvm.collections.EconomicMap;
5150
import org.graalvm.nativeimage.ImageSingletons;
5251

5352
import com.oracle.svm.core.CPUFeatureAccess;
@@ -67,7 +66,7 @@
6766
import com.oracle.svm.core.graal.code.PatchConsumerFactory;
6867
import com.oracle.svm.core.graal.code.SharedCompilationResult;
6968
import com.oracle.svm.core.graal.code.StubCallingConvention;
70-
import com.oracle.svm.core.graal.code.SubstrateBackend;
69+
import com.oracle.svm.core.graal.code.SubstrateBackendWithAssembler;
7170
import com.oracle.svm.core.graal.code.SubstrateCallingConvention;
7271
import com.oracle.svm.core.graal.code.SubstrateCallingConventionKind;
7372
import com.oracle.svm.core.graal.code.SubstrateCallingConventionType;
@@ -222,7 +221,7 @@
222221
import jdk.vm.ci.meta.ResolvedJavaMethod;
223222
import jdk.vm.ci.meta.Value;
224223

225-
public class SubstrateAMD64Backend extends SubstrateBackend implements LIRGenerationProvider {
224+
public class SubstrateAMD64Backend extends SubstrateBackendWithAssembler<AMD64MacroAssembler> implements LIRGenerationProvider {
226225

227226
protected static CompressEncoding getCompressEncoding() {
228227
return ImageSingletons.lookup(CompressEncoding.class);
@@ -1922,6 +1921,7 @@ public CompilationResultBuilder newCompilationResultBuilder(LIRGenerationResult
19221921
return crb;
19231922
}
19241923

1924+
@Override
19251925
protected AMD64MacroAssembler createAssembler(OptionValues options) {
19261926
return new AMD64MacroAssembler(getTarget(), options, true);
19271927
}
@@ -1961,11 +1961,6 @@ public void emitCode(CompilationResultBuilder crb, ResolvedJavaMethod installedC
19611961
}
19621962
}
19631963

1964-
public AMD64Assembler createAssemblerNoOptions() {
1965-
OptionValues o = new OptionValues(EconomicMap.create());
1966-
return createAssembler(o);
1967-
}
1968-
19691964
protected void resetForEmittingCode(CompilationResultBuilder crb) {
19701965
crb.resetForEmittingCode();
19711966
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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.graal.code;
26+
27+
import org.graalvm.collections.EconomicMap;
28+
29+
import jdk.graal.compiler.asm.Assembler;
30+
import jdk.graal.compiler.options.OptionValues;
31+
import jdk.graal.compiler.phases.util.Providers;
32+
33+
public abstract class SubstrateBackendWithAssembler<A extends Assembler<?>> extends SubstrateBackend {
34+
35+
protected SubstrateBackendWithAssembler(Providers providers) {
36+
super(providers);
37+
}
38+
39+
protected abstract A createAssembler(OptionValues options);
40+
41+
public final A createAssemblerNoOptions() {
42+
OptionValues o = new OptionValues(EconomicMap.create());
43+
return createAssembler(o);
44+
}
45+
}

substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/ForeignFunctionsFeature.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@
8989
import com.oracle.svm.core.foreign.Target_java_nio_MappedMemoryUtils;
9090
import com.oracle.svm.core.foreign.phases.SubstrateOptimizeSharedArenaAccessPhase;
9191
import com.oracle.svm.core.graal.RuntimeCompilation;
92+
import com.oracle.svm.core.graal.code.SubstrateBackend;
93+
import com.oracle.svm.core.graal.code.SubstrateBackendWithAssembler;
9294
import com.oracle.svm.core.graal.meta.SubstrateForeignCallsProvider;
9395
import com.oracle.svm.core.jdk.VectorAPIEnabled;
9496
import com.oracle.svm.core.meta.MethodPointer;
@@ -405,6 +407,17 @@ public void duringSetup(DuringSetupAccess a) {
405407
ConfigurationParserUtils.parseAndRegisterConfigurationsFromCombinedFile(getConfigurationParser(imageClassLoader), imageClassLoader, "panama foreign");
406408
}
407409

410+
@Override
411+
public void beforeCompilation(BeforeCompilationAccess access) {
412+
FeatureImpl.BeforeCompilationAccessImpl a = (FeatureImpl.BeforeCompilationAccessImpl) access;
413+
SubstrateBackend b = a.getRuntimeConfiguration().getBackendForNormalMethod();
414+
if (b instanceof SubstrateBackendWithAssembler<?> bAsm) {
415+
foreignFunctionsRuntime.generateTrampolineTemplate(bAsm);
416+
} else {
417+
throw VMError.shouldNotReachHere("Support for the Foreign Function and Memory API needs a backend with an assembler, it is not available with backend %s", b.getClass());
418+
}
419+
}
420+
408421
private ConfigurationParser getConfigurationParser(ImageClassLoader imageClassLoader) {
409422
/*
410423
* If foreign function calls are not supported on this platform, we still want to parse the

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/amd64/AMD64PLTStubGenerator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ public byte[] generatePLT(SharedMethod[] got, SubstrateBackend substrateBackend)
114114
RegisterConfig registerConfig = amd64Backend.getCodeCache().getRegisterConfig();
115115
Register register = configuration.getGOTPassingRegister(registerConfig);
116116

117-
AMD64MacroAssembler asm = (AMD64MacroAssembler) amd64Backend.createAssemblerNoOptions();
117+
AMD64MacroAssembler asm = amd64Backend.createAssemblerNoOptions();
118118
PLTSectionSupport support = HostedPLTGOTConfiguration.singleton().getPLTSectionSupport();
119119

120120
asm.setCodePatchingAnnotationConsumer(this::recordResolverCallForPatching);

0 commit comments

Comments
 (0)