diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index 677ef89796ce..415f8587a603 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -480,7 +480,7 @@ "subDir": "src", "sourceDirs": ["src"], "dependencies": [ - "com.oracle.svm.core.graal.amd64", + "com.oracle.svm.hosted", "com.oracle.svm.core.graal.aarch64", "com.oracle.svm.core.graal.riscv64", ], @@ -511,7 +511,7 @@ "subDir": "src", "sourceDirs": ["src"], "dependencies": [ - "com.oracle.svm.core.graal.amd64", + "com.oracle.svm.hosted", ], "requiresConcealed" : { "jdk.internal.vm.ci" : [ @@ -639,8 +639,8 @@ "sourceDirs": ["src"], "dependencies": [ "com.oracle.objectfile", - "com.oracle.svm.core", - "com.oracle.graal.reachability" + "com.oracle.graal.reachability", + "com.oracle.svm.core.graal.amd64", ], "requires" : [ "jdk.jfr", 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 12598c0855fb..b87390e83b09 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 @@ -72,6 +72,7 @@ import com.oracle.svm.core.meta.SubstrateMethodPointerConstant; import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.core.nodes.SafepointCheckNode; +import com.oracle.svm.core.pltgot.PLTGOTConfiguration; import com.oracle.svm.core.thread.VMThreads.StatusSupport; import com.oracle.svm.core.util.VMError; @@ -667,6 +668,14 @@ public Register getHeapBaseRegister() { protected int getVMPageSize() { return SubstrateOptions.getPageSize(); } + + @Override + public void emitExitMethodAddressResolution(Value ip) { + PLTGOTConfiguration configuration = PLTGOTConfiguration.singleton(); + RegisterValue exitThroughRegisterValue = configuration.getExitMethodAddressResolutionRegister(getRegisterConfig()).asValue(ip.getValueKind()); + emitMove(exitThroughRegisterValue, ip); + append(configuration.createExitMethodAddressResolutionOp(exitThroughRegisterValue)); + } } public class SubstrateAArch64NodeLIRBuilder extends AArch64NodeLIRBuilder implements SubstrateNodeLIRBuilder { 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 97ab24224391..1f3ffa102aff 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 @@ -93,6 +93,7 @@ import com.oracle.svm.core.meta.SubstrateMethodPointerConstant; import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.core.nodes.SafepointCheckNode; +import com.oracle.svm.core.pltgot.PLTGOTConfiguration; import com.oracle.svm.core.thread.VMThreads.StatusSupport; import com.oracle.svm.core.util.VMError; @@ -748,6 +749,14 @@ public void emitInstructionSynchronizationBarrier() { throw shouldNotReachHere("AMD64 does not need instruction synchronization"); } + @Override + public void emitExitMethodAddressResolution(Value ip) { + PLTGOTConfiguration configuration = PLTGOTConfiguration.singleton(); + RegisterValue exitThroughRegisterValue = configuration.getExitMethodAddressResolutionRegister(getRegisterConfig()).asValue(ip.getValueKind()); + emitMove(exitThroughRegisterValue, ip); + append(configuration.createExitMethodAddressResolutionOp(exitThroughRegisterValue)); + } + @Override public void emitFarReturn(AllocatableValue result, Value sp, Value ip, boolean fromMethodWithCalleeSavedRegisters) { append(new AMD64FarReturnOp(result, asAllocatable(sp), asAllocatable(ip), fromMethodWithCalleeSavedRegisters)); @@ -1813,7 +1822,7 @@ public void emitCode(CompilationResultBuilder crb, ResolvedJavaMethod installedC } } - private AMD64Assembler createAssemblerNoOptions() { + public AMD64Assembler createAssemblerNoOptions() { OptionValues o = new OptionValues(EconomicMap.create()); return createAssembler(o); } diff --git a/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMGenerator.java b/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMGenerator.java index 4761dbda4642..4e213d0a817a 100644 --- a/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMGenerator.java +++ b/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMGenerator.java @@ -1147,6 +1147,11 @@ public void emitInstructionSynchronizationBarrier() { throw unimplemented("the LLVM backend doesn't support instruction synchronization"); // ExcludeFromJacocoGeneratedReport } + @Override + public void emitExitMethodAddressResolution(Value ip) { + throw unimplemented("the LLVM backend doesn't support PLT/GOT"); // ExcludeFromJacocoGeneratedReport + } + @Override public I append(I op) { throw unimplemented("the LLVM backend doesn't support LIR instructions"); // ExcludeFromJacocoGeneratedReport diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinGOTHeapSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinGOTHeapSupport.java new file mode 100644 index 000000000000..489fea0db173 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinGOTHeapSupport.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2023, 2024, 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.posix.darwin; + +import static com.oracle.svm.core.posix.headers.Mman.MAP_ANON; +import static com.oracle.svm.core.posix.headers.Mman.MAP_FAILED; +import static com.oracle.svm.core.posix.headers.Mman.MAP_PRIVATE; +import static com.oracle.svm.core.posix.headers.Mman.PROT_READ; +import static com.oracle.svm.core.posix.headers.Mman.PROT_WRITE; +import static com.oracle.svm.core.posix.headers.Mman.NoTransitions.mmap; + +import org.graalvm.nativeimage.StackValue; +import org.graalvm.nativeimage.c.type.CIntPointer; +import org.graalvm.nativeimage.c.type.WordPointer; +import org.graalvm.word.Pointer; +import org.graalvm.word.WordFactory; + +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.c.CGlobalData; +import com.oracle.svm.core.c.CGlobalDataFactory; +import com.oracle.svm.core.c.function.CEntryPointErrors; +import com.oracle.svm.core.headers.LibC; +import com.oracle.svm.core.os.VirtualMemoryProvider; +import com.oracle.svm.core.os.VirtualMemoryProvider.Access; +import com.oracle.svm.core.pltgot.GOTHeapSupport; +import com.oracle.svm.core.posix.headers.darwin.DarwinVirtualMemory; + +public class DarwinGOTHeapSupport extends GOTHeapSupport { + + private static final CGlobalData DARWIN_GOT_START_ADDRESS = CGlobalDataFactory.createWord(); + + @Override + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + protected int initialize(WordPointer gotStartAddress) { + int flags = MAP_ANON() | MAP_PRIVATE(); + Pointer gotMemory = mmap(WordFactory.nullPointer(), getPageAlignedGotSize(), PROT_READ() | PROT_WRITE(), flags, -1, 0); + if (gotMemory.isNull() || gotMemory.equal(MAP_FAILED())) { + return CEntryPointErrors.DYNAMIC_METHOD_ADDRESS_RESOLUTION_GOT_FD_CREATE_FAILED; + } + + Pointer gotStartInMemory = gotMemory.add(getGotOffsetFromStartOfMapping()); + LibC.memcpy(gotStartInMemory, IMAGE_GOT_BEGIN.get(), getGotSectionSize()); + + gotStartAddress.write(gotMemory); + DARWIN_GOT_START_ADDRESS.get().write(gotMemory); + + return CEntryPointErrors.NO_ERROR; + } + + @Override + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public int mapGot(Pointer start) { + WordPointer taskSelf = DarwinVirtualMemory.mach_task_self(); + + /* Unmap part of the heap address space that is designated for the GOT */ + int ret = DarwinVirtualMemory.vm_deallocate(DarwinVirtualMemory.mach_task_self(), start, getPageAlignedGotSize()); + if (ret != 0) { + return CEntryPointErrors.DYNAMIC_METHOD_ADDRESS_RESOLUTION_GOT_MMAP_FAILED; + } + + WordPointer gotStart = StackValue.get(WordPointer.class); + gotStart.write(start); + + CIntPointer currentProt = StackValue.get(CIntPointer.class); + CIntPointer maxProt = StackValue.get(CIntPointer.class); + + int intFalse = 0; + + /* + * Map reserved address space for GOT to "global" GOT allocation, so that all isolates are + * backed by the same table. + */ + ret = DarwinVirtualMemory.vm_remap(taskSelf, gotStart, getPageAlignedGotSize(), WordFactory.nullPointer(), intFalse, + taskSelf, DARWIN_GOT_START_ADDRESS.get().read(), intFalse, currentProt, maxProt, DarwinVirtualMemory.VM_INHERIT_SHARE()); + if (ret != 0) { + return CEntryPointErrors.DYNAMIC_METHOD_ADDRESS_RESOLUTION_GOT_WRONG_MMAP; + } + + /* + * The new mapping "inherits" cur_prot and max_prot from the original mapping, but another + * isolate could race trying to write the original GOT => the new mapping could inherit + * cur_prot=RW. Ensure that the new-mapping remains read-only, regardless of races. + */ + if (currentProt.read() != PROT_READ()) { + ret = VirtualMemoryProvider.get().protect(start, getPageAlignedGotSize(), Access.READ); + if (ret != 0) { + return ret; + } + } + + return CEntryPointErrors.NO_ERROR; + } +} diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinPLTGOTFeature.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinPLTGOTFeature.java new file mode 100644 index 000000000000..d15b4d87b74f --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinPLTGOTFeature.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023, 2024, 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.posix.darwin; + +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; + +import com.oracle.svm.core.code.DynamicMethodAddressResolutionHeapSupport; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.hosted.pltgot.PLTGOTOptions; + +@AutomaticallyRegisteredFeature +public class DarwinPLTGOTFeature implements InternalFeature { + + @Override + public boolean isInConfiguration(IsInConfigurationAccess access) { + return Platform.includedIn(Platform.DARWIN.class) && PLTGOTOptions.EnablePLTGOT.getValue(); + } + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + ImageSingletons.add(DynamicMethodAddressResolutionHeapSupport.class, new DarwinGOTHeapSupport()); + } +} diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxGOTHeapSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxGOTHeapSupport.java new file mode 100644 index 000000000000..cddb80ab1096 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxGOTHeapSupport.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2023, 2024, 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.posix.linux; + +import static com.oracle.svm.core.posix.headers.Mman.MAP_SHARED; +import static com.oracle.svm.core.posix.headers.Mman.PROT_READ; +import static com.oracle.svm.core.posix.headers.Mman.PROT_WRITE; +import static com.oracle.svm.core.posix.headers.Mman.NoTransitions.mmap; +import static com.oracle.svm.core.posix.headers.Mman.NoTransitions.shm_open; +import static com.oracle.svm.core.posix.headers.Mman.NoTransitions.shm_unlink; + +import org.graalvm.nativeimage.StackValue; +import org.graalvm.nativeimage.c.struct.SizeOf; +import org.graalvm.nativeimage.c.type.CCharPointer; +import org.graalvm.nativeimage.c.type.WordPointer; +import org.graalvm.word.Pointer; +import org.graalvm.word.SignedWord; +import org.graalvm.word.UnsignedWord; +import org.graalvm.word.WordBase; +import org.graalvm.word.WordFactory; + +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.c.CGlobalData; +import com.oracle.svm.core.c.CGlobalDataFactory; +import com.oracle.svm.core.c.function.CEntryPointErrors; +import com.oracle.svm.core.headers.LibC; +import com.oracle.svm.core.os.VirtualMemoryProvider; +import com.oracle.svm.core.os.VirtualMemoryProvider.Access; +import com.oracle.svm.core.pltgot.GOTHeapSupport; +import com.oracle.svm.core.posix.headers.Errno; +import com.oracle.svm.core.posix.headers.Fcntl; +import com.oracle.svm.core.posix.headers.Unistd; +import com.oracle.svm.core.thread.VMThreads; + +public class LinuxGOTHeapSupport extends GOTHeapSupport { + + private static final String FILE_NAME_PREFIX = "/ni-got-"; + private static final int FILE_NAME_PREFIX_LEN = FILE_NAME_PREFIX.length(); + private static final CGlobalData memoryViewFd = CGlobalDataFactory.createWord((WordBase) WordFactory.signed(-1)); + private static final CGlobalData fileNamePrefix = CGlobalDataFactory.createCString(FILE_NAME_PREFIX); + + @Override + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + protected int initialize(WordPointer gotStartAddress) { + int pid = Unistd.NoTransitions.getpid(); + int pidLen = 0; + int temp = pid; + while (temp != 0) { + pidLen++; + temp /= 10; + } + + /* GOT shared memory file name format: fileNamePrefix-pid */ + CCharPointer nameStr = StackValue.get(64 * SizeOf.get(CCharPointer.class)); + LibC.memcpy(nameStr, fileNamePrefix.get(), WordFactory.unsigned(FILE_NAME_PREFIX_LEN)); + + int iter = FILE_NAME_PREFIX_LEN + pidLen; + nameStr.write(iter, (byte) '\0'); + temp = pid; + + while (temp != 0) { + iter--; + nameStr.write(iter, (byte) ('0' + (temp % 10))); + temp /= 10; + } + + int fd = -1; + for (int i = 0; i < 10; ++i) { + fd = shm_open(nameStr, Fcntl.O_CREAT() | Fcntl.O_EXCL() | Fcntl.O_RDWR(), 0); + if (fd == -1) { + int errno = LibC.errno(); + if (errno != Errno.EEXIST()) { + return CEntryPointErrors.DYNAMIC_METHOD_ADDRESS_RESOLUTION_GOT_FD_CREATE_FAILED; + } + VMThreads.singleton().nativeSleep(5); + } else { + shm_unlink(nameStr); + break; + } + } + + if (fd == -1) { + return CEntryPointErrors.DYNAMIC_METHOD_ADDRESS_RESOLUTION_GOT_UNIQUE_FILE_CREATE_FAILED; + } + + UnsignedWord gotPageAlignedSize = getPageAlignedGotSize(); + + if (Unistd.NoTransitions.ftruncate(fd, WordFactory.signed(gotPageAlignedSize.rawValue())) != 0) { + Unistd.NoTransitions.close(fd); + return CEntryPointErrors.DYNAMIC_METHOD_ADDRESS_RESOLUTION_GOT_FD_RESIZE_FAILED; + } + + Pointer gotMemory = mmap(WordFactory.nullPointer(), gotPageAlignedSize, PROT_READ() | PROT_WRITE(), MAP_SHARED(), fd, 0); + if (gotMemory.isNull()) { + Unistd.NoTransitions.close(fd); + return CEntryPointErrors.DYNAMIC_METHOD_ADDRESS_RESOLUTION_GOT_FD_MAP_FAILED; + } + + Pointer gotStartInMemory = gotMemory.add(getGotOffsetFromStartOfMapping()); + LibC.memcpy(gotStartInMemory, IMAGE_GOT_BEGIN.get(), getGotSectionSize()); + + /* Keep the initial GOT mapping for writing. */ + + memoryViewFd.get().write(WordFactory.signed(fd)); + gotStartAddress.write(gotMemory); + + return CEntryPointErrors.NO_ERROR; + } + + @Override + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public int mapGot(Pointer start) { + SignedWord memViewFd = memoryViewFd.get().read(); + if (memViewFd.lessThan(0)) { + return CEntryPointErrors.DYNAMIC_METHOD_ADDRESS_RESOLUTION_GOT_FD_INVALID; + } + + Pointer mappedAddress = VirtualMemoryProvider.get().mapFile( + start, + getPageAlignedGotSize(), + memViewFd, + WordFactory.zero(), + Access.READ); + + if (mappedAddress.isNull()) { + return CEntryPointErrors.DYNAMIC_METHOD_ADDRESS_RESOLUTION_GOT_MMAP_FAILED; + } + + return CEntryPointErrors.NO_ERROR; + } +} diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxPLTGOTFeature.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxPLTGOTFeature.java new file mode 100644 index 000000000000..3accab55cfe2 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxPLTGOTFeature.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023, 2024, 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.posix.linux; + +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; + +import com.oracle.svm.core.code.DynamicMethodAddressResolutionHeapSupport; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.hosted.pltgot.PLTGOTOptions; + +@AutomaticallyRegisteredFeature +public class LinuxPLTGOTFeature implements InternalFeature { + + @Override + public boolean isInConfiguration(IsInConfigurationAccess access) { + return Platform.includedIn(Platform.LINUX.class) && PLTGOTOptions.EnablePLTGOT.getValue(); + } + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + ImageSingletons.add(DynamicMethodAddressResolutionHeapSupport.class, new LinuxGOTHeapSupport()); + } +} diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsGOTHeapSupport.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsGOTHeapSupport.java new file mode 100644 index 000000000000..85081e020f09 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsGOTHeapSupport.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2023, 2024, 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.windows; + +import org.graalvm.nativeimage.c.type.WordPointer; +import org.graalvm.word.Pointer; +import org.graalvm.word.UnsignedWord; +import org.graalvm.word.WordFactory; + +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.c.CGlobalData; +import com.oracle.svm.core.c.CGlobalDataFactory; +import com.oracle.svm.core.c.function.CEntryPointErrors; +import com.oracle.svm.core.headers.LibC; +import com.oracle.svm.core.os.VirtualMemoryProvider; +import com.oracle.svm.core.os.VirtualMemoryProvider.Access; +import com.oracle.svm.core.pltgot.GOTHeapSupport; +import com.oracle.svm.core.util.UnsignedUtils; +import com.oracle.svm.core.windows.headers.MemoryAPI; +import com.oracle.svm.core.windows.headers.WinBase; +import com.oracle.svm.core.windows.headers.WinBase.HANDLE; + +public class WindowsGOTHeapSupport extends GOTHeapSupport { + + private static final CGlobalData GOT_MAPPING_HANDLE = CGlobalDataFactory.createWord(); + + @Override + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + protected int initialize(WordPointer gotStartAddress) { + UnsignedWord alignedGotSize = getPageAlignedGotSize(); + + HANDLE gotMappingHandle = MemoryAPI.CreateFileMappingW( + WinBase.INVALID_HANDLE_VALUE(), // in-memory + WordFactory.nullPointer(), + MemoryAPI.PAGE_READWRITE(), + 0, + UnsignedUtils.safeToInt(alignedGotSize), + WordFactory.nullPointer() // anonymous + ); + + if (gotMappingHandle.isNull()) { + return CEntryPointErrors.DYNAMIC_METHOD_ADDRESS_RESOLUTION_GOT_FD_CREATE_FAILED; + } + + Pointer gotMappedAddress = MemoryAPI.MapViewOfFile( + gotMappingHandle, + MemoryAPI.FILE_MAP_READ() | MemoryAPI.FILE_MAP_WRITE(), + 0, + 0, + WordFactory.zero() // map it whole + ); + + if (gotMappedAddress.isNull()) { + WinBase.CloseHandle(gotMappingHandle); + return CEntryPointErrors.DYNAMIC_METHOD_ADDRESS_RESOLUTION_GOT_FD_MAP_FAILED; + } + + Pointer gotStartInMemory = gotMappedAddress.add(getGotOffsetFromStartOfMapping()); + LibC.memcpy(gotStartInMemory, IMAGE_GOT_BEGIN.get(), getGotSectionSize()); + + // Keep the initial GOT mapping for writing. + + GOT_MAPPING_HANDLE.get().write(gotMappingHandle); + gotStartAddress.write(gotMappedAddress); + + return CEntryPointErrors.NO_ERROR; + } + + @Override + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public int mapGot(Pointer address) { + HANDLE gotMappingHandle = GOT_MAPPING_HANDLE.get().read(); + if (gotMappingHandle.isNull()) { + return CEntryPointErrors.DYNAMIC_METHOD_ADDRESS_RESOLUTION_GOT_FD_INVALID; + } + + Pointer mappedAddress = VirtualMemoryProvider.get().mapFile( + address, + getPageAlignedGotSize(), + gotMappingHandle, + WordFactory.zero(), + Access.READ); + + if (mappedAddress.isNull()) { + return CEntryPointErrors.DYNAMIC_METHOD_ADDRESS_RESOLUTION_GOT_MMAP_FAILED; + } + + return CEntryPointErrors.NO_ERROR; + } +} diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsPLTGOTFeature.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsPLTGOTFeature.java new file mode 100644 index 000000000000..ee5d4522f970 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsPLTGOTFeature.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023, 2024, 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.windows; + +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; + +import com.oracle.svm.core.code.DynamicMethodAddressResolutionHeapSupport; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.hosted.pltgot.PLTGOTOptions; + +@AutomaticallyRegisteredFeature +public class WindowsPLTGOTFeature implements InternalFeature { + + @Override + public boolean isInConfiguration(IsInConfigurationAccess access) { + return Platform.includedIn(Platform.WINDOWS.class) && PLTGOTOptions.EnablePLTGOT.getValue(); + } + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + ImageSingletons.add(DynamicMethodAddressResolutionHeapSupport.class, new WindowsGOTHeapSupport()); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/code/SubstrateLIRGenerator.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/code/SubstrateLIRGenerator.java index 22109f78d166..a9b2ffc401a9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/code/SubstrateLIRGenerator.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/code/SubstrateLIRGenerator.java @@ -36,4 +36,6 @@ public interface SubstrateLIRGenerator { void emitVerificationMarker(Object marker); void emitInstructionSynchronizationBarrier(); + + void emitExitMethodAddressResolution(Value ip); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/ExitMethodAddressResolutionNode.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/ExitMethodAddressResolutionNode.java new file mode 100644 index 000000000000..6d76253e0888 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/ExitMethodAddressResolutionNode.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023, 2024, 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.pltgot; + +import org.graalvm.nativeimage.c.function.CodePointer; + +import com.oracle.svm.core.graal.code.SubstrateLIRGenerator; + +import jdk.graal.compiler.core.common.type.StampFactory; +import jdk.graal.compiler.graph.NodeClass; +import jdk.graal.compiler.lir.gen.LIRGeneratorTool; +import jdk.graal.compiler.nodeinfo.NodeCycles; +import jdk.graal.compiler.nodeinfo.NodeInfo; +import jdk.graal.compiler.nodeinfo.NodeSize; +import jdk.graal.compiler.nodes.ControlSinkNode; +import jdk.graal.compiler.nodes.ValueNode; +import jdk.graal.compiler.nodes.spi.LIRLowerable; +import jdk.graal.compiler.nodes.spi.NodeLIRBuilderTool; + +@NodeInfo(cycles = NodeCycles.CYCLES_1, size = NodeSize.SIZE_1) +public final class ExitMethodAddressResolutionNode extends ControlSinkNode implements LIRLowerable { + public static final NodeClass TYPE = NodeClass.create(ExitMethodAddressResolutionNode.class); + + @Input protected ValueNode ip; + + public ExitMethodAddressResolutionNode(ValueNode ip) { + super(TYPE, StampFactory.forVoid()); + this.ip = ip; + } + + @Override + public void generate(NodeLIRBuilderTool builder) { + LIRGeneratorTool gen = builder.getLIRGeneratorTool(); + ((SubstrateLIRGenerator) gen).emitExitMethodAddressResolution(builder.operand(ip)); + } + + @NodeIntrinsic + public static native void exitMethodAddressResolution(CodePointer ip); +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/GOTAccess.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/GOTAccess.java new file mode 100644 index 000000000000..6c8cffc4f24d --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/GOTAccess.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023, 2024, 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.pltgot; + +import org.graalvm.word.Pointer; +import org.graalvm.word.UnsignedWord; + +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.config.ConfigurationValues; + +import jdk.graal.compiler.word.Word; + +public class GOTAccess { + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public static int getGotEntryOffsetFromHeapRegister(int gotEntry) { + return -(gotEntry + 1) * ConfigurationValues.getTarget().wordSize; + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public static void writeToGotEntry(int gotEntry, UnsignedWord address) { + Pointer gotStartAddress = GOTHeapSupport.GOT_START_ADDRESS.get().read(); + Pointer gotEndAddress = gotStartAddress.add(GOTHeapSupport.getPageAlignedGotSize()); + gotEndAddress.writeWord(getGotEntryOffsetFromHeapRegister(gotEntry), address); + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public static Word readFromGotEntry(int gotEntry) { + Pointer gotStartAddress = GOTHeapSupport.GOT_START_ADDRESS.get().read(); + Pointer gotEndAddress = gotStartAddress.add(GOTHeapSupport.getPageAlignedGotSize()); + return gotEndAddress.readWord(getGotEntryOffsetFromHeapRegister(gotEntry)); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/GOTHeapSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/GOTHeapSupport.java new file mode 100644 index 000000000000..eb26f7b4aff5 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/GOTHeapSupport.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2023, 2024, 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.pltgot; + +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.c.type.WordPointer; +import org.graalvm.word.LocationIdentity; +import org.graalvm.word.Pointer; +import org.graalvm.word.PointerBase; +import org.graalvm.word.SignedWord; +import org.graalvm.word.UnsignedWord; +import org.graalvm.word.WordFactory; + +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.c.CGlobalData; +import com.oracle.svm.core.c.CGlobalDataFactory; +import com.oracle.svm.core.c.function.CEntryPointErrors; +import com.oracle.svm.core.code.DynamicMethodAddressResolutionHeapSupport; +import com.oracle.svm.core.os.VirtualMemoryProvider; +import com.oracle.svm.core.os.VirtualMemoryProvider.Access; +import com.oracle.svm.core.snippets.KnownIntrinsics; +import com.oracle.svm.core.util.PointerUtils; +import com.oracle.svm.core.util.VMError; + +import jdk.graal.compiler.api.replacements.Fold; +import jdk.graal.compiler.nodes.PauseNode; + +public abstract class GOTHeapSupport extends DynamicMethodAddressResolutionHeapSupport { + + public static final String IMAGE_GOT_END_SYMBOL_NAME = "__svm_got_end"; + public static final CGlobalData IMAGE_GOT_END = CGlobalDataFactory.forSymbol(IMAGE_GOT_END_SYMBOL_NAME); + public static final String IMAGE_GOT_BEGIN_SYMBOL_NAME = "__svm_got_begin"; + public static final CGlobalData IMAGE_GOT_BEGIN = CGlobalDataFactory.forSymbol(IMAGE_GOT_BEGIN_SYMBOL_NAME); + + private static final SignedWord GOT_UNINITIALIZED = WordFactory.signed(-1); + private static final SignedWord GOT_INITIALIZATION_IN_PROGRESS = WordFactory.signed(-2); + private static final CGlobalData GOT_STATUS = CGlobalDataFactory.createWord(GOT_UNINITIALIZED); + static final CGlobalData GOT_START_ADDRESS = CGlobalDataFactory.createWord(); + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + protected static UnsignedWord getGotSectionSize() { + return IMAGE_GOT_END.get().subtract(IMAGE_GOT_BEGIN.get()); + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + protected static UnsignedWord getPageAlignedGotSize() { + UnsignedWord gotSectionSize = getGotSectionSize(); + UnsignedWord pageSize = VirtualMemoryProvider.get().getGranularity(); + return PointerUtils.roundUp((PointerBase) gotSectionSize, pageSize); + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + protected static UnsignedWord getGotOffsetFromStartOfMapping() { + return getPageAlignedGotSize().subtract(getGotSectionSize()); + } + + @Override + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public UnsignedWord getRequiredPreHeapMemoryInBytes() { + return getPageAlignedGotSize(); + } + + @Fold + public static GOTHeapSupport get() { + return (GOTHeapSupport) ImageSingletons.lookup(DynamicMethodAddressResolutionHeapSupport.class); + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public void makeGOTWritable() { + changeGOTMappingProtection(true); + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public void makeGOTReadOnly() { + changeGOTMappingProtection(false); + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + protected abstract int mapGot(Pointer address); + + @Override + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public int install(Pointer heapBase) { + return mapGot(getPreHeapMappingStartAddress(heapBase)); + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + protected Pointer getPreHeapMappingStartAddress() { + return getPreHeapMappingStartAddress(KnownIntrinsics.heapBase()); + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + protected Pointer getPreHeapMappingStartAddress(PointerBase heapBase) { + return ((Pointer) heapBase).subtract(getRequiredPreHeapMemoryInBytes()); + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + private static void changeGOTMappingProtection(boolean writable) { + Pointer gotMappingStartAddress = GOT_START_ADDRESS.get().read(); + VMError.guarantee(gotMappingStartAddress.isNonNull()); + int access = Access.READ; + if (writable) { + access |= Access.WRITE; + } + int ret = VirtualMemoryProvider.get().protect(gotMappingStartAddress, getPageAlignedGotSize(), access); + VMError.guarantee(ret == 0, "Failed to change GOT protection."); + } + + @Override + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public int initialize() { + boolean isFirstIsolate = GOT_STATUS.get().logicCompareAndSwapWord(0, GOT_UNINITIALIZED, GOT_INITIALIZATION_IN_PROGRESS, LocationIdentity.ANY_LOCATION); + if (!isFirstIsolate) { + while (true) { + SignedWord status = GOT_STATUS.get().readWordVolatile(0, LocationIdentity.ANY_LOCATION); + if (status.notEqual(GOT_INITIALIZATION_IN_PROGRESS)) { + long rawStatus = status.rawValue(); + assert rawStatus == (int) rawStatus; + return (int) rawStatus; + } + /* Being nice to the CPU while busy waiting */ + PauseNode.pause(); + } + } + + // Only the first isolate can reach here. + int ret = initialize(GOT_START_ADDRESS.get()); + if (ret == CEntryPointErrors.NO_ERROR) { + makeGOTReadOnly(); + } + GOT_STATUS.get().writeWordVolatile(0, WordFactory.signed(ret)); + return ret; + } + + /** + * Initialize the GOT and write its address to {@code gotStartAddress}. Return + * {@link CEntryPointErrors#NO_ERROR} on success, an error code otherwise. + */ + protected abstract int initialize(WordPointer gotStartAddress); +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/IdentityMethodAddressResolver.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/IdentityMethodAddressResolver.java new file mode 100644 index 000000000000..996d6cf8d4a5 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/IdentityMethodAddressResolver.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023, 2024, 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.pltgot; + +import org.graalvm.word.Pointer; +import org.graalvm.word.UnsignedWord; +import org.graalvm.word.WordFactory; + +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.c.CGlobalData; +import com.oracle.svm.core.c.CGlobalDataFactory; +import com.oracle.svm.core.config.ConfigurationValues; + +public class IdentityMethodAddressResolver implements MethodAddressResolver { + + private static final CGlobalData methodTable = CGlobalDataFactory.forSymbol("__svm_methodtable_begin"); + + @Override + @Uninterruptible(reason = "Called from the PLT stub where stack walks are not safe.") + public long resolveMethodWithGotEntry(long gotEntry) { + /* Fetch the absolute address of the method that corresponds to the target GOT entry. */ + UnsignedWord methodTableOffset = WordFactory.unsigned(gotEntry).multiply(ConfigurationValues.getTarget().wordSize); + UnsignedWord address = methodTable.get().readWord(methodTableOffset); + /* + * Write the resolved address to the GOT entry so that it can be directly used for future + * calls instead of going through this resolver. + */ + GOTAccess.writeToGotEntry((int) gotEntry, address); + + return address.rawValue(); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/MethodAddressResolutionDispatcher.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/MethodAddressResolutionDispatcher.java new file mode 100644 index 000000000000..6d95f0e43975 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/MethodAddressResolutionDispatcher.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2023, 2024, 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.pltgot; + +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.thread.JavaSpinLockUtils; + +import jdk.internal.misc.Unsafe; + +public class MethodAddressResolutionDispatcher { + private static final MethodAddressResolutionDispatcher dispatcher = new MethodAddressResolutionDispatcher(); + private static final long LOCK_OFFSET = Unsafe.getUnsafe().objectFieldOffset(MethodAddressResolutionDispatcher.class, "lock"); + + @SuppressWarnings("unused")// + private volatile int lock; + private int activeResolverInstances = 0; + + @Uninterruptible(reason = "PLT/GOT method address resolution doesn't support interruptible code paths.") + protected static long resolveMethodAddress(long gotEntry) { + try { + JavaSpinLockUtils.lockNoTransition(dispatcher, LOCK_OFFSET); + if (dispatcher.activeResolverInstances == 0) { + GOTHeapSupport.get().makeGOTWritable(); + } + dispatcher.activeResolverInstances++; + } finally { + JavaSpinLockUtils.unlock(dispatcher, LOCK_OFFSET); + } + + long resolvedMethodAddress = PLTGOTConfiguration.singleton().getMethodAddressResolver().resolveMethodWithGotEntry(gotEntry); + + try { + JavaSpinLockUtils.lockNoTransition(dispatcher, LOCK_OFFSET); + if (dispatcher.activeResolverInstances == 1) { + GOTHeapSupport.get().makeGOTReadOnly(); + } + dispatcher.activeResolverInstances--; + } finally { + JavaSpinLockUtils.unlock(dispatcher, LOCK_OFFSET); + } + return resolvedMethodAddress; + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/MethodAddressResolver.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/MethodAddressResolver.java new file mode 100644 index 000000000000..ee45a0204c51 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/MethodAddressResolver.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023, 2024, 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.pltgot; + +import com.oracle.svm.core.Uninterruptible; + +public interface MethodAddressResolver { + + /** + * Resolves the absolute address of a method represented by the given GOT entry. + * + * Note that it is the responsibility of this method to write the resolved address to the GOT + * entry as otherwise it will be called for subsequent calls of the same method. + */ + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + long resolveMethodWithGotEntry(long gotEntry); + +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/PLTGOTConfiguration.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/PLTGOTConfiguration.java new file mode 100644 index 000000000000..f880c0b20f3c --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/PLTGOTConfiguration.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, 2024, 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.pltgot; + +import org.graalvm.nativeimage.ImageSingletons; + +import jdk.graal.compiler.api.replacements.Fold; +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.RegisterConfig; +import jdk.vm.ci.code.RegisterValue; + +public abstract class PLTGOTConfiguration { + protected MethodAddressResolver methodAddressResolver; + + @Fold + public static PLTGOTConfiguration singleton() { + return ImageSingletons.lookup(PLTGOTConfiguration.class); + } + + @Fold + public MethodAddressResolver getMethodAddressResolver() { + return methodAddressResolver; + } + + public abstract Register getExitMethodAddressResolutionRegister(RegisterConfig registerConfig); + + public abstract LIRInstruction createExitMethodAddressResolutionOp(RegisterValue exitThroughRegisterValue); + +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/aarch64/AArch64ExitMethodAddressResolutionOp.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/aarch64/AArch64ExitMethodAddressResolutionOp.java new file mode 100644 index 000000000000..4f824a1a146f --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/aarch64/AArch64ExitMethodAddressResolutionOp.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, 2024, 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.pltgot.aarch64; + +import static jdk.vm.ci.code.ValueUtil.asRegister; + +import jdk.graal.compiler.asm.aarch64.AArch64MacroAssembler; +import jdk.graal.compiler.lir.LIRInstructionClass; +import jdk.graal.compiler.lir.Opcode; +import jdk.graal.compiler.lir.aarch64.AArch64BlockEndOp; +import jdk.graal.compiler.lir.asm.CompilationResultBuilder; +import jdk.vm.ci.meta.Value; + +@Opcode("EXIT_METHOD_ADDRESS_RESOLUTION") +public class AArch64ExitMethodAddressResolutionOp extends AArch64BlockEndOp { + public static final LIRInstructionClass TYPE = LIRInstructionClass.create(AArch64ExitMethodAddressResolutionOp.class); + private @Use Value ip; + + public AArch64ExitMethodAddressResolutionOp(Value ip) { + super(TYPE); + this.ip = ip; + } + + @Override + protected void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) { + crb.frameContext.leave(crb); + masm.jmp(asRegister(ip)); + crb.frameContext.returned(crb); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/aarch64/AArch64MethodAddressResolutionDispatcher.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/aarch64/AArch64MethodAddressResolutionDispatcher.java new file mode 100644 index 000000000000..15dcfb3f23f6 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/aarch64/AArch64MethodAddressResolutionDispatcher.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023, 2024, 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.pltgot.aarch64; + +import static com.oracle.svm.core.pltgot.ExitMethodAddressResolutionNode.exitMethodAddressResolution; + +import org.graalvm.word.Pointer; +import org.graalvm.word.WordFactory; + +import com.oracle.svm.core.NeverInline; +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.deopt.DeoptimizationSlotPacking; +import com.oracle.svm.core.graal.code.StubCallingConvention; +import com.oracle.svm.core.pltgot.MethodAddressResolutionDispatcher; +import com.oracle.svm.core.snippets.KnownIntrinsics; + +import jdk.graal.compiler.nodes.UnreachableNode; + +public final class AArch64MethodAddressResolutionDispatcher extends MethodAddressResolutionDispatcher { + /** + * This method is never called directly, instead we jump to it through a scratch register from + * the PLT stub (see {@code AArch64PLTStubGenerator}). The PLT stub writes the GOT entry into + * the stack frame padding space after the return address that is conventionally used for the + * deopt frame data. We do this in order to avoid having to spill the argument passing registers + * to the stack when we call @{code resolveMethodAddress}. Note that we cannot use the full + * word, see {@link DeoptimizationSlotPacking} on the convention that should be used. + */ + @StubCallingConvention + @Uninterruptible(reason = "PLT/GOT method address resolution doesn't support interruptible code paths.") + @NeverInline("This method must never be inlined or called directly because we only jump to it from the PLT stub.") + public static void resolveMethodAddress() { + Pointer paddingSlot = KnownIntrinsics.readCallerStackPointer(); + long gotEntry = DeoptimizationSlotPacking.decodeGOTIndex(paddingSlot.readWord(0).rawValue()); + long resolvedMethodAddress = MethodAddressResolutionDispatcher.resolveMethodAddress(gotEntry); + exitMethodAddressResolution(WordFactory.pointer(resolvedMethodAddress)); + throw UnreachableNode.unreachable(); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/amd64/AMD64ExitMethodAddressResolutionOp.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/amd64/AMD64ExitMethodAddressResolutionOp.java new file mode 100644 index 000000000000..8c10e02166d5 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/amd64/AMD64ExitMethodAddressResolutionOp.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, 2024, 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.pltgot.amd64; + +import static jdk.vm.ci.code.ValueUtil.asRegister; + +import jdk.graal.compiler.asm.amd64.AMD64MacroAssembler; +import jdk.graal.compiler.lir.LIRInstructionClass; +import jdk.graal.compiler.lir.Opcode; +import jdk.graal.compiler.lir.amd64.AMD64BlockEndOp; +import jdk.graal.compiler.lir.asm.CompilationResultBuilder; +import jdk.vm.ci.meta.Value; + +@Opcode("EXIT_METHOD_ADDRESS_RESOLUTION") +public class AMD64ExitMethodAddressResolutionOp extends AMD64BlockEndOp { + public static final LIRInstructionClass TYPE = LIRInstructionClass.create(AMD64ExitMethodAddressResolutionOp.class); + private @Use Value ip; + + public AMD64ExitMethodAddressResolutionOp(Value ip) { + super(TYPE); + this.ip = ip; + } + + @Override + public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) { + crb.frameContext.leave(crb); + masm.jmp(asRegister(ip)); + crb.frameContext.returned(crb); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/amd64/AMD64MethodAddressResolutionDispatcher.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/amd64/AMD64MethodAddressResolutionDispatcher.java new file mode 100644 index 000000000000..8f06638f0394 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/pltgot/amd64/AMD64MethodAddressResolutionDispatcher.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023, 2024, 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.pltgot.amd64; + +import static com.oracle.svm.core.pltgot.ExitMethodAddressResolutionNode.exitMethodAddressResolution; + +import org.graalvm.word.WordFactory; + +import com.oracle.svm.core.NeverInline; +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.graal.code.ExplicitCallingConvention; +import com.oracle.svm.core.graal.code.StubCallingConvention; +import com.oracle.svm.core.graal.code.SubstrateCallingConventionKind; +import com.oracle.svm.core.pltgot.MethodAddressResolutionDispatcher; + +import jdk.graal.compiler.nodes.UnreachableNode; + +public final class AMD64MethodAddressResolutionDispatcher extends MethodAddressResolutionDispatcher { + /** + * This method is never called directly, we jump to it from a PLT stub for a method with a given + * gotEntry. Because the {@link SubstrateCallingConventionKind#ForwardReturnValue} calling + * convention takes the value of its one and only parameter {@code gotEntry} from the return + * register, we load the got entry value of a method that we are resolving into the return value + * register in the PLT stub. + * + * In the lir op for {@code exitMethodAddressResolution} we jump through the return register to + * the {@code resolvedMethodAddress} and that's why the method {@code resolveMethodAddress} has + * to have a return kind {@code long} so that the calleeSaved register restore for this method + * doesn't override the return register. See + * {@link jdk.graal.compiler.lir.asm.FrameContext#leave}. + */ + @StubCallingConvention + @Uninterruptible(reason = "PLT/GOT method address resolution doesn't support interruptible code paths.") + @ExplicitCallingConvention(SubstrateCallingConventionKind.ForwardReturnValue) + @NeverInline("This method must never be inlined or called directly because we only jump to it from the PLT stub.") + public static long resolveMethodAddress(long gotEntry) { + long resolvedMethodAddress = MethodAddressResolutionDispatcher.resolveMethodAddress(gotEntry); + exitMethodAddressResolution(WordFactory.pointer(resolvedMethodAddress)); + throw UnreachableNode.unreachable(); + } +} diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/pltgot/GOTCall.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/pltgot/GOTCall.java new file mode 100644 index 000000000000..e397e8f53d42 --- /dev/null +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/pltgot/GOTCall.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023, 2024, 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.graal.pltgot; + +import jdk.vm.ci.code.DebugInfo; +import jdk.vm.ci.code.site.Infopoint; +import jdk.vm.ci.code.site.InfopointReason; + +public class GOTCall extends Infopoint { + public GOTCall(int pcOffset, DebugInfo debugInfo, InfopointReason reason) { + super(pcOffset, debugInfo, reason); + } +} diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/pltgot/PLTGOTNonSnippetLowerings.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/pltgot/PLTGOTNonSnippetLowerings.java new file mode 100644 index 000000000000..5216dc48dab4 --- /dev/null +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/pltgot/PLTGOTNonSnippetLowerings.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2023, 2024, 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.graal.pltgot; + +import java.util.Map; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.word.LocationIdentity; + +import com.oracle.svm.core.FrameAccess; +import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.graal.meta.KnownOffsets; +import com.oracle.svm.core.graal.meta.RuntimeConfiguration; +import com.oracle.svm.core.graal.snippets.NodeLoweringProvider; +import com.oracle.svm.core.graal.snippets.NonSnippetLowerings; +import com.oracle.svm.core.meta.SharedMethod; +import com.oracle.svm.core.pltgot.GOTAccess; +import com.oracle.svm.hosted.nodes.ReadReservedRegister; +import com.oracle.svm.hosted.pltgot.GOTEntryAllocator; +import com.oracle.svm.hosted.pltgot.HostedPLTGOTConfiguration; +import com.oracle.svm.hosted.pltgot.MethodAddressResolutionSupport; + +import jdk.graal.compiler.core.common.memory.BarrierType; +import jdk.graal.compiler.core.common.memory.MemoryOrderMode; +import jdk.graal.compiler.graph.Node; +import jdk.graal.compiler.graph.NodeInputList; +import jdk.graal.compiler.nodes.CallTargetNode; +import jdk.graal.compiler.nodes.ConstantNode; +import jdk.graal.compiler.nodes.FixedNode; +import jdk.graal.compiler.nodes.IndirectCallTargetNode; +import jdk.graal.compiler.nodes.InvokeNode; +import jdk.graal.compiler.nodes.InvokeWithExceptionNode; +import jdk.graal.compiler.nodes.LoweredCallTargetNode; +import jdk.graal.compiler.nodes.StructuredGraph; +import jdk.graal.compiler.nodes.ValueNode; +import jdk.graal.compiler.nodes.java.MethodCallTargetNode; +import jdk.graal.compiler.nodes.memory.ReadNode; +import jdk.graal.compiler.nodes.memory.address.OffsetAddressNode; +import jdk.vm.ci.code.CallingConvention; +import jdk.vm.ci.meta.JavaType; + +@Platforms(Platform.HOSTED_ONLY.class) +public final class PLTGOTNonSnippetLowerings { + + public static void registerLowerings(RuntimeConfiguration runtimeConfig, Map, NodeLoweringProvider> lowerings) { + InvokeThroughGOTLowering invokeLowering = new InvokeThroughGOTLowering(runtimeConfig); + lowerings.put(InvokeNode.class, invokeLowering); + lowerings.put(InvokeWithExceptionNode.class, invokeLowering); + } + + @Platforms(Platform.HOSTED_ONLY.class) + private static final class InvokeThroughGOTLowering extends NonSnippetLowerings.InvokeLowering { + + private final MethodAddressResolutionSupport methodAddressResolutionSupport; + private final GOTEntryAllocator gotEntryAllocator; + + InvokeThroughGOTLowering(RuntimeConfiguration runtimeConfig) { + super(runtimeConfig, SubstrateOptions.VerifyTypes.getValue(), KnownOffsets.singleton()); + this.methodAddressResolutionSupport = HostedPLTGOTConfiguration.singleton().getMethodAddressResolutionSupport(); + this.gotEntryAllocator = HostedPLTGOTConfiguration.singleton().getGOTEntryAllocator(); + } + + @Override + protected LoweredCallTargetNode createDirectCall(StructuredGraph graph, MethodCallTargetNode callTarget, NodeInputList parameters, JavaType[] signature, + CallingConvention.Type callType, CallTargetNode.InvokeKind invokeKind, SharedMethod callee, FixedNode node) { + SharedMethod caller = (SharedMethod) graph.method(); + if (methodAddressResolutionSupport.shouldCallViaPLTGOT(caller, callee)) { + ValueNode heapBaseNode = graph.addOrUnique(ReadReservedRegister.createReadHeapBaseNode(graph)); + int targetGotEntry = gotEntryAllocator.getMethodGotEntry(callee); + ValueNode offsetNode = ConstantNode.forIntegerKind(ConfigurationValues.getWordKind(), GOTAccess.getGotEntryOffsetFromHeapRegister(targetGotEntry), graph); + OffsetAddressNode offsetAddressNode = graph.unique(new OffsetAddressNode(heapBaseNode, offsetNode)); + ReadNode methodAddress = graph + .add(new ReadNode(offsetAddressNode, LocationIdentity.ANY_LOCATION, FrameAccess.getWordStamp(), BarrierType.NONE, MemoryOrderMode.PLAIN)); + SubstrateGOTCallTargetNode loweredCallTarget = graph.add( + new SubstrateGOTCallTargetNode(methodAddress, parameters.toArray(ValueNode.EMPTY_ARRAY), callTarget.returnStamp(), signature, callee, callType, invokeKind)); + + graph.addBeforeFixed(node, methodAddress); + return loweredCallTarget; + } + + return super.createDirectCall(graph, callTarget, parameters, signature, callType, invokeKind, callee, node); + } + + @Override + protected IndirectCallTargetNode createIndirectCall(StructuredGraph graph, MethodCallTargetNode callTarget, NodeInputList parameters, SharedMethod callee, JavaType[] signature, + CallingConvention.Type callType, CallTargetNode.InvokeKind invokeKind, ValueNode entry) { + SharedMethod caller = (SharedMethod) graph.method(); + /* + * We don't change how virtual methods are called; instead we make sure that the + * relocation to an appropriate PLT stub will be emitted in a vtable slot for the + * callee. + * + * This will force all the implementations of a callee method to be resolved via PLT/GOT + * mechanism. In the future, when we introduce the concept of hot and cold calls we + * could use a dispatch stub instead to reduce the number of GOT entries. + */ + if (methodAddressResolutionSupport.shouldCallViaPLTGOT(caller, callee)) { + for (SharedMethod implementation : callee.getImplementations()) { + gotEntryAllocator.reserveMethodGotEntry(implementation); + } + } + return super.createIndirectCall(graph, callTarget, parameters, callee, signature, callType, invokeKind, entry); + } + } + +} diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/pltgot/SubstrateGOTCallTargetNode.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/pltgot/SubstrateGOTCallTargetNode.java new file mode 100644 index 000000000000..c90c6f187114 --- /dev/null +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/pltgot/SubstrateGOTCallTargetNode.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2023, 2024, 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.graal.pltgot; + +import com.oracle.svm.core.nodes.SubstrateIndirectCallTargetNode; + +import jdk.graal.compiler.core.common.type.StampPair; +import jdk.graal.compiler.graph.NodeClass; +import jdk.graal.compiler.nodeinfo.NodeInfo; +import jdk.graal.compiler.nodes.ValueNode; +import jdk.vm.ci.code.CallingConvention; +import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.ResolvedJavaMethod; + +@NodeInfo +public final class SubstrateGOTCallTargetNode extends SubstrateIndirectCallTargetNode { + public static final NodeClass TYPE = NodeClass.create(SubstrateGOTCallTargetNode.class); + + public SubstrateGOTCallTargetNode(ValueNode computedAddress, ValueNode[] arguments, StampPair returnStamp, JavaType[] signature, ResolvedJavaMethod target, CallingConvention.Type callType, + InvokeKind invokeKind) { + super(TYPE, computedAddress, arguments, returnStamp, signature, target, callType, invokeKind, null); + } +} diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/pltgot/aarch64/PLTGOTAArch64Lowerings.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/pltgot/aarch64/PLTGOTAArch64Lowerings.java new file mode 100644 index 000000000000..41415f28353c --- /dev/null +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/pltgot/aarch64/PLTGOTAArch64Lowerings.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023, 2024, 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.graal.pltgot.aarch64; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.hosted.Feature; + +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.graal.meta.RuntimeConfiguration; +import com.oracle.svm.core.graal.snippets.NodeLoweringProvider; +import com.oracle.svm.core.graal.snippets.aarch64.AArch64SnippetsFeature; +import com.oracle.svm.graal.pltgot.PLTGOTNonSnippetLowerings; +import com.oracle.svm.hosted.pltgot.PLTGOTOptions; + +import jdk.graal.compiler.graph.Node; +import jdk.graal.compiler.options.OptionValues; +import jdk.graal.compiler.phases.util.Providers; + +@AutomaticallyRegisteredFeature +@Platforms(Platform.AARCH64.class) +public class PLTGOTAArch64Lowerings implements InternalFeature { + + @Override + public boolean isInConfiguration(IsInConfigurationAccess access) { + return PLTGOTOptions.EnablePLTGOT.getValue(); + } + + @Override + public List> getRequiredFeatures() { + return Collections.singletonList(AArch64SnippetsFeature.class); + } + + @Override + public void registerLowerings(RuntimeConfiguration runtimeConfig, OptionValues options, Providers providers, Map, NodeLoweringProvider> lowerings, boolean hosted) { + if (hosted) { + PLTGOTNonSnippetLowerings.registerLowerings(runtimeConfig, lowerings); + } + } +} diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/pltgot/amd64/PLTGOTAMD64Lowerings.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/pltgot/amd64/PLTGOTAMD64Lowerings.java new file mode 100644 index 000000000000..d6811cdba0c3 --- /dev/null +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/pltgot/amd64/PLTGOTAMD64Lowerings.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023, 2024, 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.graal.pltgot.amd64; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.hosted.Feature; + +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.graal.meta.RuntimeConfiguration; +import com.oracle.svm.core.graal.snippets.NodeLoweringProvider; +import com.oracle.svm.core.graal.snippets.amd64.AMD64SnippetsFeature; +import com.oracle.svm.graal.pltgot.PLTGOTNonSnippetLowerings; +import com.oracle.svm.hosted.pltgot.PLTGOTOptions; + +import jdk.graal.compiler.graph.Node; +import jdk.graal.compiler.options.OptionValues; +import jdk.graal.compiler.phases.util.Providers; + +@AutomaticallyRegisteredFeature +@Platforms(Platform.AMD64.class) +public class PLTGOTAMD64Lowerings implements InternalFeature { + + @Override + public boolean isInConfiguration(IsInConfigurationAccess access) { + return PLTGOTOptions.EnablePLTGOT.getValue(); + } + + @Override + public List> getRequiredFeatures() { + return Collections.singletonList(AMD64SnippetsFeature.class); + } + + @Override + public void registerLowerings(RuntimeConfiguration runtimeConfig, OptionValues options, Providers providers, Map, NodeLoweringProvider> lowerings, boolean hosted) { + if (hosted) { + PLTGOTNonSnippetLowerings.registerLowerings(runtimeConfig, lowerings); + } + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java index 76534d482e6c..e8331c298511 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java @@ -69,6 +69,7 @@ import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.annotate.Delete; import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.graal.code.SubstrateBackend; import com.oracle.svm.core.graal.meta.RuntimeConfiguration; import com.oracle.svm.core.hub.ClassForNameSupport; import com.oracle.svm.core.meta.SharedField; @@ -766,15 +767,22 @@ public void registerLinkerInvocationTransformer(Function entryPoints, JavaMainSupport j createAbstractImage(k, hostedEntryPoints, heap, hMetaAccess, codeCache); - FeatureImpl.AfterAbstractImageCreationAccessImpl access = new FeatureImpl.AfterAbstractImageCreationAccessImpl(featureHandler, loader, debug, image); + FeatureImpl.AfterAbstractImageCreationAccessImpl access = new FeatureImpl.AfterAbstractImageCreationAccessImpl(featureHandler, loader, debug, image, + runtimeConfiguration.getBackendForNormalMethod()); featureHandler.forEachGraalFeature(feature -> feature.afterAbstractImageCreation(access)); image.build(imageName, debug); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/CollectPLTGOTCallSitesResolutionSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/CollectPLTGOTCallSitesResolutionSupport.java new file mode 100644 index 000000000000..340c6140f2e0 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/CollectPLTGOTCallSitesResolutionSupport.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2023, 2024, 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.hosted.pltgot; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.ConcurrentSkipListSet; + +import com.oracle.objectfile.ObjectFile; +import com.oracle.svm.core.meta.SharedMethod; +import com.oracle.svm.core.pltgot.MethodAddressResolver; +import com.oracle.svm.hosted.meta.HostedMethod; +import com.oracle.svm.hosted.meta.HostedUniverse; + +class CollectPLTGOTCallSitesResolutionSupport implements MethodAddressResolutionSupport { + private final MethodAddressResolutionSupport resolver; + private final ConcurrentMap> callerCalleesMap; + private final Set calleesWithUnknownCaller; + + CollectPLTGOTCallSitesResolutionSupport(MethodAddressResolutionSupport resolver) { + this.resolver = resolver; + this.callerCalleesMap = new ConcurrentSkipListMap<>(HostedUniverse.METHOD_COMPARATOR); + this.calleesWithUnknownCaller = new ConcurrentSkipListSet<>(HostedUniverse.METHOD_COMPARATOR); + } + + @Override + public boolean shouldCallViaPLTGOT(SharedMethod caller, SharedMethod callee) { + boolean shouldCall = resolver.shouldCallViaPLTGOT(caller, callee); + if (shouldCall) { + var callees = callerCalleesMap.computeIfAbsent((HostedMethod) caller, k -> ConcurrentHashMap.newKeySet()); + callees.add((HostedMethod) callee); + } + return shouldCall; + } + + @Override + public boolean shouldCallViaPLTGOT(SharedMethod callee) { + boolean shouldCall = resolver.shouldCallViaPLTGOT(callee); + if (shouldCall) { + calleesWithUnknownCaller.add((HostedMethod) callee); + } + return shouldCall; + } + + @Override + public void augmentImageObjectFile(ObjectFile imageObjectFile) { + resolver.augmentImageObjectFile(imageObjectFile); + } + + @Override + public MethodAddressResolver createMethodAddressResolver() { + return resolver.createMethodAddressResolver(); + } + + public Map> getCallerCalleesMap() { + return Collections.unmodifiableMap(callerCalleesMap); + } + + public Set getCalleesWithUnknownCaller() { + return Collections.unmodifiableSet(calleesWithUnknownCaller); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/GOTEntryAllocator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/GOTEntryAllocator.java new file mode 100644 index 000000000000..7c70ca2d51b1 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/GOTEntryAllocator.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2023, 2024, 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.hosted.pltgot; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +import com.oracle.svm.core.meta.SharedMethod; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.meta.HostedMethod; + +public class GOTEntryAllocator { + public static final int GOT_NO_ENTRY = -2; + + private final ConcurrentHashMap gotMap = new ConcurrentHashMap<>(); + private SharedMethod[] got = null; + + private final AtomicInteger currentFreeEntry = new AtomicInteger(0); + + public int getMethodGotEntry(SharedMethod method) { + return gotMap.computeIfAbsent(method, m -> currentFreeEntry.getAndIncrement()); + } + + public void reserveMethodGotEntry(SharedMethod method) { + getMethodGotEntry(method); + } + + public int queryGotEntry(SharedMethod method) { + assert hasGOTLayout(); + return gotMap.getOrDefault(method, GOT_NO_ENTRY); + } + + public void reserveAndLayout(Set methods, MethodAddressResolutionSupport resolver) { + assert !hasGOTLayout(); + + methods.stream() + .filter(resolver::shouldCallViaPLTGOT) + .forEach(this::reserveMethodGotEntry); + + VMError.guarantee(got == null, "Can layout the GOT only once."); + got = new SharedMethod[gotMap.keySet().size()]; + for (Map.Entry entry : gotMap.entrySet()) { + got[entry.getValue()] = entry.getKey(); + } + } + + public boolean hasGOTLayout() { + return got != null; + } + + public SharedMethod[] getGOT() { + VMError.guarantee(got != null, "Must layout the GOT first before use."); + return got; + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/HostedPLTGOTConfiguration.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/HostedPLTGOTConfiguration.java new file mode 100644 index 000000000000..196c972a6b0e --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/HostedPLTGOTConfiguration.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2023, 2024, 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.hosted.pltgot; + +import java.lang.reflect.Method; + +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.objectfile.SectionName; +import com.oracle.svm.core.pltgot.PLTGOTConfiguration; +import com.oracle.svm.hosted.meta.HostedMetaAccess; +import com.oracle.svm.hosted.meta.HostedMethod; + +import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.RegisterConfig; + +public abstract class HostedPLTGOTConfiguration extends PLTGOTConfiguration { + public static final SectionName SVM_GOT_SECTION = new SectionName.ProgbitsSectionName("svm_got"); + + protected MethodAddressResolutionSupport methodAddressResolutionSupport; + private final GOTEntryAllocator gotEntryAllocator = new GOTEntryAllocator(); + + private PLTSectionSupport pltSectionSupport; + private HostedMetaAccess hostedMetaAccess; + + @SuppressWarnings("this-escape") + public HostedPLTGOTConfiguration() { + this.pltSectionSupport = new PLTSectionSupport(getArchSpecificPLTStubGenerator()); + } + + public static HostedPLTGOTConfiguration singleton() { + return (HostedPLTGOTConfiguration) ImageSingletons.lookup(PLTGOTConfiguration.class); + } + + public abstract Method getArchSpecificResolverAsMethod(); + + public abstract Register getGOTPassingRegister(RegisterConfig registerConfig); + + public abstract PLTStubGenerator getArchSpecificPLTStubGenerator(); + + public void setHostedMetaAccess(HostedMetaAccess metaAccess) { + assert hostedMetaAccess == null : "The field hostedMetaAccess can't be set twice."; + this.hostedMetaAccess = metaAccess; + } + + public void initializeMethodAddressResolutionSupport(MethodAddressResolutionSupportFactory methodAddressResolutionSupportFactory) { + assert methodAddressResolutionSupport == null : "The field methodAddressResolutionSupport can't be initialized twice."; + methodAddressResolutionSupport = methodAddressResolutionSupportFactory.create(); + if (PLTGOTOptions.PrintPLTGOTCallsInfo.getValue()) { + methodAddressResolutionSupport = new CollectPLTGOTCallSitesResolutionSupport(methodAddressResolutionSupport); + } + methodAddressResolver = getMethodAddressResolutionSupport().createMethodAddressResolver(); + } + + public MethodAddressResolutionSupport getMethodAddressResolutionSupport() { + assert methodAddressResolutionSupport != null : "Must call initializeMethodAddressResolutionSupport before calling getMethodAddressResolutionSupport"; + return methodAddressResolutionSupport; + } + + public PLTSectionSupport getPLTSectionSupport() { + return pltSectionSupport; + } + + public void markResolverMethodPatch() { + pltSectionSupport.markResolverMethodPatch(getArchSpecificResolverAsHostedMethod()); + } + + public HostedMethod getArchSpecificResolverAsHostedMethod() { + assert hostedMetaAccess != null : "Must set hostedMetaAccess before calling getArchSpecificResolverAsHostedMethod"; + return hostedMetaAccess.lookupJavaMethod(getArchSpecificResolverAsMethod()); + } + + public GOTEntryAllocator getGOTEntryAllocator() { + return gotEntryAllocator; + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/IdentityMethodAddressResolverFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/IdentityMethodAddressResolverFeature.java new file mode 100644 index 000000000000..8ccdc3c026d6 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/IdentityMethodAddressResolverFeature.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2023, 2024, 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.hosted.pltgot; + +import java.util.Collections; +import java.util.List; + +import org.graalvm.nativeimage.AnnotationAccess; +import org.graalvm.nativeimage.c.function.CEntryPoint; +import org.graalvm.nativeimage.c.function.CFunction; +import org.graalvm.nativeimage.hosted.Feature; + +import com.oracle.objectfile.BasicProgbitsSectionImpl; +import com.oracle.objectfile.ObjectFile; +import com.oracle.objectfile.SectionName; +import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.graal.code.ExplicitCallingConvention; +import com.oracle.svm.core.graal.code.StubCallingConvention; +import com.oracle.svm.core.graal.code.SubstrateCallingConventionKind; +import com.oracle.svm.core.jdk.InternalVMMethod; +import com.oracle.svm.core.meta.MethodPointer; +import com.oracle.svm.core.meta.SharedMethod; +import com.oracle.svm.core.pltgot.IdentityMethodAddressResolver; +import com.oracle.svm.core.pltgot.MethodAddressResolver; +import com.oracle.svm.core.snippets.SubstrateForeignCallTarget; +import com.oracle.svm.hosted.FeatureImpl; +import com.oracle.svm.hosted.image.NativeImage; +import com.oracle.svm.hosted.image.RelocatableBuffer; + +/** + * An example dynamic method address resolver implementation. + * + * The code of all reachable methods is located in the text section of the generated image. This + * resolver introduces another section ('.svm_methodtable') that contains absolute addresses of + * methods whose address is dynamically resolved. The methodtable section is the same size as the + * GOT, and the index of any method is the same in both. This feature registers the + * {@link IdentityMethodAddressResolver} resolver that, when a method is called for the first time, + * lookups the absolute method address in the above section. This address is then written in the + * appropriate GOT entry and is used for subsequent calls of the same method. + * + */ + +public class IdentityMethodAddressResolverFeature implements InternalFeature { + + // Restrict segment names to 16 chars on Mach-O. + public static final SectionName SVM_METHODTABLE = new SectionName.ProgbitsSectionName("svm_methodtbl"); + + private RelocatableBuffer offsetsSectionBuffer; + + private ObjectFile.ProgbitsSectionImpl offsetsSectionBufferImpl; + + protected class IdentityMethodAddressResolverSupport implements MethodAddressResolutionSupport { + private static boolean isAllowed(SharedMethod method) { + if (AnnotationAccess.isAnnotationPresent(method, CEntryPoint.class)) { + return false; + } + if (AnnotationAccess.isAnnotationPresent(method, CFunction.class)) { + return false; + } + if (AnnotationAccess.isAnnotationPresent(method, StubCallingConvention.class)) { + return false; + } + if (AnnotationAccess.isAnnotationPresent(method, Uninterruptible.class)) { + return false; + } + if (AnnotationAccess.isAnnotationPresent(method, SubstrateForeignCallTarget.class)) { + return false; + } + if (AnnotationAccess.isAnnotationPresent(method.getDeclaringClass(), InternalVMMethod.class)) { + return false; + } + if (AnnotationAccess.isAnnotationPresent(method, ExplicitCallingConvention.class) && + AnnotationAccess.getAnnotation(method, ExplicitCallingConvention.class).value().equals(SubstrateCallingConventionKind.ForwardReturnValue)) { + /* + * Methods that use ForwardReturnValue calling convention can't be resolved with + * PLT/GOT on AMD64 because + * AMD64MethodAddressResolutionDispatcher.resolveMethodAddress uses the same calling + * convention, and we can't save the callers value of the `rax` register on AMD64 + * without spilling it. + */ + return false; + } + return true; + } + + @Override + @SuppressWarnings("unused") + public boolean shouldCallViaPLTGOT(SharedMethod caller, SharedMethod callee) { + return isAllowed(callee); + } + + @Override + public boolean shouldCallViaPLTGOT(SharedMethod callee) { + return isAllowed(callee); + } + + @Override + public void augmentImageObjectFile(ObjectFile imageObjectFile) { + GOTEntryAllocator gotEntryAllocator = HostedPLTGOTConfiguration.singleton().getGOTEntryAllocator(); + SharedMethod[] got = gotEntryAllocator.getGOT(); + long methodCount = got.length; + int wordSize = ConfigurationValues.getTarget().wordSize; + long gotSectionSize = methodCount * wordSize; + offsetsSectionBuffer = new RelocatableBuffer(gotSectionSize, imageObjectFile.getByteOrder()); + offsetsSectionBufferImpl = new BasicProgbitsSectionImpl(offsetsSectionBuffer.getBackingArray()); + String name = SVM_METHODTABLE.getFormatDependentName(imageObjectFile.getFormat()); + ObjectFile.Section offsetsSection = imageObjectFile.newProgbitsSection(name, imageObjectFile.getPageSize(), true, false, offsetsSectionBufferImpl); + + ObjectFile.RelocationKind relocationKind = ObjectFile.RelocationKind.getDirect(wordSize); + for (int gotEntryNo = 0; gotEntryNo < got.length; ++gotEntryNo) { + offsetsSectionBuffer.addRelocationWithoutAddend(gotEntryNo * wordSize, relocationKind, new MethodPointer(got[gotEntryNo], true)); + } + + imageObjectFile.createDefinedSymbol(offsetsSection.getName(), offsetsSection, 0, 0, false, false); + imageObjectFile.createDefinedSymbol("__svm_methodtable_begin", offsetsSection, 0, wordSize, false, SubstrateOptions.InternalSymbolsAreGlobal.getValue()); + imageObjectFile.createDefinedSymbol("__svm_methodtable_end", offsetsSection, gotSectionSize, wordSize, false, SubstrateOptions.InternalSymbolsAreGlobal.getValue()); + } + + @Override + public MethodAddressResolver createMethodAddressResolver() { + return new IdentityMethodAddressResolver(); + } + } + + @Override + public List> getRequiredFeatures() { + return Collections.singletonList(PLTGOTFeature.class); + } + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + HostedPLTGOTConfiguration.singleton().initializeMethodAddressResolutionSupport(IdentityMethodAddressResolverSupport::new); + } + + @Override + public void beforeImageWrite(BeforeImageWriteAccess access) { + prepareOffsetsSection((NativeImage) ((FeatureImpl.BeforeImageWriteAccessImpl) access).getImage()); + } + + private void prepareOffsetsSection(NativeImage image) { + image.markRelocationSitesFromBuffer(offsetsSectionBuffer, offsetsSectionBufferImpl); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/MethodAddressResolutionSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/MethodAddressResolutionSupport.java new file mode 100644 index 000000000000..724206517249 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/MethodAddressResolutionSupport.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023, 2024, 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.hosted.pltgot; + +import com.oracle.objectfile.ObjectFile; +import com.oracle.svm.core.meta.SharedMethod; +import com.oracle.svm.core.pltgot.MethodAddressResolver; + +/** + * Provides necessary services for dynamic method address resolution through the PLT/GOT. + */ +public interface MethodAddressResolutionSupport { + + /** + * Predicate that determines if an indirect GOT call should be emitted when the callee is called + * from the caller. + * + */ + boolean shouldCallViaPLTGOT(SharedMethod caller, SharedMethod callee); + + /** + * Predicate that determines if an indirect GOT call should be used when the caller of the + * method is not known. + */ + boolean shouldCallViaPLTGOT(SharedMethod callee); + + /** + * Allows the resolver to augment the object file produced by the image builder. This can be + * used, for example, to create a custom section in the resulting object file. + */ + void augmentImageObjectFile(ObjectFile imageObjectFile); + + /** + * Creates a resolver that will be used to resolve the addresses of methods called through the + * PLT/GOT at runtime. + */ + MethodAddressResolver createMethodAddressResolver(); +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/MethodAddressResolutionSupportFactory.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/MethodAddressResolutionSupportFactory.java new file mode 100644 index 000000000000..af7289ca1bc3 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/MethodAddressResolutionSupportFactory.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023, 2024, 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.hosted.pltgot; + +public interface MethodAddressResolutionSupportFactory { + MethodAddressResolutionSupport create(); +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/PLTGOTFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/PLTGOTFeature.java new file mode 100644 index 000000000000..3dc472c1856c --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/PLTGOTFeature.java @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2023, 2024, 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.hosted.pltgot; + +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.reflect.Method; +import java.util.Iterator; +import java.util.Set; +import java.util.function.Consumer; + +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; + +import com.oracle.graal.pointsto.reports.ReportUtils; +import com.oracle.objectfile.BasicProgbitsSectionImpl; +import com.oracle.objectfile.ObjectFile; +import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.graal.RuntimeCompilation; +import com.oracle.svm.core.meta.SharedMethod; +import com.oracle.svm.core.pltgot.GOTAccess; +import com.oracle.svm.core.pltgot.GOTHeapSupport; +import com.oracle.svm.core.pltgot.PLTGOTConfiguration; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.FeatureImpl; +import com.oracle.svm.hosted.image.MethodPointerRelocationProvider; +import com.oracle.svm.hosted.image.RelocatableBuffer; +import com.oracle.svm.hosted.pltgot.aarch64.AArch64HostedPLTGOTConfiguration; +import com.oracle.svm.hosted.pltgot.amd64.AMD64HostedPLTGOTConfiguration; + +import jdk.graal.compiler.util.json.JsonWriter; + +/** + * Introduces the PLT and GOT mechanism to native calls that allows resolving the target method's + * address at runtime. + * + * To create a custom method address resolver, a user needs to: + *
    + *
  1. Implement the {@link MethodAddressResolutionSupport} resolver support class.
  2. + *
  3. Create a feature that enables the {@link PLTGOTOptions#EnablePLTGOT} option and registers + * their resolver in the {@link ImageSingletons}.
  4. + *
+ * For an example resolver, see {@link com.oracle.svm.core.pltgot.IdentityMethodAddressResolver}. + * + * From an implementation point of view, this feature and the supporting classes implement + * the PLT (Procedure + * Linkage Table) and GOT (Global Offset Table) mechanism used by the Linux dynamic linker to + * enable lazy binding. + * + * Native Image constructs the GOT during the image build by assigning each eligible method an entry + * into the GOT. At runtime, the GOT is represented as an array of pointers mapped right before the + * image heap. Multiple isolates all see the same GOT. Direct calls of eligible methods are replaced + * with indirect calls that load the method's address from the GOT. + * + * For each eligible method, Native Image constructs a PLT stub. Initially, each GOT entry points to + * the PLT stub of the method assigned to that GOT entry. Vtable entries of eligible methods also + * point to the PLT stub. + * + * There are two kinds of calls on the native level: + *
    + *
  1. Direct calls
  2. + *
  3. Indirect calls
  4. + *
+ * Native image emits direct calls as IP relative calls. If a method is directly called and requires + * the PLT/GOT, these calls are instead emitted as indirect calls where the address of the method to + * call is located at HEAP_BASE_REGISTER - (METHOD_GOT_ENTRY_NO * WORD_SIZE). Indirect calls, used + * for virtual calls, are unchanged. For virtual calls, each vtable entry is rewritten to point to + * the PLT stub associated with the method we are calling, and it will never change during program + * execution. As a consequence, virtual calls for methods that are resolved with the PLT/GOT + * mechanism are doubly indirected. Also, we only rewrite the vtable entries for the methods that + * are called through the PLT/GOT mechanism. + * + * A couple of implementation notes: + *
    + *
  • The GOT is always mapped relative to the image heap of an isolate and is backed by the same + * memory across isolates. This means that modifications made to the GOT by one isolate are visible + * to all other isolates. This mapping is necessary to avoid relocations in the code (we don't want + * to patch the code at runtime).
  • + *
  • Virtual calls now have two levels of indirection instead of one. The vtables contain + * addresses of PLT stubs corresponding to the virtual methods. A PLT stub either resolves the + * address of the actual method and writes it to the GOT, or it reads the previously resolved method + * address from the GOT, before finally jumping into the target method. The vtables themselves are + * never modified so that the relocatable section of the image heap can remain read-only.
  • + * + * If the PLT/GOT mechanism is used for all eligible methods, the additional indirections that are + * now present in calls can result in a slowdown that ranges from just a few percent to up to 20% + * depending on the workload for the default configuration. + *
+ */ +public class PLTGOTFeature implements InternalFeature { + + @Override + public boolean isInConfiguration(IsInConfigurationAccess access) { + return PLTGOTOptions.EnablePLTGOT.getValue(); + } + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + VMError.guarantee(Platform.includedIn(Platform.LINUX.class) || Platform.includedIn(Platform.DARWIN.class) || Platform.includedIn(Platform.WINDOWS.class), + "PLT and GOT is currently only supported on Linux, Darwin and Windows."); + VMError.guarantee(Platform.includedIn(Platform.AARCH64.class) || Platform.includedIn(Platform.AMD64.class), "PLT and GOT is currently only supported on AArch64 and AMD64."); + VMError.guarantee(!RuntimeCompilation.isEnabled(), "PLT and GOT is currently not supported with runtime compilation."); + VMError.guarantee(SubstrateOptions.SpawnIsolates.getValue(), "PLT and GOT cannot work without isolates."); + VMError.guarantee("lir".equals(SubstrateOptions.CompilerBackend.getValue()), "PLT and GOT cannot work with a custom compiler backend."); + + ImageSingletons.add(PLTGOTConfiguration.class, createConfiguration()); + } + + private static PLTGOTConfiguration createConfiguration() { + if (Platform.includedIn(Platform.AMD64.class)) { + return new AMD64HostedPLTGOTConfiguration(); + } else if (Platform.includedIn(Platform.AARCH64.class)) { + return new AArch64HostedPLTGOTConfiguration(); + } else { + throw VMError.shouldNotReachHere("PLT and GOT is currently only supported on AArch64 and AMD64."); + } + } + + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + Method resolver = HostedPLTGOTConfiguration.singleton().getArchSpecificResolverAsMethod(); + ((FeatureImpl.BeforeAnalysisAccessImpl) access).registerAsRoot(resolver, false, "PLT GOT support, registered in " + PLTGOTFeature.class); + } + + @Override + public void beforeCompilation(BeforeCompilationAccess access) { + HostedPLTGOTConfiguration.singleton().setHostedMetaAccess(((FeatureImpl.BeforeCompilationAccessImpl) access).getMetaAccess()); + } + + @Override + public void afterCompilation(AfterCompilationAccess access) { + MethodAddressResolutionSupport methodAddressResolutionSupport = HostedPLTGOTConfiguration.singleton().getMethodAddressResolutionSupport(); + GOTEntryAllocator gotEntryAllocator = HostedPLTGOTConfiguration.singleton().getGOTEntryAllocator(); + + gotEntryAllocator.reserveAndLayout(((FeatureImpl.AfterCompilationAccessImpl) access).getCompilations().keySet(), methodAddressResolutionSupport); + + Set gotTable = Set.of(gotEntryAllocator.getGOT()); + ImageSingletons.add(MethodPointerRelocationProvider.class, new PLTGOTPointerRelocationProvider(gotTable::contains)); + } + + @Override + public void beforeImageWrite(BeforeImageWriteAccess access) { + HostedPLTGOTConfiguration.singleton().markResolverMethodPatch(); + if (PLTGOTOptions.PrintPLTGOTCallsInfo.getValue()) { + reportPLTGOTCallSites(); + } + } + + @Override + public void afterAbstractImageCreation(AfterAbstractImageCreationAccess access) { + FeatureImpl.AfterAbstractImageCreationAccessImpl accessImpl = (FeatureImpl.AfterAbstractImageCreationAccessImpl) access; + ObjectFile imageObjectFile = accessImpl.getImage().getObjectFile(); + SharedMethod[] got = HostedPLTGOTConfiguration.singleton().getGOTEntryAllocator().getGOT(); + /* We must create the PLT and the GOT section before we mark any relocations. */ + PLTSectionSupport pltSectionSupport = HostedPLTGOTConfiguration.singleton().getPLTSectionSupport(); + pltSectionSupport.createPLTSection(got, imageObjectFile, accessImpl.getSubstrateBackend()); + createGOTSection(got, imageObjectFile, pltSectionSupport); + HostedPLTGOTConfiguration.singleton().getMethodAddressResolutionSupport().augmentImageObjectFile(imageObjectFile); + } + + public static void createGOTSection(SharedMethod[] got, ObjectFile objectFile, PLTSectionSupport pltSectionSupport) { + int wordSize = ConfigurationValues.getTarget().wordSize; + int gotSectionSize = got.length * wordSize; + RelocatableBuffer gotBuffer = new RelocatableBuffer(gotSectionSize, objectFile.getByteOrder()); + ObjectFile.ProgbitsSectionImpl gotBufferImpl = new BasicProgbitsSectionImpl(gotBuffer.getBackingArray()); + String name = HostedPLTGOTConfiguration.SVM_GOT_SECTION.getFormatDependentName(objectFile.getFormat()); + ObjectFile.Section gotSection = objectFile.newProgbitsSection(name, objectFile.getPageSize(), true, false, gotBufferImpl); + + ObjectFile.RelocationKind relocationKind = ObjectFile.RelocationKind.getDirect(wordSize); + for (int gotEntryNo = 0; gotEntryNo < got.length; ++gotEntryNo) { + int methodGOTEntryOffsetInSection = gotSectionSize + GOTAccess.getGotEntryOffsetFromHeapRegister(gotEntryNo); + pltSectionSupport.markRelocationToPLTResolverJump(gotBufferImpl, methodGOTEntryOffsetInSection, relocationKind, got[gotEntryNo]); + } + + objectFile.createDefinedSymbol(gotSection.getName(), gotSection, 0, 0, false, false); + objectFile.createDefinedSymbol(GOTHeapSupport.IMAGE_GOT_BEGIN_SYMBOL_NAME, gotSection, 0, wordSize, false, + SubstrateOptions.InternalSymbolsAreGlobal.getValue()); + objectFile.createDefinedSymbol(GOTHeapSupport.IMAGE_GOT_END_SYMBOL_NAME, gotSection, gotSectionSize, wordSize, false, + SubstrateOptions.InternalSymbolsAreGlobal.getValue()); + + if (PLTGOTOptions.PrintGOT.getValue()) { + ReportUtils.report("GOT Section contents", SubstrateOptions.reportsPath(), "got", "txt", writer -> { + writer.println("GOT Entry No | GOT Entry Offset From Image Heap Register | Method Name"); + for (int i = 0; i < got.length; ++i) { + writer.printf("%5X %5X %s%n", i, -GOTAccess.getGotEntryOffsetFromHeapRegister(i), got[i].toString()); + } + }); + } + } + + private static void reportPLTGOTCallSites() { + CollectPLTGOTCallSitesResolutionSupport resolver = (CollectPLTGOTCallSitesResolutionSupport) HostedPLTGOTConfiguration.singleton().getMethodAddressResolutionSupport(); + Consumer reportWriter = (pw) -> { + final String methodFormat = "%H.%n(%p)"; + try (JsonWriter writer = new JsonWriter(pw)) { + writer.append('{').newline(); + var calleesWithUnknownCaller = resolver.getCalleesWithUnknownCaller().stream().map(m -> m.format(methodFormat)).toList(); + if (!calleesWithUnknownCaller.isEmpty()) { + writer.quote("UNKNOWN_CALLER").append(":[").indent().newline(); + appendCallees(writer, calleesWithUnknownCaller.iterator()); + writer.newline().unindent().append("]"); + } + for (var entry : resolver.getCallerCalleesMap().entrySet()) { + writer.append(',').newline(); + var caller = entry.getKey(); + var calleesIter = entry.getValue().stream().map(m -> m.format(methodFormat)) + .sorted().iterator(); + writer.quote(caller.format(methodFormat)).append(":[").indent().newline(); + appendCallees(writer, calleesIter); + writer.unindent().newline().append(']'); + } + writer.newline().append('}').newline(); + } catch (IOException e) { + VMError.shouldNotReachHere(e); + } + }; + ReportUtils.report("PLT/GOT call-sites info", + SubstrateOptions.reportsPath(), "plt_got_call-sites_info", "json", reportWriter); + } + + private static void appendCallees(JsonWriter writer, Iterator callees) throws IOException { + while (callees.hasNext()) { + var callee = callees.next(); + writer.quote(callee); + if (callees.hasNext()) { + writer.append(','); + writer.newline(); + } + } + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/PLTGOTOptions.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/PLTGOTOptions.java new file mode 100644 index 000000000000..869874560091 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/PLTGOTOptions.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023, 2024, 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.hosted.pltgot; + +import com.oracle.svm.core.option.HostedOptionKey; + +import jdk.graal.compiler.options.Option; +import jdk.graal.compiler.options.OptionType; + +public class PLTGOTOptions { + @Option(help = "Enables support for dynamic method address resolution. Should never be enabled directly.", type = OptionType.Expert)// + public static final HostedOptionKey EnablePLTGOT = new HostedOptionKey<>(false); + + @Option(help = "Prints the contents of the GOT.", type = OptionType.Debug)// + public static final HostedOptionKey PrintGOT = new HostedOptionKey<>(false); + + @Option(help = "Prints Infopoint call sites inside methods called through PLT/GOT.", type = OptionType.Debug)// + public static final HostedOptionKey PrintPLTGOTCallsInfo = new HostedOptionKey<>(false); +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/PLTGOTPointerRelocationProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/PLTGOTPointerRelocationProvider.java new file mode 100644 index 000000000000..df3eb86848f5 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/PLTGOTPointerRelocationProvider.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2023, 2024, 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.hosted.pltgot; + +import java.util.function.Predicate; + +import com.oracle.objectfile.ObjectFile; +import com.oracle.svm.core.meta.MethodPointer; +import com.oracle.svm.core.meta.SharedMethod; +import com.oracle.svm.hosted.image.MethodPointerRelocationProvider; +import com.oracle.svm.hosted.meta.HostedMethod; + +/** + * Emits method pointer relocations in the image object file that take PLT/GOT into account. + * + * For methods invoked through PLT/GOT, unless overridden explicitly, emits relocations that point + * to the generated PLT stub. + */ +public class PLTGOTPointerRelocationProvider extends MethodPointerRelocationProvider { + + private final Predicate shouldMarkRelocationToPLTStub; + private final PLTSectionSupport pltSectionSupport; + + public PLTGOTPointerRelocationProvider(Predicate shouldMarkRelocationToPLTStub) { + this.shouldMarkRelocationToPLTStub = shouldMarkRelocationToPLTStub; + this.pltSectionSupport = HostedPLTGOTConfiguration.singleton().getPLTSectionSupport(); + } + + private boolean hasPLTStub(HostedMethod target, boolean isStaticallyResolved) { + return !isStaticallyResolved && shouldMarkRelocationToPLTStub.test(target); + } + + @Override + public void markMethodPointerRelocation(ObjectFile.ProgbitsSectionImpl section, int offset, ObjectFile.RelocationKind relocationKind, HostedMethod target, long addend, + MethodPointer methodPointer, boolean isInjectedNotCompiled) { + boolean isStaticallyResolved = methodPointer.isAbsolute(); + if (hasPLTStub(target, isStaticallyResolved)) { + pltSectionSupport.markRelocationToPLTStub(section, offset, relocationKind, target, addend); + } else { + super.markMethodPointerRelocation(section, offset, relocationKind, target, addend, methodPointer, isInjectedNotCompiled); + } + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/PLTSectionSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/PLTSectionSupport.java new file mode 100644 index 000000000000..1f61742d6e83 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/PLTSectionSupport.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2023, 2024, 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.hosted.pltgot; + +import java.util.HashMap; +import java.util.Map; + +import com.oracle.objectfile.BasicProgbitsSectionImpl; +import com.oracle.objectfile.ObjectFile; +import com.oracle.objectfile.ObjectFile.ProgbitsSectionImpl; +import com.oracle.objectfile.ObjectFile.RelocationKind; +import com.oracle.objectfile.SectionName; +import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.graal.code.SubstrateBackend; +import com.oracle.svm.core.meta.SharedMethod; +import com.oracle.svm.hosted.image.NativeImage; +import com.oracle.svm.hosted.image.RelocatableBuffer; +import com.oracle.svm.hosted.meta.HostedMethod; + +import jdk.vm.ci.meta.ResolvedJavaMethod; + +public class PLTSectionSupport { + + public static final SectionName SVM_PLT_SECTION_NAME = new SectionName.ProgbitsSectionName("svm_plt"); + + public static String pltSymbolNameForMethod(ResolvedJavaMethod method) { + return "svm_plt_" + NativeImage.localSymbolNameForMethod(method); + } + + private final Map methodPLTStubStart = new HashMap<>(); + private final Map methodPLTStubResolverOffset = new HashMap<>(); + + private ProgbitsSectionImpl pltBufferImpl; + private PLTStubGenerator stubGenerator; + + public PLTSectionSupport(PLTStubGenerator stubGenerator) { + this.stubGenerator = stubGenerator; + } + + void createPLTSection(SharedMethod[] got, ObjectFile objectFile, SubstrateBackend substrateBackend) { + byte[] pltCode = stubGenerator.generatePLT(got, substrateBackend); + int pltSectionSize = pltCode.length; + + RelocatableBuffer pltBuffer = new RelocatableBuffer(pltSectionSize, objectFile.getByteOrder()); + pltBufferImpl = new BasicProgbitsSectionImpl(pltBuffer.getBackingArray()); + String name = SVM_PLT_SECTION_NAME.getFormatDependentName(objectFile.getFormat()); + ObjectFile.Section pltSection = objectFile.newProgbitsSection(name, objectFile.getPageSize(), false, true, pltBufferImpl); + + pltBuffer.getByteBuffer().put(pltCode, 0, pltSectionSize); + + objectFile.createDefinedSymbol(pltSection.getName(), pltSection, 0, 0, true, false); + + for (SharedMethod method : got) { + HostedMethod m = (HostedMethod) method; + int offset = getMethodPLTStubStart(m); + objectFile.createDefinedSymbol(pltSymbolNameForMethod(m), pltSection, offset, ConfigurationValues.getTarget().wordSize, true, + SubstrateOptions.InternalSymbolsAreGlobal.getValue()); + } + + } + + void markRelocationToPLTStub(ProgbitsSectionImpl section, int offset, RelocationKind relocationKind, SharedMethod target, long addend) { + section.markRelocationSite(offset, relocationKind, pltSymbolNameForMethod(target), addend); + } + + void markRelocationToPLTResolverJump(ProgbitsSectionImpl section, int offset, RelocationKind relocationKind, SharedMethod target) { + assert methodPLTStubResolverOffset.get(target) != null : "Trying to mark a relocation to the `resolver-jump` part of the plt stub for a target that doesn't have a plt stub: " + target; + section.markRelocationSite(offset, relocationKind, pltSymbolNameForMethod(target), methodPLTStubResolverOffset.get(target)); + } + + void markResolverMethodPatch(HostedMethod resolverMethod) { + stubGenerator.markResolverMethodPatch(pltBufferImpl, resolverMethod); + } + + public void recordMethodPLTStubStart(SharedMethod method, int offset) { + methodPLTStubStart.put(method, offset); + } + + public void recordMethodPLTStubResolverOffset(SharedMethod method, int resolverOffset) { + methodPLTStubResolverOffset.put(method, resolverOffset); + } + + private int getMethodPLTStubStart(SharedMethod method) { + return methodPLTStubStart.get(method); + } + +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/PLTStubGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/PLTStubGenerator.java new file mode 100644 index 000000000000..694872d0ecce --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/PLTStubGenerator.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023, 2024, 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.hosted.pltgot; + +import com.oracle.objectfile.ObjectFile; +import com.oracle.svm.core.graal.code.SubstrateBackend; +import com.oracle.svm.core.meta.SharedMethod; + +import jdk.vm.ci.meta.ResolvedJavaMethod; + +public interface PLTStubGenerator { + + byte[] generatePLT(SharedMethod[] got, SubstrateBackend substrateBackend); + + void markResolverMethodPatch(ObjectFile.ProgbitsSectionImpl pltBuffer, ResolvedJavaMethod resolverMethod); + +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/aarch64/AArch64HostedPLTGOTConfiguration.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/aarch64/AArch64HostedPLTGOTConfiguration.java new file mode 100644 index 000000000000..c8765d716650 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/aarch64/AArch64HostedPLTGOTConfiguration.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023, 2024, 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.hosted.pltgot.aarch64; + +import java.lang.reflect.Method; + +import com.oracle.svm.core.pltgot.aarch64.AArch64ExitMethodAddressResolutionOp; +import com.oracle.svm.core.pltgot.aarch64.AArch64MethodAddressResolutionDispatcher; +import com.oracle.svm.hosted.pltgot.HostedPLTGOTConfiguration; +import com.oracle.svm.hosted.pltgot.PLTStubGenerator; +import com.oracle.svm.util.ReflectionUtil; + +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.vm.ci.aarch64.AArch64; +import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.RegisterConfig; +import jdk.vm.ci.code.RegisterValue; + +public final class AArch64HostedPLTGOTConfiguration extends HostedPLTGOTConfiguration { + + @Override + public Method getArchSpecificResolverAsMethod() { + return ReflectionUtil.lookupMethod(AArch64MethodAddressResolutionDispatcher.class, "resolveMethodAddress"); + } + + @Override + public PLTStubGenerator getArchSpecificPLTStubGenerator() { + return new AArch64PLTStubGenerator(); + } + + @Override + public Register getExitMethodAddressResolutionRegister(RegisterConfig registerConfig) { + return AArch64.rscratch2; + } + + @Override + public LIRInstruction createExitMethodAddressResolutionOp(RegisterValue exitThroughRegisterValue) { + return new AArch64ExitMethodAddressResolutionOp(exitThroughRegisterValue); + } + + @Override + public Register getGOTPassingRegister(RegisterConfig registerConfig) { + throw new UnsupportedOperationException("AArch64 passes got entries via (unused) deopt frame handle slot."); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/aarch64/AArch64PLTStubGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/aarch64/AArch64PLTStubGenerator.java new file mode 100644 index 000000000000..fff68c2ea1f4 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/aarch64/AArch64PLTStubGenerator.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2023, 2024, 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.hosted.pltgot.aarch64; + +import static com.oracle.objectfile.ObjectFile.RelocationKind.AARCH64_R_AARCH64_ADD_ABS_LO12_NC; +import static com.oracle.objectfile.ObjectFile.RelocationKind.AARCH64_R_AARCH64_ADR_PREL_PG_HI21; +import static jdk.graal.compiler.asm.aarch64.AArch64Address.createImmediateAddress; +import static jdk.graal.compiler.asm.aarch64.AArch64Address.AddressingMode.IMMEDIATE_SIGNED_UNSCALED; +import static jdk.vm.ci.aarch64.AArch64.sp; + +import com.oracle.objectfile.ObjectFile; +import com.oracle.svm.core.ReservedRegisters; +import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.deopt.DeoptimizationSlotPacking; +import com.oracle.svm.core.graal.code.SubstrateBackend; +import com.oracle.svm.core.meta.SharedMethod; +import com.oracle.svm.core.pltgot.GOTAccess; +import com.oracle.svm.core.pltgot.aarch64.AArch64MethodAddressResolutionDispatcher; +import com.oracle.svm.hosted.image.NativeImage; +import com.oracle.svm.hosted.meta.HostedMethod; +import com.oracle.svm.hosted.pltgot.HostedPLTGOTConfiguration; +import com.oracle.svm.hosted.pltgot.PLTSectionSupport; +import com.oracle.svm.hosted.pltgot.PLTStubGenerator; +import com.oracle.svm.hosted.pltgot.amd64.AMD64PLTStubGenerator; + +import jdk.graal.compiler.asm.Assembler; +import jdk.graal.compiler.asm.Label; +import jdk.graal.compiler.asm.aarch64.AArch64Address; +import jdk.graal.compiler.asm.aarch64.AArch64MacroAssembler; +import jdk.vm.ci.code.Register; +import jdk.vm.ci.meta.ResolvedJavaMethod; + +/** + * Generates the contents of the PLT section for the PLT/GOT mechanism. + * + * Please first see {@link AMD64PLTStubGenerator} for more details on how PLT/GOT mechanism works in + * general. + * + * AArch64 and AMD64 .svm_plt sections are different because on AArch64 we can't use the + * {@code ForwardReturnValue} calling convention for the + * {@link AArch64MethodAddressResolutionDispatcher#resolveMethodAddress()} without spilling the + * register x0, because x0 is both the first parameter register and the return value register. + * That's why we pass the method's GOT entry index via the (unused) deopt frame handle slot on the + * stack. The .svm_plt stub at the beginning of the section stores the got entry just after the + * return address, which is the deopt frame handle slot, and then we jump to the + * {@link AArch64MethodAddressResolutionDispatcher#resolveMethodAddress()}. As there are other users + * of the deopt slot, an encoding is used (see{@link DeoptimizationSlotPacking}). + * + * The AArch64 variant has a common svm_plt stub to avoid repeating the instruction that stores the + * got entry at the deopt frame handle slot and the instructions that load the address of the + * resolver method and jump to it. + * + * An example of an `.svm_plt` section that contains 2 methods that are called via PLT/GOT on + * darwin-aarch64: + * + *
+ * 0x7f0000 <.svm_plt>:
+ * 0x7f0000:  ldur x8, [sp]
+ * 0x7f0004:  and x8, x8, #0xff00000000000000
+ * 0x7f0008:  orr x9, x9, x8
+ * 0x7f000c:  adrp x8, 0x5df000
+ * 0x7f0010:  add x8, x8, #0xdd0
+ * 0x7f0014:  stur x9, [sp]        @ store gotEntry at the deopt frame handle slot
+ * 0x7f0018:  br x8                @ Jumps to 
+ * 0x7f001c :
+ * 0x7f001c:  ldr x9, [x27,#-8]    @ <--- We jump here from the virtual method call site
+ * 0x7f0020:  br x9                @ Jumps to the resolved method, or to the line below if the method wasn't resolved.
+ * 0x7f0024:  mov w9, wzr          @ <---- We jump here from the direct method call site. This line loads the gotEntry id
+ * 0x7f0028:  b 0x7f0000 <.svm_plt>
+ * 0x7f002c :
+ * 0x7f002c:  ldr x9, [x27,#-16]
+ * 0x7f0030:  br x9
+ * 0x7f0034:  orr w9, wzr, #0x1
+ * 0x7f0038:  b 0x7f0000 <.svm_plt>
+ * 0x7f003c :
+ * 0x7f003c:  ldr x9, [x27,#-24]
+ * 0x7f0040:  br x9
+ * 0x7f0044:  orr w9, wzr, #0x2
+ * 0x7f0048:  b 0x7f0000 <.svm_plt>
+ * 
+ */ +public class AArch64PLTStubGenerator implements PLTStubGenerator { + private int resolverAddressLoadOffset = -1; + + @Override + public byte[] generatePLT(SharedMethod[] got, SubstrateBackend substrateBackend) { + AArch64MacroAssembler masm = new AArch64MacroAssembler(ConfigurationValues.getTarget()); + Label pltStart = new Label(); + masm.bind(pltStart); + try (AArch64MacroAssembler.ScratchRegister scratchRegister1 = masm.getScratchRegister(); AArch64MacroAssembler.ScratchRegister scratchRegister2 = masm.getScratchRegister()) { + Register resolverJmpRegister = scratchRegister1.getRegister(); + Register gotEntryPassingRegister = scratchRegister2.getRegister(); + + generateResolverCallStub(masm, gotEntryPassingRegister, resolverJmpRegister); + + PLTSectionSupport support = HostedPLTGOTConfiguration.singleton().getPLTSectionSupport(); + for (int gotEntryNo = 0; gotEntryNo < got.length; ++gotEntryNo) { + HostedMethod method = (HostedMethod) got[gotEntryNo]; + int pltStubStart = masm.position(); + + /* Start of PLT stub for this GOT entry. */ + support.recordMethodPLTStubStart(method, pltStubStart); + + int gotEntryOffset = GOTAccess.getGotEntryOffsetFromHeapRegister(gotEntryNo); + Register heapReg = ReservedRegisters.singleton().getHeapBaseRegister(); + AArch64Address addr = masm.makeAddress(64, heapReg, gotEntryOffset, gotEntryPassingRegister); + + masm.maybeEmitIndirectTargetMarker(); + masm.ldr(64, gotEntryPassingRegister, addr); + masm.jmp(gotEntryPassingRegister); + + /* + * This is used as initial entry in the GOT, so that on first access this entry is + * going to be resolved. + */ + support.recordMethodPLTStubResolverOffset(method, masm.position() - pltStubStart); + masm.maybeEmitIndirectTargetMarker(); + masm.mov(gotEntryPassingRegister, gotEntryNo); + masm.jmp(pltStart); + } + } + return masm.close(true); + } + + @Override + public void markResolverMethodPatch(ObjectFile.ProgbitsSectionImpl pltBuffer, ResolvedJavaMethod resolverMethod) { + pltBuffer.markRelocationSite(resolverAddressLoadOffset, AARCH64_R_AARCH64_ADR_PREL_PG_HI21, NativeImage.localSymbolNameForMethod(resolverMethod), 0); + pltBuffer.markRelocationSite(resolverAddressLoadOffset + 4, AARCH64_R_AARCH64_ADD_ABS_LO12_NC, NativeImage.localSymbolNameForMethod(resolverMethod), 0); + } + + public void generateResolverCallStub(AArch64MacroAssembler masm, Register gotEntryPassingRegister, Register jmpTarget) { + masm.setCodePatchingAnnotationConsumer(this::recordResolverCallForPatching); + + /* + * GR-54839: Upper byte of deoptSlot may be used by leaveInterpreterStub, therefore an + * encoding is used for this word. + */ + Register scratch = jmpTarget; + masm.ldr(64, scratch, createImmediateAddress(64, IMMEDIATE_SIGNED_UNSCALED, sp, 0)); + masm.and(64, scratch, scratch, DeoptimizationSlotPacking.MASK_VARIABLE_FRAMESIZE); + masm.orr(64, gotEntryPassingRegister, gotEntryPassingRegister, scratch); + + /* + * use indirect jump to avoid problems around branch islands (displacement larger than + * +/-128MB) + */ + masm.adrpAdd(jmpTarget); + masm.str(64, gotEntryPassingRegister, createImmediateAddress(64, IMMEDIATE_SIGNED_UNSCALED, sp, 0)); + masm.jmp(jmpTarget); + } + + private void recordResolverCallForPatching(Assembler.CodeAnnotation a) { + if (resolverAddressLoadOffset != -1) { + return; + } + assert a instanceof AArch64MacroAssembler.AdrpAddMacroInstruction; + AArch64MacroAssembler.AdrpAddMacroInstruction annotation = (AArch64MacroAssembler.AdrpAddMacroInstruction) a; + resolverAddressLoadOffset = annotation.instructionPosition; + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/amd64/AMD64HostedPLTGOTConfiguration.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/amd64/AMD64HostedPLTGOTConfiguration.java new file mode 100644 index 000000000000..766195155610 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/amd64/AMD64HostedPLTGOTConfiguration.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023, 2024, 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.hosted.pltgot.amd64; + +import java.lang.reflect.Method; + +import com.oracle.svm.core.pltgot.amd64.AMD64ExitMethodAddressResolutionOp; +import com.oracle.svm.core.pltgot.amd64.AMD64MethodAddressResolutionDispatcher; +import com.oracle.svm.hosted.pltgot.HostedPLTGOTConfiguration; +import com.oracle.svm.hosted.pltgot.PLTStubGenerator; +import com.oracle.svm.util.ReflectionUtil; + +import jdk.graal.compiler.lir.LIRInstruction; +import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.RegisterConfig; +import jdk.vm.ci.code.RegisterValue; + +public final class AMD64HostedPLTGOTConfiguration extends HostedPLTGOTConfiguration { + + @Override + public Method getArchSpecificResolverAsMethod() { + return ReflectionUtil.lookupMethod(AMD64MethodAddressResolutionDispatcher.class, "resolveMethodAddress", long.class); + } + + @Override + public PLTStubGenerator getArchSpecificPLTStubGenerator() { + return new AMD64PLTStubGenerator(); + } + + @Override + public Register getExitMethodAddressResolutionRegister(RegisterConfig registerConfig) { + return getGOTPassingRegister(registerConfig); + } + + @Override + public LIRInstruction createExitMethodAddressResolutionOp(RegisterValue exitThroughRegisterValue) { + return new AMD64ExitMethodAddressResolutionOp(exitThroughRegisterValue); + } + + @Override + public Register getGOTPassingRegister(RegisterConfig registerConfig) { + return registerConfig.getReturnRegister(getArchSpecificResolverAsHostedMethod().getSignature().getReturnKind()); + } +} 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 new file mode 100644 index 000000000000..355f145d631b --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/pltgot/amd64/AMD64PLTStubGenerator.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2023, 2024, 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.hosted.pltgot.amd64; + +import java.util.ArrayList; +import java.util.List; + +import com.oracle.objectfile.ObjectFile; +import com.oracle.svm.core.ReservedRegisters; +import com.oracle.svm.core.graal.amd64.SubstrateAMD64Backend; +import com.oracle.svm.core.graal.code.SubstrateBackend; +import com.oracle.svm.core.graal.code.SubstrateCallingConventionKind; +import com.oracle.svm.core.meta.SharedMethod; +import com.oracle.svm.core.pltgot.GOTAccess; +import com.oracle.svm.core.pltgot.amd64.AMD64MethodAddressResolutionDispatcher; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.image.NativeImage; +import com.oracle.svm.hosted.meta.HostedMethod; +import com.oracle.svm.hosted.pltgot.HostedPLTGOTConfiguration; +import com.oracle.svm.hosted.pltgot.PLTSectionSupport; +import com.oracle.svm.hosted.pltgot.PLTStubGenerator; + +import jdk.graal.compiler.asm.Assembler; +import jdk.graal.compiler.asm.amd64.AMD64Address; +import jdk.graal.compiler.asm.amd64.AMD64BaseAssembler; +import jdk.graal.compiler.asm.amd64.AMD64MacroAssembler; +import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.RegisterConfig; +import jdk.vm.ci.meta.ResolvedJavaMethod; + +/** + * Generates the contents of the PLT section for the PLT/GOT mechanism. + * + * Every method that can be dynamically resolved through the PLT/GOT mechanism has its unique PLT + * stub. We handle method resolution for virtual calls and direct calls differently. + * + * + * + * When resolving a virtual method the corresponding PLT stub loads the GOT entry associated with + * the called method via r14 - (GOT_ENTRY + 1) * 8 into the return value register (ie. rax). If the + * method is already resolved then the second instruction in the PLT stub will jump directly to the + * resolved method. Otherwise, because the GOT entries are initialized to point to the third + * instruction in the corresponding PLT stub, the second instruction will jump to the instruction + * below that loads the gotEntry id into the return value register and then the fourth instruction + * jumps to the {@link AMD64MethodAddressResolutionDispatcher#resolveMethodAddress(long)} that + * resolves the method with a given got entry. + * + * When resolving direct calls we jump directly to the second part of the PLT stub (third + * instruction) from the callsite because we rewrite the direct calls to the first two instructions + * of the corresponding PLT stub. That way, for direct calls, we only jump to the PLT stub if the + * method wasn't already resolved. + * + * The {@link AMD64MethodAddressResolutionDispatcher#resolveMethodAddress(long)} uses the + * {@link SubstrateCallingConventionKind#ForwardReturnValue} which expects its one and only argument + * to be in the return value register. This calling convention for the method resolver enables us to + * avoid having to spill the callers first argument to the stack. Also, we can reuse the same + * register as scratch for the second instruction in the PLT stub because the return value register + * is caller saved. + * + * An example of the `.svm_plt` section that contains 2 methods that are called via PLT/GOT (on + * Linux AMD64): + * + *
+ * {@code
+ *0000000000355000 :
+ *   355000:  mov      rax,QWORD PTR [r14-0x8] # <- virtual call jumps here.
+ *   355004:  jmp      rax                     # <- if the method is resolved jump to it, else jump to the instruction below.
+ *   355006:  mov      rax,0x0                 # <- direct call jumps here only once if the method hasn't already been resolved
+ *   35500b:  jmp      19e700 
+ *
+ * 0000000000355010 :
+ *   355010:  mov      rax,QWORD PTR [r14-0x10] # load the address from the GOT table. (`35501b` if the method wasn't resolved already, else method's address)
+ *   355014:  jmp      rax
+ *   355016:  mov      rax,0x1
+ *   35501b:  jmp      19e700 
+ * }
+ * 
+ */ +public class AMD64PLTStubGenerator implements PLTStubGenerator { + private List resolverPatchOffsets = new ArrayList<>(); + private int resolverKindAddend; + private ObjectFile.RelocationKind resolverPatchRelocationKind = null; + + @Override + public byte[] generatePLT(SharedMethod[] got, SubstrateBackend substrateBackend) { + HostedPLTGOTConfiguration configuration = HostedPLTGOTConfiguration.singleton(); + VMError.guarantee(configuration.getArchSpecificResolverAsHostedMethod().getCallingConventionKind().equals(SubstrateCallingConventionKind.ForwardReturnValue), + "AMD64PLTStubGenerator assumes that %s is using %s ", + configuration.getArchSpecificResolverAsHostedMethod().format("%H.%n(%p)"), SubstrateCallingConventionKind.ForwardReturnValue.name()); + + SubstrateAMD64Backend amd64Backend = (SubstrateAMD64Backend) substrateBackend; + RegisterConfig registerConfig = amd64Backend.getCodeCache().getRegisterConfig(); + Register register = configuration.getGOTPassingRegister(registerConfig); + + AMD64MacroAssembler asm = (AMD64MacroAssembler) amd64Backend.createAssemblerNoOptions(); + PLTSectionSupport support = HostedPLTGOTConfiguration.singleton().getPLTSectionSupport(); + + asm.setCodePatchingAnnotationConsumer(this::recordResolverCallForPatching); + for (int gotEntryNo = 0; gotEntryNo < got.length; ++gotEntryNo) { + HostedMethod method = (HostedMethod) got[gotEntryNo]; + + /* + * This is the method's call target. The GOT will be read to determine the method's + * actual address. + */ + int pltStubStart = asm.position(); + support.recordMethodPLTStubStart(method, pltStubStart); + + int gotEntryOffset = GOTAccess.getGotEntryOffsetFromHeapRegister(gotEntryNo); + asm.maybeEmitIndirectTargetMarker(); + asm.movq(register, new AMD64Address(ReservedRegisters.singleton().getHeapBaseRegister(), gotEntryOffset)); + asm.jmp(register); + + /* + * This is the initial target of the jmp directly above. Calls the resolver stub with + * the key for this method. + */ + support.recordMethodPLTStubResolverOffset(method, asm.position() - pltStubStart); + asm.maybeEmitIndirectTargetMarker(); + asm.movl(register, gotEntryNo); + /* + * We patch this later to the + * AMD64MethodAddressResolutionDispatcher#resolveMethodAddress(long). + */ + asm.jmp(); + } + + return asm.close(true); + } + + @Override + public void markResolverMethodPatch(ObjectFile.ProgbitsSectionImpl pltBuffer, ResolvedJavaMethod resolverMethod) { + for (int resolverPatchOffset : resolverPatchOffsets) { + pltBuffer.markRelocationSite(resolverPatchOffset, resolverPatchRelocationKind, NativeImage.localSymbolNameForMethod(resolverMethod), resolverKindAddend); + } + } + + private void recordResolverCallForPatching(Assembler.CodeAnnotation a) { + assert a instanceof AMD64BaseAssembler.OperandDataAnnotation; + AMD64BaseAssembler.OperandDataAnnotation annotation = (AMD64BaseAssembler.OperandDataAnnotation) a; + resolverPatchOffsets.add(annotation.operandPosition); + resolverKindAddend = -annotation.operandSize; + resolverPatchRelocationKind = ObjectFile.RelocationKind.getPCRelative(annotation.operandSize); + } +}