From 997ccacd59e5f492bccf57a36cb6b99f99a5083d Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Thu, 20 Feb 2025 15:26:08 +0100 Subject: [PATCH] FFM API: fixes for windows-amd64 --- substratevm/mx.substratevm/suite.py | 3 +- .../svm/core/foreign/RuntimeSystemLookup.java | 63 ++++++++++- ...get_jdk_internal_foreign_SystemLookup.java | 7 +- .../svm/hosted/foreign/ForeignGraphKit.java | 8 +- .../oracle/svm/hosted/foreign/UpcallStub.java | 14 ++- .../src/syslookup.c | 100 ++++++++++++++++++ 6 files changed, 184 insertions(+), 11 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.native.libchelper/src/syslookup.c diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index a9ce2286a9c9..b035719dcd8b 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -803,7 +803,8 @@ ], "jdk.internal.vm.ci" : [ "jdk.vm.ci.code", - "jdk.vm.ci.meta" + "jdk.vm.ci.meta", + "jdk.vm.ci.amd64", ], }, "javaCompliance" : "22+", diff --git a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/RuntimeSystemLookup.java b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/RuntimeSystemLookup.java index f10e6144253a..93a9c401a44e 100644 --- a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/RuntimeSystemLookup.java +++ b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/RuntimeSystemLookup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,14 +24,29 @@ */ package com.oracle.svm.core.foreign; +import java.lang.foreign.MemorySegment; import java.lang.foreign.SymbolLookup; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; + +import org.graalvm.nativeimage.Platform.WINDOWS; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.c.function.CFunction; +import org.graalvm.nativeimage.c.function.CFunction.Transition; +import org.graalvm.nativeimage.c.function.CLibrary; +import org.graalvm.word.Pointer; import com.oracle.svm.core.OS; +import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.util.BasedOnJDKClass; +import jdk.graal.compiler.word.Word; +import jdk.internal.foreign.Utils; + /** * Separated from {@link Target_jdk_internal_foreign_SystemLookup} to allow (forced) runtime * initialization. @@ -56,7 +71,28 @@ public static SymbolLookup makeSystemLookup() { Path msvcrt = system32.resolve("msvcrt.dll"); boolean useUCRT = Files.exists(ucrtbase); Path stdLib = useUCRT ? ucrtbase : msvcrt; - return Util_java_lang_foreign_SymbolLookup.libraryLookup(LookupNativeLibraries::loadLibraryPlatformSpecific, List.of(stdLib)); + + SymbolLookup lookup = Util_java_lang_foreign_SymbolLookup.libraryLookup(LookupNativeLibraries::loadLibraryPlatformSpecific, List.of(stdLib)); + if (useUCRT) { + // use a fallback lookup to look up inline functions + Function> fallbackLookup = name -> { + Pointer ptr = getWindowsFallbackSymbol(name); + if (ptr.isNonNull()) { + return Optional.of(MemorySegment.ofAddress(ptr.rawValue())); + } + return Optional.empty(); + }; + final SymbolLookup finalLookup = lookup; + lookup = name -> { + Objects.requireNonNull(name); + if (Utils.containsNullChars(name)) { + return Optional.empty(); + } + return finalLookup.find(name).or(() -> fallbackLookup.apply(name)); + }; + } + + return lookup; } else { /* * This list of libraries was obtained by examining the dependencies of libsystemlookup, @@ -65,4 +101,27 @@ public static SymbolLookup makeSystemLookup() { return Util_java_lang_foreign_SymbolLookup.libraryLookup(LookupNativeLibraries::loadLibraryPlatformSpecific, List.of("libc.so.6", "libm.so.6", "libdl.so.2")); } } + + /** + * Returns the function pointer of the requested symbol specified with + * {@link Target_jdk_internal_foreign_SystemLookup_WindowsFallbackSymbols}. See also file + * 'com.oracle.svm.native.libchelper/src/syslookup.c'. + */ + @Platforms(WINDOWS.class) + @CLibrary(value = "libchelper", requireStatic = true) + @CFunction(value = "__svm_get_syslookup_func", transition = Transition.NO_TRANSITION) + public static native Pointer getSyslookupFunc(int i, int nExpected); + + private static Pointer getWindowsFallbackSymbol(String name) { + try { + assert Target_jdk_internal_foreign_SystemLookup_WindowsFallbackSymbols.class.isEnum(); + @SuppressWarnings({"unchecked", "rawtypes"}) + Enum value = Enum.valueOf(SubstrateUtil.cast(Target_jdk_internal_foreign_SystemLookup_WindowsFallbackSymbols.class, Class.class), name); + Pointer func = getSyslookupFunc(value.ordinal(), Target_jdk_internal_foreign_SystemLookup_WindowsFallbackSymbols.class.getEnumConstants().length); + assert func.isNonNull() : "Function pointer for " + value.name() + " is NULL but shouldn't."; + return func; + } catch (IllegalArgumentException e) { + return Word.nullPointer(); + } + } } diff --git a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_foreign_SystemLookup.java b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_foreign_SystemLookup.java index 3469c9fdaac3..46bb52aa4bc7 100644 --- a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_foreign_SystemLookup.java +++ b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_foreign_SystemLookup.java @@ -27,7 +27,6 @@ import java.lang.foreign.MemorySegment; import java.util.Optional; -import com.oracle.svm.core.annotate.Delete; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; @@ -44,7 +43,11 @@ public Optional find(String name) { } } +/* + * IMPORTANT: If the substitution target (i.e. enum + * 'jdk.internal.foreign.SystemLookup$WindowsFallbackSymbols') changes, ensure that the enum values + * are still in sync with 'com.oracle.svm.native.libchelper/src/syslookup.c'. + */ @TargetClass(className = "jdk.internal.foreign.SystemLookup", innerClass = "WindowsFallbackSymbols", onlyWith = ForeignFunctionsEnabled.class) -@Delete final class Target_jdk_internal_foreign_SystemLookup_WindowsFallbackSymbols { } diff --git a/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/ForeignGraphKit.java b/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/ForeignGraphKit.java index 2933076fba47..4449c77f3fbd 100644 --- a/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/ForeignGraphKit.java +++ b/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/ForeignGraphKit.java @@ -44,6 +44,7 @@ import jdk.graal.compiler.nodes.java.NewArrayNode; import jdk.graal.compiler.replacements.nodes.ReadRegisterNode; import jdk.graal.compiler.replacements.nodes.WriteRegisterNode; +import jdk.vm.ci.amd64.AMD64; import jdk.vm.ci.code.Register; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.MetaAccessProvider; @@ -125,9 +126,14 @@ public ValueNode bindRegister(Register register, JavaKind kind) { return append(new ReadRegisterNode(register, kind, false, false)); } + private JavaKind getRegisterKind(Register register) { + // GR-62468: possible incorrect backup/restore of XMM register (Windows only) + return AMD64.XMM.equals(register.getRegisterCategory()) ? JavaKind.Double : getWordTypes().getWordKind(); + } + public Map saveRegisters(Iterable registers) { return StreamSupport.stream(registers.spliterator(), false) - .collect(Collectors.toMap(reg -> reg, register -> bindRegister(register, getWordTypes().getWordKind()))); + .collect(Collectors.toMap(reg -> reg, register -> bindRegister(register, getRegisterKind(register)))); } public void restoreRegisters(Map save) { diff --git a/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/UpcallStub.java b/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/UpcallStub.java index cb4a1891dc90..d1be270bc28e 100644 --- a/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/UpcallStub.java +++ b/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/UpcallStub.java @@ -188,8 +188,11 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos List arguments = new ArrayList<>(kit.getInitialArguments()); /* - * Prologue: save function-preserved registers, allocate return space if needed, transition - * from native to Java. + * Prologue: save callee-save registers, allocate return space if needed, transition from + * native to Java. + * + * Saving the callee-save registers is necessary because the invocation of the high-level + * stub uses the Java calling convention which may interfere with those registers. */ assert !savedRegisters.asList().contains(registers.methodHandle()); assert !savedRegisters.asList().contains(registers.isolate()); @@ -213,11 +216,11 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos FrameState frameState = new FrameState(BytecodeFrame.UNKNOWN_BCI); frameState.invalidateForDeoptimization(); returnBuffer.setStateAfter(kit.getGraph().add(frameState)); - arguments.add(0, returnBuffer); + arguments.addFirst(returnBuffer); } /* Transfers to the Java-side stub; note that exceptions should be handled there. */ - arguments.add(0, mh); + arguments.addFirst(mh); InvokeWithExceptionNode returnValue = kit.createJavaCallWithException(CallTargetNode.InvokeKind.Static, highLevelStub, arguments.toArray(ValueNode.EMPTY_ARRAY)); kit.exceptionPart(); kit.append(new DeadEndNode()); @@ -232,6 +235,7 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos long offset = 0; for (AssignedLocation loc : AbiUtils.create().toMemoryAssignment(jep.returnAssignment(), true)) { assert loc.assignsToRegister(); + // an output location must not be a callee-save register assert !save.containsKey(loc.register()); AddressNode address = new OffsetAddressNode(returnBuffer, ConstantNode.forLong(offset, kit.getGraph())); @@ -307,7 +311,7 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos List allArguments = new ArrayList<>(kit.getInitialArguments()); - ValueNode mh = allArguments.remove(0); + ValueNode mh = allArguments.removeFirst(); /* If adaptations are ever needed for upcalls, they should most likely be applied here */ allArguments = kit.boxArguments(allArguments, jep.handleType()); ValueNode arguments = kit.packArguments(allArguments); diff --git a/substratevm/src/com.oracle.svm.native.libchelper/src/syslookup.c b/substratevm/src/com.oracle.svm.native.libchelper/src/syslookup.c new file mode 100644 index 000000000000..6d116c05bfed --- /dev/null +++ b/substratevm/src/com.oracle.svm.native.libchelper/src/syslookup.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifdef _WIN64 + +#include +#include +#include + +// Forces generation of code on Windows for functions that are not available as regular symbols +static void* _syslookup_funcs[] = { + // stdio.h + &fprintf, + &fprintf_s, + &fscanf, + &fscanf_s, + &fwprintf, + &fwprintf_s, + &fwscanf, + &fwscanf_s, + &printf, + &printf_s, + &scanf, + &scanf_s, + &snprintf, + &sprintf, + &sprintf_s, + &sscanf, + &sscanf_s, + &swprintf, + &swprintf_s, + &swscanf, + &swscanf_s, + &vfprintf, + &vfprintf_s, + &vfscanf, + &vfscanf_s, + &vfwprintf, + &vfwprintf_s, + &vfwscanf, + &vfwscanf_s, + &vprintf, + &vprintf_s, + &vscanf, + &vscanf_s, + &vsnprintf, + &vsnprintf_s, + &vsprintf, + &vsprintf_s, + &vsscanf, + &vsscanf_s, + &vswprintf, + &vswprintf_s, + &vswscanf, + &vswscanf_s, + &vwprintf, + &vwprintf_s, + &vwscanf, + &vwscanf_s, + &wprintf, + &wprintf_s, + &wscanf, + &wscanf_s, + + // time.h + &gmtime +}; + +#define N_SYSLOOKUP_FUNCS (sizeof(_syslookup_funcs) / sizeof(void*)) + +void* __svm_get_syslookup_func(int32_t i, int32_t n_expected) { + if (n_expected != N_SYSLOOKUP_FUNCS || i < 0 || i >= N_SYSLOOKUP_FUNCS) { + return NULL; + } + return _syslookup_funcs[i]; +} + +#endif