diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/asm/Assembler.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/asm/Assembler.java index e5344761ad0d..9724baf81b8a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/asm/Assembler.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/asm/Assembler.java @@ -198,8 +198,24 @@ public void emitString(String s, int pos) { * including) {@code position()} is returned * @return the data in this buffer or a trimmed copy if {@code trimmedCopy} is {@code true} */ - public byte[] close(boolean trimmedCopy) { + public final byte[] close(boolean trimmedCopy) { + return closeAligned(trimmedCopy, 0); + } + + /** + * Closes this assembler. No extra data can be written to this assembler after this call. + * + * @param trimmedCopy if {@code true}, then a copy of the underlying byte array up to (but not + * including) {@code position()} is returned + * @param alignment if {@code > 0}, then align the end of the code buffer with NOPs to the + * specified alignment + * @return the data in this buffer or a trimmed copy if {@code trimmedCopy} is {@code true} + */ + public byte[] closeAligned(boolean trimmedCopy, int alignment) { checkAndClearLabelsWithPatches(); + if (alignment > 0 && position() % alignment != 0) { + this.align(alignment); + } finalCodeSize = position(); return codeBuffer.close(trimmedCopy); } diff --git a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/AbiUtils.java b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/AbiUtils.java index b437aae7da13..4e5520f08f0c 100644 --- a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/AbiUtils.java +++ b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/AbiUtils.java @@ -52,13 +52,17 @@ import org.graalvm.nativeimage.Platforms; import org.graalvm.word.Pointer; +import com.oracle.svm.core.BuildPhaseProvider.AfterAnalysis; +import com.oracle.svm.core.SubstrateControlFlowIntegrity; import com.oracle.svm.core.SubstrateTargetDescription; import com.oracle.svm.core.aarch64.SubstrateAArch64MacroAssembler; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.foreign.AbiUtils.Adapter.Adaptation; import com.oracle.svm.core.graal.code.AssignedLocation; +import com.oracle.svm.core.graal.code.SubstrateBackendWithAssembler; import com.oracle.svm.core.headers.LibC; import com.oracle.svm.core.headers.WindowsAPIs; +import com.oracle.svm.core.heap.UnknownPrimitiveField; import com.oracle.svm.core.util.BasedOnJDKClass; import com.oracle.svm.core.util.BasedOnJDKFile; import com.oracle.svm.core.util.VMError; @@ -69,8 +73,8 @@ import jdk.graal.compiler.asm.aarch64.AArch64Address; import jdk.graal.compiler.asm.aarch64.AArch64MacroAssembler; import jdk.graal.compiler.asm.amd64.AMD64Address; -import jdk.graal.compiler.asm.amd64.AMD64Assembler; import jdk.graal.compiler.asm.amd64.AMD64BaseAssembler; +import jdk.graal.compiler.asm.amd64.AMD64MacroAssembler; import jdk.graal.compiler.graph.Node; import jdk.graal.compiler.nodes.ValueNode; import jdk.graal.compiler.nodes.calc.AddNode; @@ -671,11 +675,40 @@ public record Registers(Register methodHandle, Register isolate) { public abstract int trampolineSize(); - record TrampolineTemplate(byte[] assemblyTemplate, int isolateOffset, int methodHandleOffset, int stubOffset) { + public static class TrampolineTemplate { + + private final byte[] assemblyTemplate; + + /* + * These fields will only be filled after the analysis, when an assembler is available. + * Prevent optimizations that constant-fold these fields already during analysis. + */ + + @UnknownPrimitiveField(availability = AfterAnalysis.class) // + private int isolateOffset; + @UnknownPrimitiveField(availability = AfterAnalysis.class) // + private int methodHandleOffset; + @UnknownPrimitiveField(availability = AfterAnalysis.class) // + private int stubOffset; + + public TrampolineTemplate(byte[] assemblyTemplate) { + this.assemblyTemplate = assemblyTemplate; + } + + @Platforms(Platform.HOSTED_ONLY.class) + public void setTemplate(byte[] code, int isolateOff, int methodHandleOff, int stubOff) { + assert code.length == this.assemblyTemplate.length; + System.arraycopy(code, 0, this.assemblyTemplate, 0, this.assemblyTemplate.length); + this.isolateOffset = isolateOff; + this.methodHandleOffset = methodHandleOff; + this.stubOffset = stubOff; + } + public Pointer write(Pointer at, Isolate isolate, Word methodHandle, Word stubPointer) { for (int i = 0; i < assemblyTemplate.length; ++i) { at.writeByte(i, assemblyTemplate[i]); } + at.writeWord(isolateOffset, isolate); at.writeWord(methodHandleOffset, methodHandle); at.writeWord(stubOffset, stubPointer); @@ -685,7 +718,7 @@ public Pointer write(Pointer at, Isolate isolate, Word methodHandle, Word stubPo } @Platforms(Platform.HOSTED_ONLY.class) - abstract TrampolineTemplate generateTrampolineTemplate(); + abstract void generateTrampolineTemplate(SubstrateBackendWithAssembler backend, TrampolineTemplate template); } class ABIs { @@ -740,8 +773,8 @@ public int trampolineSize() { } @Override - public TrampolineTemplate generateTrampolineTemplate() { - return null; + public void generateTrampolineTemplate(SubstrateBackendWithAssembler backend, TrampolineTemplate template) { + fail(); } @Override @@ -812,8 +845,8 @@ public int trampolineSize() { @Platforms(Platform.HOSTED_ONLY.class) @Override - public TrampolineTemplate generateTrampolineTemplate() { - AArch64MacroAssembler masm = new SubstrateAArch64MacroAssembler(ConfigurationValues.getTarget()); + public void generateTrampolineTemplate(SubstrateBackendWithAssembler backend, TrampolineTemplate template) { + AArch64MacroAssembler masm = (AArch64MacroAssembler) backend.createAssemblerNoOptions(); Register mhRegister = upcallSpecialArgumentsRegisters().methodHandle(); Register isolateRegister = upcallSpecialArgumentsRegisters().isolate(); @@ -849,12 +882,11 @@ public TrampolineTemplate generateTrampolineTemplate() { masm.jmp(scratch); assert trampolineSize() >= masm.position(); - masm.align(trampolineSize()); - byte[] assembly = masm.close(true); + byte[] assembly = masm.closeAligned(true, trampolineSize()); assert assembly.length == trampolineSize(); - return new TrampolineTemplate(assembly, posIsolate, posMHArray, posCallTarget); + template.setTemplate(assembly, posIsolate, posMHArray, posCallTarget); } @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() { @Platforms(Platform.HOSTED_ONLY.class) @Override - public TrampolineTemplate generateTrampolineTemplate() { + public void generateTrampolineTemplate(SubstrateBackendWithAssembler backend, TrampolineTemplate template) { // Generate the trampoline - AMD64Assembler asm = new AMD64Assembler(ConfigurationValues.getTarget()); + AMD64MacroAssembler asm = (AMD64MacroAssembler) backend.createAssemblerNoOptions(); var odas = new ArrayList(3); // Collect the positions of the address in the movq instructions. asm.setCodePatchingAnnotationConsumer(ca -> { @@ -988,6 +1020,7 @@ public TrampolineTemplate generateTrampolineTemplate() { Register mhRegister = upcallSpecialArgumentsRegisters().methodHandle(); Register isolateRegister = upcallSpecialArgumentsRegisters().isolate(); + asm.maybeEmitIndirectTargetMarker(); /* Store isolate in the assigned register */ asm.movq(isolateRegister, 0L, true); /* r10 points in the mh array */ @@ -997,17 +1030,21 @@ public TrampolineTemplate generateTrampolineTemplate() { /* rax contains the stub address */ asm.movq(rax, 0L, true); /* executes the stub */ - asm.jmp(new AMD64Address(rax, 0)); + if (SubstrateControlFlowIntegrity.useSoftwareCFI()) { + asm.movq(rax, new AMD64Address(rax, 0)); + asm.jmp(rax); + } else { + asm.jmp(new AMD64Address(rax, 0)); + } assert trampolineSize() - asm.position() >= 0; - asm.nop(trampolineSize() - asm.position()); - byte[] assembly = asm.close(true); + byte[] assembly = asm.closeAligned(true, trampolineSize()); assert assembly.length == trampolineSize(); assert odas.size() == 3; assert odas.stream().allMatch(oda -> oda.operandSize == 8); - return new TrampolineTemplate(assembly, odas.get(0).operandPosition, odas.get(1).operandPosition, odas.get(2).operandPosition); + template.setTemplate(assembly, odas.get(0).operandPosition, odas.get(1).operandPosition, odas.get(2).operandPosition); } } diff --git a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/ForeignFunctionsRuntime.java b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/ForeignFunctionsRuntime.java index 4ccc4ea305b2..c064ab470e92 100644 --- a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/ForeignFunctionsRuntime.java +++ b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/ForeignFunctionsRuntime.java @@ -58,7 +58,9 @@ import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.c.InvokeJavaFunctionPointer; +import com.oracle.svm.core.foreign.AbiUtils.TrampolineTemplate; import com.oracle.svm.core.foreign.phases.SubstrateOptimizeSharedArenaAccessPhase.OptimizeSharedArenaConfig; +import com.oracle.svm.core.graal.code.SubstrateBackendWithAssembler; import com.oracle.svm.core.headers.LibC; import com.oracle.svm.core.headers.WindowsAPIs; import com.oracle.svm.core.image.DisallowedImageHeapObjects.DisallowedObjectReporter; @@ -99,7 +101,12 @@ public static ForeignFunctionsRuntime singleton() { @Platforms(Platform.HOSTED_ONLY.class) public ForeignFunctionsRuntime(AbiUtils abiUtils) { - this.trampolineTemplate = abiUtils.generateTrampolineTemplate(); + this.trampolineTemplate = new TrampolineTemplate(new byte[abiUtils.trampolineSize()]); + } + + @Platforms(Platform.HOSTED_ONLY.class) + public void generateTrampolineTemplate(SubstrateBackendWithAssembler backend) { + AbiUtils.singleton().generateTrampolineTemplate(backend, this.trampolineTemplate); } public static boolean areFunctionCallsSupported() { diff --git a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/TrampolineSet.java b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/TrampolineSet.java index 90c2262a61c0..6ea751b50c86 100644 --- a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/TrampolineSet.java +++ b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/TrampolineSet.java @@ -159,7 +159,7 @@ private Pointer prepareTrampolines(PinnedObject mhsArray, PinnedObject stubsArra VMError.guarantee(it.belowOrEqual(end), "Not enough memory was allocated to hold trampolines"); } - VMError.guarantee(VirtualMemoryProvider.get().protect(page, pageSize, VirtualMemoryProvider.Access.EXECUTE) == 0, + VMError.guarantee(VirtualMemoryProvider.get().protect(page, pageSize, VirtualMemoryProvider.Access.READ | VirtualMemoryProvider.Access.EXECUTE) == 0, "Error when making the trampoline allocation executable"); /* diff --git a/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java b/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java index 102fb5358250..a2362bd795ec 100755 --- a/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java +++ b/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java @@ -55,7 +55,7 @@ import com.oracle.svm.core.graal.code.AssignedLocation; import com.oracle.svm.core.graal.code.PatchConsumerFactory; import com.oracle.svm.core.graal.code.SharedCompilationResult; -import com.oracle.svm.core.graal.code.SubstrateBackend; +import com.oracle.svm.core.graal.code.SubstrateBackendWithAssembler; import com.oracle.svm.core.graal.code.SubstrateCallingConvention; import com.oracle.svm.core.graal.code.SubstrateCallingConventionKind; import com.oracle.svm.core.graal.code.SubstrateCallingConventionType; @@ -194,7 +194,7 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.Value; -public class SubstrateAArch64Backend extends SubstrateBackend implements LIRGenerationProvider { +public class SubstrateAArch64Backend extends SubstrateBackendWithAssembler implements LIRGenerationProvider { protected static CompressEncoding getCompressEncoding() { return ImageSingletons.lookup(CompressEncoding.class); @@ -1564,4 +1564,9 @@ public LIRGenerationResult newLIRGenerationResult(CompilationIdentifier compilat public BasePhase newAddressLoweringPhase(CodeCacheProvider codeCache) { return new AddressLoweringByUsePhase(new AArch64AddressLoweringByUse(createLirKindTool(), false)); } + + @Override + public SubstrateAArch64MacroAssembler createAssembler(OptionValues options) { + return new SubstrateAArch64MacroAssembler(getTarget()); + } } diff --git a/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/SubstrateAMD64Backend.java b/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/SubstrateAMD64Backend.java index 27543c2b58fc..b7fd936d6b00 100644 --- a/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/SubstrateAMD64Backend.java +++ b/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/SubstrateAMD64Backend.java @@ -47,7 +47,6 @@ import java.util.List; import java.util.function.BiConsumer; -import org.graalvm.collections.EconomicMap; import org.graalvm.nativeimage.ImageSingletons; import com.oracle.svm.core.CPUFeatureAccess; @@ -67,7 +66,7 @@ import com.oracle.svm.core.graal.code.PatchConsumerFactory; import com.oracle.svm.core.graal.code.SharedCompilationResult; import com.oracle.svm.core.graal.code.StubCallingConvention; -import com.oracle.svm.core.graal.code.SubstrateBackend; +import com.oracle.svm.core.graal.code.SubstrateBackendWithAssembler; import com.oracle.svm.core.graal.code.SubstrateCallingConvention; import com.oracle.svm.core.graal.code.SubstrateCallingConventionKind; import com.oracle.svm.core.graal.code.SubstrateCallingConventionType; @@ -222,7 +221,7 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.Value; -public class SubstrateAMD64Backend extends SubstrateBackend implements LIRGenerationProvider { +public class SubstrateAMD64Backend extends SubstrateBackendWithAssembler implements LIRGenerationProvider { protected static CompressEncoding getCompressEncoding() { return ImageSingletons.lookup(CompressEncoding.class); @@ -1922,6 +1921,7 @@ public CompilationResultBuilder newCompilationResultBuilder(LIRGenerationResult return crb; } + @Override protected AMD64MacroAssembler createAssembler(OptionValues options) { return new AMD64MacroAssembler(getTarget(), options, true); } @@ -1961,11 +1961,6 @@ public void emitCode(CompilationResultBuilder crb, ResolvedJavaMethod installedC } } - public AMD64Assembler createAssemblerNoOptions() { - OptionValues o = new OptionValues(EconomicMap.create()); - return createAssembler(o); - } - protected void resetForEmittingCode(CompilationResultBuilder crb) { crb.resetForEmittingCode(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/code/SubstrateBackendWithAssembler.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/code/SubstrateBackendWithAssembler.java new file mode 100644 index 000000000000..fa118e20dd70 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/code/SubstrateBackendWithAssembler.java @@ -0,0 +1,45 @@ +/* + * 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.graal.code; + +import org.graalvm.collections.EconomicMap; + +import jdk.graal.compiler.asm.Assembler; +import jdk.graal.compiler.options.OptionValues; +import jdk.graal.compiler.phases.util.Providers; + +public abstract class SubstrateBackendWithAssembler> extends SubstrateBackend { + + protected SubstrateBackendWithAssembler(Providers providers) { + super(providers); + } + + protected abstract A createAssembler(OptionValues options); + + public final A createAssemblerNoOptions() { + OptionValues o = new OptionValues(EconomicMap.create()); + return createAssembler(o); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/ForeignFunctionsFeature.java b/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/ForeignFunctionsFeature.java index 256acca4a8e0..d1816f8bc0ab 100644 --- a/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/ForeignFunctionsFeature.java +++ b/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/ForeignFunctionsFeature.java @@ -89,6 +89,8 @@ import com.oracle.svm.core.foreign.Target_java_nio_MappedMemoryUtils; import com.oracle.svm.core.foreign.phases.SubstrateOptimizeSharedArenaAccessPhase; import com.oracle.svm.core.graal.RuntimeCompilation; +import com.oracle.svm.core.graal.code.SubstrateBackend; +import com.oracle.svm.core.graal.code.SubstrateBackendWithAssembler; import com.oracle.svm.core.graal.meta.SubstrateForeignCallsProvider; import com.oracle.svm.core.jdk.VectorAPIEnabled; import com.oracle.svm.core.meta.MethodPointer; @@ -405,6 +407,17 @@ public void duringSetup(DuringSetupAccess a) { ConfigurationParserUtils.parseAndRegisterConfigurationsFromCombinedFile(getConfigurationParser(imageClassLoader), imageClassLoader, "panama foreign"); } + @Override + public void beforeCompilation(BeforeCompilationAccess access) { + FeatureImpl.BeforeCompilationAccessImpl a = (FeatureImpl.BeforeCompilationAccessImpl) access; + SubstrateBackend b = a.getRuntimeConfiguration().getBackendForNormalMethod(); + if (b instanceof SubstrateBackendWithAssembler bAsm) { + foreignFunctionsRuntime.generateTrampolineTemplate(bAsm); + } else { + 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()); + } + } + private ConfigurationParser getConfigurationParser(ImageClassLoader imageClassLoader) { /* * If foreign function calls are not supported on this platform, we still want to parse the diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/amd64/AMD64PLTStubGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/amd64/AMD64PLTStubGenerator.java index 355f145d631b..9337985c4db0 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/amd64/AMD64PLTStubGenerator.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/amd64/AMD64PLTStubGenerator.java @@ -114,7 +114,7 @@ public byte[] generatePLT(SharedMethod[] got, SubstrateBackend substrateBackend) RegisterConfig registerConfig = amd64Backend.getCodeCache().getRegisterConfig(); Register register = configuration.getGOTPassingRegister(registerConfig); - AMD64MacroAssembler asm = (AMD64MacroAssembler) amd64Backend.createAssemblerNoOptions(); + AMD64MacroAssembler asm = amd64Backend.createAssemblerNoOptions(); PLTSectionSupport support = HostedPLTGOTConfiguration.singleton().getPLTSectionSupport(); asm.setCodePatchingAnnotationConsumer(this::recordResolverCallForPatching);