diff --git a/make/autoconf/configure.ac b/make/autoconf/configure.ac index 05f2a51a7ed..31cda63f033 100644 --- a/make/autoconf/configure.ac +++ b/make/autoconf/configure.ac @@ -218,6 +218,10 @@ JDKOPT_SETUP_ADDRESS_SANITIZER # UndefinedBehaviorSanitizer JDKOPT_SETUP_UNDEFINED_BEHAVIOR_SANITIZER +# Fallback linker +# This needs to go before 'LIB_DETERMINE_DEPENDENCIES' +JDKOPT_SETUP_FALLBACK_LINKER + ############################################################################### # # Check dependencies for external and internal libraries. diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index 06749ebe761..0a7242b6e25 100644 --- a/make/autoconf/jdk-options.m4 +++ b/make/autoconf/jdk-options.m4 @@ -864,3 +864,22 @@ AC_DEFUN([JDKOPT_SETUP_MACOSX_SIGNING], AC_SUBST(MACOSX_CODESIGN_MODE) fi ]) + +################################################################################ +# +# fallback linker +# +AC_DEFUN_ONCE([JDKOPT_SETUP_FALLBACK_LINKER], +[ + FALLBACK_LINKER_DEFAULT=false + + if HOTSPOT_CHECK_JVM_VARIANT(zero); then + FALLBACK_LINKER_DEFAULT=true + fi + + UTIL_ARG_ENABLE(NAME: fallback-linker, DEFAULT: $FALLBACK_LINKER_DEFAULT, + RESULT: ENABLE_FALLBACK_LINKER, + DESC: [enable libffi-based fallback implementation of java.lang.foreign.Linker], + CHECKING_MSG: [if fallback linker enabled]) + AC_SUBST(ENABLE_FALLBACK_LINKER) +]) diff --git a/make/autoconf/libraries.m4 b/make/autoconf/libraries.m4 index 7a1d8d80bb2..25bf79463df 100644 --- a/make/autoconf/libraries.m4 +++ b/make/autoconf/libraries.m4 @@ -82,7 +82,7 @@ AC_DEFUN_ONCE([LIB_DETERMINE_DEPENDENCIES], fi # Check if ffi is needed - if HOTSPOT_CHECK_JVM_VARIANT(zero); then + if HOTSPOT_CHECK_JVM_VARIANT(zero) || test "x$ENABLE_FALLBACK_LINKER" = "xtrue"; then NEEDS_LIB_FFI=true else NEEDS_LIB_FFI=false diff --git a/make/autoconf/spec.gmk.in b/make/autoconf/spec.gmk.in index 84ff5955eb1..64d44713406 100644 --- a/make/autoconf/spec.gmk.in +++ b/make/autoconf/spec.gmk.in @@ -407,6 +407,9 @@ TEST_JOBS?=@TEST_JOBS@ DEFAULT_MAKE_TARGET:=@DEFAULT_MAKE_TARGET@ DEFAULT_LOG:=@DEFAULT_LOG@ +# Fallback linker +ENABLE_FALLBACK_LINKER:=@ENABLE_FALLBACK_LINKER@ + FREETYPE_TO_USE:=@FREETYPE_TO_USE@ FREETYPE_LIBS:=@FREETYPE_LIBS@ FREETYPE_CFLAGS:=@FREETYPE_CFLAGS@ diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index 064dd7bbd32..f789e71fbdf 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -575,11 +575,12 @@ var getJibProfilesProfiles = function (input, common, data) { "linux-x64-zero": { target_os: "linux", target_cpu: "x64", - dependencies: ["devkit", "gtest"], + dependencies: ["devkit", "gtest", "libffi"], configure_args: concat(common.configure_args_64bit, [ "--with-zlib=system", "--with-jvm-variants=zero", - "--enable-libffi-bundling" + "--with-libffi=" + input.get("libffi", "home_path"), + "--enable-libffi-bundling", ]) }, @@ -729,6 +730,40 @@ var getJibProfilesProfiles = function (input, common, data) { common.debug_profile_artifacts(artifactData[name])); }); + // Define artifact just for linux-x64-zero, which is the only one we test on + ["linux-x64"].forEach(function (name) { + var o = artifactData[name] + var pf = o.platform + var jdk_subdir = (o.jdk_subdir != null ? o.jdk_subdir : "jdk-" + data.version); + var jdk_suffix = (o.jdk_suffix != null ? o.jdk_suffix : "tar.gz"); + var zeroName = name + "-zero"; + profiles[zeroName].artifacts = { + jdk: { + local: "bundles/\\(jdk.*bin." + jdk_suffix + "\\)", + remote: [ + "bundles/" + pf + "/jdk-" + data.version + "_" + pf + "_bin-zero." + jdk_suffix, + ], + subdir: jdk_subdir, + exploded: "images/jdk", + }, + test: { + local: "bundles/\\(jdk.*bin-tests.tar.gz\\)", + remote: [ + "bundles/" + pf + "/jdk-" + data.version + "_" + pf + "_bin-zero-tests.tar.gz", + ], + exploded: "images/test" + }, + jdk_symbols: { + local: "bundles/\\(jdk.*bin-symbols.tar.gz\\)", + remote: [ + "bundles/" + pf + "/jdk-" + data.version + "_" + pf + "_bin-zero-symbols.tar.gz", + ], + subdir: jdk_subdir, + exploded: "images/jdk" + }, + }; + }); + buildJdkDep = input.build_os + "-" + input.build_cpu + ".jdk"; docsProfiles = { "docs": { @@ -1222,6 +1257,13 @@ var getJibProfilesDependencies = function (input, common) { ext: "tar.gz", revision: "1.8.1" }, + + libffi: { + organization: common.organization, + module: "libffi-" + input.build_platform, + ext: "tar.gz", + revision: "3.4.2+1.0" + }, }; return dependencies; diff --git a/make/data/hotspot-symbols/symbols-shared b/make/data/hotspot-symbols/symbols-shared index ab6adf06d4d..e3975f907cb 100644 --- a/make/data/hotspot-symbols/symbols-shared +++ b/make/data/hotspot-symbols/symbols-shared @@ -30,5 +30,6 @@ jio_vsnprintf JNI_CreateJavaVM JNI_GetCreatedJavaVMs JNI_GetDefaultJavaVMInitArgs +JVM_IsForeignLinkerSupported JVM_FindClassFromBootLoader JVM_InitAgentProperties diff --git a/make/devkit/createLibffiBundle.sh b/make/devkit/createLibffiBundle.sh new file mode 100644 index 00000000000..62d714a5148 --- /dev/null +++ b/make/devkit/createLibffiBundle.sh @@ -0,0 +1,111 @@ +#!/bin/bash +# +# Copyright (c) 2023, 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. +# + +# This script generates a libffi bundle. On linux by building it from source +# using a devkit, which should match the devkit used to build the JDK. +# +# Set MAKE_ARGS to add parameters to make. Ex: +# +# $ MAKE_ARGS=-j32 bash createLibffiBundle.sh +# +# The script tries to behave well on multiple invocations, only performing steps +# not already done. To redo a step, manually delete the target files from that +# step. +# +# Note that the libtool and texinfo packages are needed to build libffi +# $ sudo apt install libtool texinfo + +LIBFFI_VERSION=3.4.2 + +BUNDLE_NAME=libffi-$LIBFFI_VERSION.tar.gz + +SCRIPT_FILE="$(basename $0)" +SCRIPT_DIR="$(cd "$(dirname $0)" > /dev/null && pwd)" +OUTPUT_DIR="${SCRIPT_DIR}/../../build/libffi" +SRC_DIR="$OUTPUT_DIR/src" +DOWNLOAD_DIR="$OUTPUT_DIR/download" +INSTALL_DIR="$OUTPUT_DIR/install" +IMAGE_DIR="$OUTPUT_DIR/image" + +USAGE="$0 <devkit dir>" + +if [ "$1" = "" ]; then + echo $USAGE + exit 1 +fi +DEVKIT_DIR="$1" + +# Download source distros +mkdir -p $DOWNLOAD_DIR +cd $DOWNLOAD_DIR +SOURCE_TAR=v$LIBFFI_VERSION.tar.gz +if [ ! -f $SOURCE_TAR ]; then + wget https://github.com/libffi/libffi/archive/refs/tags/v$LIBFFI_VERSION.tar.gz +fi + +# Unpack src +mkdir -p $SRC_DIR +cd $SRC_DIR +LIBFFI_DIRNAME=libffi-$LIBFFI_VERSION +LIBFFI_DIR=$SRC_DIR/$LIBFFI_DIRNAME +if [ ! -d $LIBFFI_DIRNAME ]; then + echo "Unpacking $SOURCE_TAR" + tar xf $DOWNLOAD_DIR/$SOURCE_TAR +fi + +# Build +cd $LIBFFI_DIR +if [ ! -e $LIBFFI_DIR/configure ]; then + bash ./autogen.sh +fi +bash ./configure --prefix=$INSTALL_DIR CC=$DEVKIT_DIR/bin/gcc CXX=$DEVKIT_DIR/bin/g++ + +# Run with nice to keep system usable during build. +nice make $MAKE_ARGS install + +mkdir -p $IMAGE_DIR +# Extract what we need into an image +if [ ! -e $IMAGE_DIR/lib/libffi.so ]; then + echo "Copying libffi.so* to image" + mkdir -p $IMAGE_DIR/lib + cp -a $INSTALL_DIR/lib64/libffi.so* $IMAGE_DIR/lib/ +fi +if [ ! -e $IMAGE_DIR/include/ ]; then + echo "Copying include to image" + mkdir -p $IMAGE_DIR/include + cp -a $INSTALL_DIR/include/. $IMAGE_DIR/include/ +fi +if [ ! -e $IMAGE_DIR/$SCRIPT_FILE ]; then + echo "Copying this script to image" + cp -a $SCRIPT_DIR/$SCRIPT_FILE $IMAGE_DIR/ +fi + +# Create bundle +if [ ! -e $OUTPUT_DIR/$BUNDLE_NAME ]; then + echo "Creating $OUTPUT_DIR/$BUNDLE_NAME" + cd $IMAGE_DIR + tar zcf $OUTPUT_DIR/$BUNDLE_NAME * +fi diff --git a/make/modules/java.base/Lib.gmk b/make/modules/java.base/Lib.gmk index 3b782577258..fb77d904297 100644 --- a/make/modules/java.base/Lib.gmk +++ b/make/modules/java.base/Lib.gmk @@ -217,3 +217,18 @@ $(eval $(call SetupJdkLibrary, BUILD_SYSLOOKUPLIB, \ )) TARGETS += $(BUILD_SYSLOOKUPLIB) + +################################################################################ +# Create fallback linker lib + +ifeq ($(ENABLE_FALLBACK_LINKER), true) + $(eval $(call SetupJdkLibrary, BUILD_LIBFALLBACKLINKER, \ + NAME := fallbackLinker, \ + CFLAGS := $(CFLAGS_JDKLIB) $(LIBFFI_CFLAGS), \ + LDFLAGS := $(LDFLAGS_JDKLIB) \ + $(call SET_SHARED_LIBRARY_ORIGIN), \ + LIBS := $(LIBFFI_LIBS), \ + )) + + TARGETS += $(BUILD_LIBFALLBACKLINKER) +endif diff --git a/src/hotspot/cpu/aarch64/foreignGlobals_aarch64.cpp b/src/hotspot/cpu/aarch64/foreignGlobals_aarch64.cpp index 90c1942c32d..f5b668b4dae 100644 --- a/src/hotspot/cpu/aarch64/foreignGlobals_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/foreignGlobals_aarch64.cpp @@ -33,6 +33,10 @@ #include "prims/vmstorage.hpp" #include "utilities/formatBuffer.hpp" +bool ForeignGlobals::has_port() { + return true; +} + bool ABIDescriptor::is_volatile_reg(Register reg) const { return _integer_argument_registers.contains(reg) || _integer_additional_volatile_registers.contains(reg); diff --git a/src/hotspot/cpu/arm/foreignGlobals_arm.cpp b/src/hotspot/cpu/arm/foreignGlobals_arm.cpp index 5438cbe5cd6..e8a697667d5 100644 --- a/src/hotspot/cpu/arm/foreignGlobals_arm.cpp +++ b/src/hotspot/cpu/arm/foreignGlobals_arm.cpp @@ -29,6 +29,10 @@ class MacroAssembler; +bool ForeignGlobals::has_port() { + return false; +} + const ABIDescriptor ForeignGlobals::parse_abi_descriptor(jobject jabi) { Unimplemented(); return {}; diff --git a/src/hotspot/cpu/ppc/foreignGlobals_ppc.cpp b/src/hotspot/cpu/ppc/foreignGlobals_ppc.cpp index b685023656d..bf2ddc9de2b 100644 --- a/src/hotspot/cpu/ppc/foreignGlobals_ppc.cpp +++ b/src/hotspot/cpu/ppc/foreignGlobals_ppc.cpp @@ -29,6 +29,10 @@ class MacroAssembler; +bool ForeignGlobals::has_port() { + return false; +} + // Stubbed out, implement later const ABIDescriptor ForeignGlobals::parse_abi_descriptor(jobject jabi) { Unimplemented(); diff --git a/src/hotspot/cpu/riscv/foreignGlobals_riscv.cpp b/src/hotspot/cpu/riscv/foreignGlobals_riscv.cpp index aefbb56a491..30cc6e1cf50 100644 --- a/src/hotspot/cpu/riscv/foreignGlobals_riscv.cpp +++ b/src/hotspot/cpu/riscv/foreignGlobals_riscv.cpp @@ -30,6 +30,10 @@ class MacroAssembler; +bool ForeignGlobals::has_port() { + return false; +} + const ABIDescriptor ForeignGlobals::parse_abi_descriptor(jobject jabi) { ShouldNotCallThis(); return {}; diff --git a/src/hotspot/cpu/s390/foreignGlobals_s390.cpp b/src/hotspot/cpu/s390/foreignGlobals_s390.cpp index 5438cbe5cd6..e8a697667d5 100644 --- a/src/hotspot/cpu/s390/foreignGlobals_s390.cpp +++ b/src/hotspot/cpu/s390/foreignGlobals_s390.cpp @@ -29,6 +29,10 @@ class MacroAssembler; +bool ForeignGlobals::has_port() { + return false; +} + const ABIDescriptor ForeignGlobals::parse_abi_descriptor(jobject jabi) { Unimplemented(); return {}; diff --git a/src/hotspot/cpu/x86/foreignGlobals_x86_32.cpp b/src/hotspot/cpu/x86/foreignGlobals_x86_32.cpp index 8a31955f4d1..cea03858f2c 100644 --- a/src/hotspot/cpu/x86/foreignGlobals_x86_32.cpp +++ b/src/hotspot/cpu/x86/foreignGlobals_x86_32.cpp @@ -28,6 +28,10 @@ class MacroAssembler; +bool ForeignGlobals::has_port() { + return false; +} + const ABIDescriptor ForeignGlobals::parse_abi_descriptor(jobject jabi) { Unimplemented(); return {}; diff --git a/src/hotspot/cpu/x86/foreignGlobals_x86_64.cpp b/src/hotspot/cpu/x86/foreignGlobals_x86_64.cpp index 74afbe4fd61..0a7d18b2689 100644 --- a/src/hotspot/cpu/x86/foreignGlobals_x86_64.cpp +++ b/src/hotspot/cpu/x86/foreignGlobals_x86_64.cpp @@ -30,6 +30,10 @@ #include "runtime/sharedRuntime.hpp" #include "utilities/formatBuffer.hpp" +bool ForeignGlobals::has_port() { + return true; +} + bool ABIDescriptor::is_volatile_reg(Register reg) const { return _integer_argument_registers.contains(reg) || _integer_additional_volatile_registers.contains(reg); diff --git a/src/hotspot/cpu/zero/foreignGlobals_zero.cpp b/src/hotspot/cpu/zero/foreignGlobals_zero.cpp index 7c35da7e3e0..32d548e2a48 100644 --- a/src/hotspot/cpu/zero/foreignGlobals_zero.cpp +++ b/src/hotspot/cpu/zero/foreignGlobals_zero.cpp @@ -28,6 +28,10 @@ class MacroAssembler; +bool ForeignGlobals::has_port() { + return false; +} + const ABIDescriptor ForeignGlobals::parse_abi_descriptor(jobject jabi) { ShouldNotCallThis(); return {}; diff --git a/src/hotspot/cpu/zero/globalDefinitions_zero.hpp b/src/hotspot/cpu/zero/globalDefinitions_zero.hpp index 9db2060b8dd..6e09da517b1 100644 --- a/src/hotspot/cpu/zero/globalDefinitions_zero.hpp +++ b/src/hotspot/cpu/zero/globalDefinitions_zero.hpp @@ -32,7 +32,7 @@ #define SUPPORT_MONITOR_COUNT -#ifndef FFI_GO_CLOSURES +#ifdef __APPLE__ #define FFI_GO_CLOSURES 0 #endif diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index 98e0233887c..b33aea42071 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -174,6 +174,9 @@ JVM_IsPreviewEnabled(void); JNIEXPORT jboolean JNICALL JVM_IsContinuationsSupported(void); +JNIEXPORT jboolean JNICALL +JVM_IsForeignLinkerSupported(void); + JNIEXPORT void JNICALL JVM_InitializeFromArchive(JNIEnv* env, jclass cls); diff --git a/src/hotspot/share/prims/foreignGlobals.hpp b/src/hotspot/share/prims/foreignGlobals.hpp index d0160f23226..9383756dcef 100644 --- a/src/hotspot/share/prims/foreignGlobals.hpp +++ b/src/hotspot/share/prims/foreignGlobals.hpp @@ -76,6 +76,8 @@ class ForeignGlobals { static void parse_register_array(objArrayOop jarray, StorageType type_index, GrowableArray<T>& array, T (*converter)(int)); public: + static bool has_port(); + static const ABIDescriptor parse_abi_descriptor(jobject jabi); static const CallRegs parse_call_regs(jobject jconv); static VMStorage parse_vmstorage(oop storage); diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index d79b31bdb30..7941b1f4878 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -63,6 +63,7 @@ #include "oops/objArrayKlass.hpp" #include "oops/objArrayOop.inline.hpp" #include "oops/oop.inline.hpp" +#include "prims/foreignGlobals.hpp" #include "prims/jvm_misc.hpp" #include "prims/jvmtiExport.hpp" #include "prims/jvmtiThreadState.inline.hpp" @@ -3474,6 +3475,10 @@ JVM_LEAF(jboolean, JVM_IsContinuationsSupported(void)) return VMContinuations ? JNI_TRUE : JNI_FALSE; JVM_END +JVM_LEAF(jboolean, JVM_IsForeignLinkerSupported(void)) + return ForeignGlobals::has_port() ? JNI_TRUE : JNI_FALSE; +JVM_END + // String support /////////////////////////////////////////////////////////////////////////// JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str)) diff --git a/src/java.base/share/classes/jdk/internal/foreign/CABI.java b/src/java.base/share/classes/jdk/internal/foreign/CABI.java index 52718ff3de2..ec1722bae88 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/CABI.java +++ b/src/java.base/share/classes/jdk/internal/foreign/CABI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -25,6 +25,9 @@ */ package jdk.internal.foreign; +import jdk.internal.foreign.abi.fallback.FallbackLinker; +import jdk.internal.vm.ForeignLinkerSupport; + import static java.lang.foreign.ValueLayout.ADDRESS; import static sun.security.action.GetPropertyAction.privilegedGetProperty; @@ -33,45 +36,49 @@ public enum CABI { WIN_64, LINUX_AARCH_64, MAC_OS_AARCH_64, - WIN_AARCH_64; + WIN_AARCH_64, + FALLBACK, + UNSUPPORTED; - private static final CABI ABI; - private static final String ARCH; - private static final String OS; - private static final long ADDRESS_SIZE; + private static final CABI CURRENT = computeCurrent(); - static { - ARCH = privilegedGetProperty("os.arch"); - OS = privilegedGetProperty("os.name"); - ADDRESS_SIZE = ADDRESS.bitSize(); - // might be running in a 32-bit VM on a 64-bit platform. - // addressSize will be correctly 32 - if ((ARCH.equals("amd64") || ARCH.equals("x86_64")) && ADDRESS_SIZE == 64) { - if (OS.startsWith("Windows")) { - ABI = WIN_64; - } else { - ABI = SYS_V; - } - } else if (ARCH.equals("aarch64")) { - if (OS.startsWith("Mac")) { - ABI = MAC_OS_AARCH_64; - } else if (OS.startsWith("Windows")) { - ABI = WIN_AARCH_64; - } else { - // The Linux ABI follows the standard AAPCS ABI - ABI = LINUX_AARCH_64; + private static CABI computeCurrent() { + String abi = privilegedGetProperty("jdk.internal.foreign.CABI"); + if (abi != null) { + return CABI.valueOf(abi); + } + + if (ForeignLinkerSupport.isSupported()) { + // figure out the ABI based on the platform + String arch = privilegedGetProperty("os.arch"); + String os = privilegedGetProperty("os.name"); + long addressSize = ADDRESS.bitSize(); + // might be running in a 32-bit VM on a 64-bit platform. + // addressSize will be correctly 32 + if ((arch.equals("amd64") || arch.equals("x86_64")) && addressSize == 64) { + if (os.startsWith("Windows")) { + return WIN_64; + } else { + return SYS_V; + } + } else if (arch.equals("aarch64")) { + if (os.startsWith("Mac")) { + return MAC_OS_AARCH_64; + } else if (os.startsWith("Windows")) { + return WIN_AARCH_64; + } else { + // The Linux ABI follows the standard AAPCS ABI + return LINUX_AARCH_64; + } } - } else { - // unsupported - ABI = null; + } else if (FallbackLinker.isSupported()) { + return FALLBACK; // fallback linker } + + return UNSUPPORTED; } public static CABI current() { - if (ABI == null) { - throw new UnsupportedOperationException( - "Unsupported os, arch, or address size: " + OS + ", " + ARCH + ", " + ADDRESS_SIZE); - } - return ABI; + return CURRENT; } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java b/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java index a7677ba53b2..9410a2b7319 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java +++ b/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java @@ -39,9 +39,12 @@ import sun.security.action.GetPropertyAction; import static java.lang.foreign.ValueLayout.ADDRESS; +import static sun.security.action.GetPropertyAction.privilegedGetProperty; public final class SystemLookup implements SymbolLookup { + private static final boolean IS_WINDOWS = privilegedGetProperty("os.name").startsWith("Windows"); + private SystemLookup() { } private static final SystemLookup INSTANCE = new SystemLookup(); @@ -57,10 +60,11 @@ private SystemLookup() { } private static SymbolLookup makeSystemLookup() { try { - return switch (CABI.current()) { - case SYS_V, LINUX_AARCH_64, MAC_OS_AARCH_64 -> libLookup(libs -> libs.load(jdkLibraryPath("syslookup"))); - case WIN_64, WIN_AARCH_64 -> makeWindowsLookup(); // out of line to workaround javac crash - }; + if (IS_WINDOWS) { + return makeWindowsLookup(); + } else { + return libLookup(libs -> libs.load(jdkLibraryPath("syslookup"))); + } } catch (Throwable ex) { // This can happen in the event of a library loading failure - e.g. if one of the libraries the // system lookup depends on cannot be loaded for some reason. In such extreme cases, rather than @@ -118,10 +122,7 @@ private static SymbolLookup libLookup(Function<RawNativeLibraries, NativeLibrary */ private static Path jdkLibraryPath(String name) { Path javahome = Path.of(GetPropertyAction.privilegedGetProperty("java.home")); - String lib = switch (CABI.current()) { - case SYS_V, LINUX_AARCH_64, MAC_OS_AARCH_64 -> "lib"; - case WIN_64, WIN_AARCH_64 -> "bin"; - }; + String lib = IS_WINDOWS ? "bin" : "lib"; String libname = System.mapLibraryName(name); return javahome.resolve(lib).resolve(libname); } diff --git a/src/java.base/share/classes/jdk/internal/foreign/Utils.java b/src/java.base/share/classes/jdk/internal/foreign/Utils.java index 1124a0a0440..e069963adda 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/Utils.java +++ b/src/java.base/share/classes/jdk/internal/foreign/Utils.java @@ -29,11 +29,14 @@ import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; import java.lang.foreign.SegmentAllocator; +import java.lang.foreign.StructLayout; import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.invoke.VarHandle; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; @@ -202,4 +205,40 @@ public static void checkAllocationSizeAndAlign(long byteSize, long byteAlignment throw new IllegalArgumentException("Invalid alignment constraint : " + byteAlignment); } } + + private static long computePadding(long offset, long align) { + boolean isAligned = offset == 0 || offset % align == 0; + if (isAligned) { + return 0; + } else { + long gap = offset % align; + return align - gap; + } + } + + /** + * {@return return a struct layout constructed from the given elements, with padding computed automatically} + * + * @param elements the structs' fields + */ + public static StructLayout computePaddedStructLayout(MemoryLayout... elements) { + long offset = 0L; + List<MemoryLayout> layouts = new ArrayList<>(); + long align = 0; + for (MemoryLayout l : elements) { + long padding = computePadding(offset, l.bitAlignment()); + if (padding != 0) { + layouts.add(MemoryLayout.paddingLayout(padding)); + offset += padding; + } + layouts.add(l); + align = Math.max(align, l.bitAlignment()); + offset += l.bitSize(); + } + long padding = computePadding(offset, align); + if (padding != 0) { + layouts.add(MemoryLayout.paddingLayout(padding)); + } + return MemoryLayout.structLayout(layouts.toArray(MemoryLayout[]::new)); + } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java index d97da7cf5c5..ea63ffb8e00 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java @@ -27,6 +27,7 @@ import jdk.internal.foreign.SystemLookup; import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker; import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker; +import jdk.internal.foreign.abi.fallback.FallbackLinker; import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64Linker; import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker; import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker; @@ -44,7 +45,9 @@ import java.util.Objects; public abstract sealed class AbstractLinker implements Linker permits LinuxAArch64Linker, MacOsAArch64Linker, - SysVx64Linker, WindowsAArch64Linker, Windowsx64Linker { + SysVx64Linker, WindowsAArch64Linker, + Windowsx64Linker, FallbackLinker { + private record LinkRequest(FunctionDescriptor descriptor, LinkerOptions options) {} private final SoftReferenceCache<LinkRequest, MethodHandle> DOWNCALL_CACHE = new SoftReferenceCache<>(); diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java index 59343475406..8254e35682d 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java @@ -50,7 +50,6 @@ public class DowncallLinker { private static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess(); private static final MethodHandle MH_INVOKE_INTERP_BINDINGS; - private static final MethodHandle MH_CHECK_SYMBOL; private static final MethodHandle EMPTY_OBJECT_ARRAY_HANDLE = MethodHandles.constant(Object[].class, new Object[0]); static { @@ -58,8 +57,6 @@ public class DowncallLinker { MethodHandles.Lookup lookup = MethodHandles.lookup(); MH_INVOKE_INTERP_BINDINGS = lookup.findVirtual(DowncallLinker.class, "invokeInterpBindings", methodType(Object.class, SegmentAllocator.class, Object[].class, InvocationData.class)); - MH_CHECK_SYMBOL = lookup.findStatic(SharedUtils.class, "checkSymbol", - methodType(void.class, MemorySegment.class)); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } @@ -111,7 +108,7 @@ public MethodHandle getBoundMethodHandle() { assert handle.type().parameterType(0) == SegmentAllocator.class; assert handle.type().parameterType(1) == MemorySegment.class; - handle = foldArguments(handle, 1, MH_CHECK_SYMBOL); + handle = foldArguments(handle, 1, SharedUtils.MH_CHECK_SYMBOL); handle = SharedUtils.swapArguments(handle, 0, 1); // normalize parameter order diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java b/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java index 85b43a3b581..97d25bf178d 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java @@ -31,6 +31,7 @@ import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker; import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker; import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64Linker; +import jdk.internal.foreign.abi.fallback.FallbackLinker; import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker; import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker; import jdk.internal.vm.annotation.ForceInline; @@ -40,7 +41,6 @@ import java.lang.foreign.GroupLayout; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentScope; import java.lang.foreign.SegmentAllocator; import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; @@ -51,9 +51,7 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Map; -import java.util.NoSuchElementException; import java.util.Objects; -import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -72,6 +70,7 @@ private SharedUtils() { private static final MethodHandle MH_ALLOC_BUFFER; private static final MethodHandle MH_BUFFER_COPY; private static final MethodHandle MH_REACHABILITY_FENCE; + public static final MethodHandle MH_CHECK_SYMBOL; static { try { @@ -82,6 +81,8 @@ private SharedUtils() { methodType(MemorySegment.class, MemorySegment.class, MemorySegment.class)); MH_REACHABILITY_FENCE = lookup.findStatic(Reference.class, "reachabilityFence", methodType(void.class, Object.class)); + MH_CHECK_SYMBOL = lookup.findStatic(SharedUtils.class, "checkSymbol", + methodType(void.class, MemorySegment.class)); } catch (ReflectiveOperationException e) { throw new BootstrapMethodError(e); } @@ -188,6 +189,8 @@ public static Linker getSystemLinker() { case LINUX_AARCH_64 -> LinuxAArch64Linker.getInstance(); case MAC_OS_AARCH_64 -> MacOsAArch64Linker.getInstance(); case WIN_AARCH_64 -> WindowsAArch64Linker.getInstance(); + case FALLBACK -> FallbackLinker.getInstance(); + case UNSUPPORTED -> throw new UnsupportedOperationException("Platform does not support native linker"); }; } @@ -239,7 +242,7 @@ static MethodHandle mergeArguments(MethodHandle mh, int sourceIndex, int destInd } - static MethodHandle swapArguments(MethodHandle mh, int firstArg, int secondArg) { + public static MethodHandle swapArguments(MethodHandle mh, int firstArg, int secondArg) { MethodType mtype = mh.type(); int[] perms = new int[mtype.parameterCount()]; MethodType swappedType = MethodType.methodType(mtype.returnType()); @@ -257,7 +260,7 @@ private static MethodHandle reachabilityFenceHandle(Class<?> type) { return MH_REACHABILITY_FENCE.asType(MethodType.methodType(void.class, type)); } - static void handleUncaughtException(Throwable t) { + public static void handleUncaughtException(Throwable t) { if (t != null) { t.printStackTrace(); JLA.exit(1); diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIABI.java b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIABI.java new file mode 100644 index 00000000000..ff074ebe5c9 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIABI.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023, 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 jdk.internal.foreign.abi.fallback; + +/** + * enum which maps the {@code ffi_abi} enum + */ +enum FFIABI { + DEFAULT(LibFallback.DEFAULT_ABI); + + private final int value; + + FFIABI(int abi) { + this.value = abi; + } + + int value() { + return value; + } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIStatus.java b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIStatus.java new file mode 100644 index 00000000000..5ac6a95efa6 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIStatus.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, 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 jdk.internal.foreign.abi.fallback; + +/** + * See doc: <a href="https://github.com/libffi/libffi/blob/7611bb4cfe90884b55ad225e0166136a1d2cf22b/doc/libffi.texi#L159"></a> + * <p> + * typedef enum { + * FFI_OK = 0, + * FFI_BAD_TYPEDEF, + * FFI_BAD_ABI, + * FFI_BAD_ARGTYPE + * } ffi_status; + */ +enum FFIStatus { + FFI_OK, + FFI_BAD_TYPEDEF, + FFI_BAD_ABI, + FFI_BAD_ARGTYPE; + + static FFIStatus of(int code) { + return switch (code) { + case 0 -> FFI_OK; + case 1 -> FFI_BAD_TYPEDEF; + case 2 -> FFI_BAD_ABI; + case 3 -> FFI_BAD_ARGTYPE; + default -> throw new IllegalArgumentException("Unknown status code: " + code); + }; + } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIType.java b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIType.java new file mode 100644 index 00000000000..149c1a25e61 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIType.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2023, 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 jdk.internal.foreign.abi.fallback; + +import jdk.internal.foreign.Utils; + +import java.lang.foreign.Arena; +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.PaddingLayout; +import java.lang.foreign.SegmentScope; +import java.lang.foreign.SequenceLayout; +import java.lang.foreign.StructLayout; +import java.lang.foreign.UnionLayout; +import java.lang.foreign.ValueLayout; +import java.lang.invoke.VarHandle; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Predicate; + +import static java.lang.foreign.ValueLayout.ADDRESS; +import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.lang.foreign.ValueLayout.JAVA_LONG; +import static java.lang.foreign.ValueLayout.JAVA_SHORT; + +/** + * typedef struct _ffi_type + * { + * size_t size; + * unsigned short alignment; + * unsigned short type; + * struct _ffi_type **elements; + * } ffi_type; + */ +class FFIType { + private static final ValueLayout SIZE_T = switch ((int) ADDRESS.bitSize()) { + case 64 -> JAVA_LONG; + case 32 -> JAVA_INT; + default -> throw new IllegalStateException("Address size not supported: " + ADDRESS.byteSize()); + }; + private static final ValueLayout UNSIGNED_SHORT = JAVA_SHORT; + private static final StructLayout LAYOUT = Utils.computePaddedStructLayout( + SIZE_T, UNSIGNED_SHORT, UNSIGNED_SHORT.withName("type"), ADDRESS.withName("elements")); + + private static final VarHandle VH_TYPE = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("type")); + private static final VarHandle VH_ELEMENTS = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("elements")); + private static final VarHandle VH_SIZE_T_ARRAY = SIZE_T.arrayElementVarHandle(); + + private static MemorySegment make(List<MemoryLayout> elements, FFIABI abi, SegmentScope scope) { + MemorySegment elementsSeg = MemorySegment.allocateNative((elements.size() + 1) * ADDRESS.byteSize(), scope); + int i = 0; + for (; i < elements.size(); i++) { + MemoryLayout elementLayout = elements.get(i); + MemorySegment elementType = toFFIType(elementLayout, abi, scope); + elementsSeg.setAtIndex(ADDRESS, i, elementType); + } + // elements array is null-terminated + elementsSeg.setAtIndex(ADDRESS, i, MemorySegment.NULL); + + MemorySegment ffiType = MemorySegment.allocateNative(LAYOUT, scope); + VH_TYPE.set(ffiType, LibFallback.STRUCT_TAG); + VH_ELEMENTS.set(ffiType, elementsSeg); + + return ffiType; + } + + private static final Map<Class<?>, MemorySegment> CARRIER_TO_TYPE = Map.of( + boolean.class, LibFallback.UINT8_TYPE, + byte.class, LibFallback.SINT8_TYPE, + short.class, LibFallback.SINT16_TYPE, + char.class, LibFallback.UINT16_TYPE, + int.class, LibFallback.SINT32_TYPE, + long.class, LibFallback.SINT64_TYPE, + float.class, LibFallback.FLOAT_TYPE, + double.class, LibFallback.DOUBLE_TYPE, + MemorySegment.class, LibFallback.POINTER_TYPE + ); + + static MemorySegment toFFIType(MemoryLayout layout, FFIABI abi, SegmentScope scope) { + if (layout instanceof GroupLayout grpl) { + if (grpl instanceof StructLayout strl) { + // libffi doesn't want our padding + List<MemoryLayout> filteredLayouts = strl.memberLayouts().stream() + .filter(Predicate.not(PaddingLayout.class::isInstance)) + .toList(); + MemorySegment structType = make(filteredLayouts, abi, scope); + verifyStructType(strl, filteredLayouts, structType, abi); + return structType; + } + assert grpl instanceof UnionLayout; + throw new UnsupportedOperationException("No unions (TODO)"); + } else if (layout instanceof SequenceLayout sl) { + List<MemoryLayout> elements = Collections.nCopies(Math.toIntExact(sl.elementCount()), sl.elementLayout()); + return make(elements, abi, scope); + } + return Objects.requireNonNull(CARRIER_TO_TYPE.get(((ValueLayout) layout).carrier())); + } + + // verify layout against what libffi sets + private static void verifyStructType(StructLayout structLayout, List<MemoryLayout> filteredLayouts, MemorySegment structType, + FFIABI abi) { + try (Arena verifyArena = Arena.openConfined()) { + MemorySegment offsetsOut = verifyArena.allocate(SIZE_T.byteSize() * filteredLayouts.size()); + LibFallback.getStructOffsets(structType, offsetsOut, abi); + long expectedOffset = 0; + int offsetIdx = 0; + for (MemoryLayout element : structLayout.memberLayouts()) { + if (!(element instanceof PaddingLayout)) { + long ffiOffset = (long) VH_SIZE_T_ARRAY.get(offsetsOut, offsetIdx++); + if (ffiOffset != expectedOffset) { + throw new IllegalArgumentException("Invalid group layout." + + " Offset of '" + element.name().orElse("<unnamed>") + + "': " + expectedOffset + " != " + ffiOffset); + } + } + expectedOffset += element.byteSize(); + } + } + } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FallbackLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FallbackLinker.java new file mode 100644 index 00000000000..c1362c2dc30 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FallbackLinker.java @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2023, 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 jdk.internal.foreign.abi.fallback; + +import jdk.internal.foreign.AbstractMemorySegmentImpl; +import jdk.internal.foreign.MemorySessionImpl; +import jdk.internal.foreign.abi.AbstractLinker; +import jdk.internal.foreign.abi.CapturableState; +import jdk.internal.foreign.abi.LinkerOptions; +import jdk.internal.foreign.abi.SharedUtils; + +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SegmentAllocator; +import java.lang.foreign.SegmentScope; +import java.lang.foreign.ValueLayout; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.ref.Reference; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import static java.lang.foreign.ValueLayout.ADDRESS; +import static java.lang.foreign.ValueLayout.JAVA_LONG; +import static java.lang.invoke.MethodHandles.foldArguments; + +public final class FallbackLinker extends AbstractLinker { + + private static final MethodHandle MH_DO_DOWNCALL; + private static final MethodHandle MH_DO_UPCALL; + + static { + try { + MH_DO_DOWNCALL = MethodHandles.lookup().findStatic(FallbackLinker.class, "doDowncall", + MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class, FallbackLinker.DowncallData.class)); + MH_DO_UPCALL = MethodHandles.lookup().findStatic(FallbackLinker.class, "doUpcall", + MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class, UpcallData.class)); + } catch (ReflectiveOperationException e) { + throw new ExceptionInInitializerError(e); + } + } + + public static FallbackLinker getInstance() { + class Holder { + static final FallbackLinker INSTANCE = new FallbackLinker(); + } + return Holder.INSTANCE; + } + + public static boolean isSupported() { + return LibFallback.SUPPORTED; + } + + @Override + protected MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options) { + MemorySegment cif = makeCif(inferredMethodType, function, FFIABI.DEFAULT, SegmentScope.auto()); + + int capturedStateMask = options.capturedCallState() + .mapToInt(CapturableState::mask) + .reduce(0, (a, b) -> a | b); + DowncallData invData = new DowncallData(cif, function.returnLayout().orElse(null), + function.argumentLayouts(), capturedStateMask); + + MethodHandle target = MethodHandles.insertArguments(MH_DO_DOWNCALL, 2, invData); + + int leadingArguments = 1; // address + MethodType type = inferredMethodType.insertParameterTypes(0, SegmentAllocator.class, MemorySegment.class); + if (capturedStateMask != 0) { + leadingArguments++; + type = type.insertParameterTypes(2, MemorySegment.class); + } + target = target.asCollector(1, Object[].class, inferredMethodType.parameterCount() + leadingArguments); + target = target.asType(type); + target = foldArguments(target, 1, SharedUtils.MH_CHECK_SYMBOL); + target = SharedUtils.swapArguments(target, 0, 1); // normalize parameter order + + return target; + } + + @Override + protected MemorySegment arrangeUpcall(MethodHandle target, MethodType targetType, FunctionDescriptor function, + SegmentScope scope) { + MemorySegment cif = makeCif(targetType, function, FFIABI.DEFAULT, scope); + + UpcallData invData = new UpcallData(target, function.returnLayout().orElse(null), + function.argumentLayouts()); + + MethodHandle doUpcallMH = MethodHandles.insertArguments(MH_DO_UPCALL, 2, invData); + return LibFallback.createClosure(cif, doUpcallMH, scope); + } + + private static MemorySegment makeCif(MethodType methodType, FunctionDescriptor function, FFIABI abi, SegmentScope scope) { + MemorySegment argTypes = MemorySegment.allocateNative(function.argumentLayouts().size() * ADDRESS.byteSize(), scope); + List<MemoryLayout> argLayouts = function.argumentLayouts(); + for (int i = 0; i < argLayouts.size(); i++) { + MemoryLayout layout = argLayouts.get(i); + argTypes.setAtIndex(ADDRESS, i, FFIType.toFFIType(layout, abi, scope)); + } + + MemorySegment returnType = methodType.returnType() != void.class + ? FFIType.toFFIType(function.returnLayout().orElseThrow(), abi, scope) + : LibFallback.VOID_TYPE; + return LibFallback.prepCif(returnType, argLayouts.size(), argTypes, abi, scope); + } + + private record DowncallData(MemorySegment cif, MemoryLayout returnLayout, List<MemoryLayout> argLayouts, + int capturedStateMask) {} + + private static Object doDowncall(SegmentAllocator returnAllocator, Object[] args, DowncallData invData) { + List<MemorySessionImpl> acquiredSessions = new ArrayList<>(); + try (Arena arena = Arena.openConfined()) { + int argStart = 0; + + MemorySegment target = (MemorySegment) args[argStart++]; + MemorySessionImpl targetImpl = ((AbstractMemorySegmentImpl) target).sessionImpl(); + targetImpl.acquire0(); + acquiredSessions.add(targetImpl); + + MemorySegment capturedState = null; + if (invData.capturedStateMask() != 0) { + capturedState = (MemorySegment) args[argStart++]; + MemorySessionImpl capturedStateImpl = ((AbstractMemorySegmentImpl) capturedState).sessionImpl(); + capturedStateImpl.acquire0(); + acquiredSessions.add(capturedStateImpl); + } + + List<MemoryLayout> argLayouts = invData.argLayouts(); + MemorySegment argPtrs = arena.allocate(argLayouts.size() * ADDRESS.byteSize()); + for (int i = 0; i < argLayouts.size(); i++) { + Object arg = args[argStart + i]; + MemoryLayout layout = argLayouts.get(i); + MemorySegment argSeg = arena.allocate(layout); + writeValue(arg, layout, argSeg, addr -> { + MemorySessionImpl sessionImpl = ((AbstractMemorySegmentImpl) addr).sessionImpl(); + sessionImpl.acquire0(); + acquiredSessions.add(sessionImpl); + }); + argPtrs.setAtIndex(ADDRESS, i, argSeg); + } + + MemorySegment retSeg = null; + if (invData.returnLayout() != null) { + retSeg = (invData.returnLayout() instanceof GroupLayout ? returnAllocator : arena).allocate(invData.returnLayout); + } + + LibFallback.doDowncall(invData.cif, target, retSeg, argPtrs, capturedState, invData.capturedStateMask()); + + Reference.reachabilityFence(invData.cif()); + + return readValue(retSeg, invData.returnLayout()); + } finally { + for (MemorySessionImpl session : acquiredSessions) { + session.release0(); + } + } + } + + private record UpcallData(MethodHandle target, MemoryLayout returnLayout, List<MemoryLayout> argLayouts) {} + + private static void doUpcall(MemorySegment retPtr, MemorySegment argPtrs, UpcallData data) throws Throwable { + List<MemoryLayout> argLayouts = data.argLayouts(); + int numArgs = argLayouts.size(); + MemoryLayout retLayout = data.returnLayout(); + try (Arena upcallArena = Arena.openConfined()) { + MemorySegment argsSeg = MemorySegment.ofAddress(argPtrs.address(), numArgs * ADDRESS.byteSize(), upcallArena.scope()); + MemorySegment retSeg = retLayout != null + ? MemorySegment.ofAddress(retPtr.address(), retLayout.byteSize(), upcallArena.scope()) + : null; + + Object[] args = new Object[numArgs]; + for (int i = 0; i < numArgs; i++) { + MemoryLayout argLayout = argLayouts.get(i); + MemorySegment argPtr = MemorySegment.ofAddress(argsSeg.getAtIndex(JAVA_LONG, i), argLayout.byteSize(), upcallArena.scope()); + + args[i] = readValue(argPtr, argLayout); + } + + Object result = data.target().invokeWithArguments(args); + + writeValue(result, data.returnLayout(), retSeg); + } + } + + // where + private static void writeValue(Object arg, MemoryLayout layout, MemorySegment argSeg) { + writeValue(arg, layout, argSeg, addr -> {}); + } + + private static void writeValue(Object arg, MemoryLayout layout, MemorySegment argSeg, + Consumer<MemorySegment> acquireCallback) { + if (layout instanceof ValueLayout.OfBoolean bl) { + argSeg.set(bl, 0, (Boolean) arg); + } else if (layout instanceof ValueLayout.OfByte bl) { + argSeg.set(bl, 0, (Byte) arg); + } else if (layout instanceof ValueLayout.OfShort sl) { + argSeg.set(sl, 0, (Short) arg); + } else if (layout instanceof ValueLayout.OfChar cl) { + argSeg.set(cl, 0, (Character) arg); + } else if (layout instanceof ValueLayout.OfInt il) { + argSeg.set(il, 0, (Integer) arg); + } else if (layout instanceof ValueLayout.OfLong ll) { + argSeg.set(ll, 0, (Long) arg); + } else if (layout instanceof ValueLayout.OfFloat fl) { + argSeg.set(fl, 0, (Float) arg); + } else if (layout instanceof ValueLayout.OfDouble dl) { + argSeg.set(dl, 0, (Double) arg); + } else if (layout instanceof ValueLayout.OfAddress al) { + MemorySegment addrArg = (MemorySegment) arg; + acquireCallback.accept(addrArg); + argSeg.set(al, 0, addrArg); + } else if (layout instanceof GroupLayout) { + argSeg.copyFrom((MemorySegment) arg); // by-value struct + } else { + assert layout == null; + } + } + + private static Object readValue(MemorySegment seg, MemoryLayout layout) { + if (layout instanceof ValueLayout.OfBoolean bl) { + return seg.get(bl, 0); + } else if (layout instanceof ValueLayout.OfByte bl) { + return seg.get(bl, 0); + } else if (layout instanceof ValueLayout.OfShort sl) { + return seg.get(sl, 0); + } else if (layout instanceof ValueLayout.OfChar cl) { + return seg.get(cl, 0); + } else if (layout instanceof ValueLayout.OfInt il) { + return seg.get(il, 0); + } else if (layout instanceof ValueLayout.OfLong ll) { + return seg.get(ll, 0); + } else if (layout instanceof ValueLayout.OfFloat fl) { + return seg.get(fl, 0); + } else if (layout instanceof ValueLayout.OfDouble dl) { + return seg.get(dl, 0); + } else if (layout instanceof ValueLayout.OfAddress al) { + return seg.get(al, 0); + } else if (layout instanceof GroupLayout) { + return seg; + } + assert layout == null; + return null; + } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/LibFallback.java b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/LibFallback.java new file mode 100644 index 00000000000..30405d4cc55 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/LibFallback.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2023, 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 jdk.internal.foreign.abi.fallback; + +import jdk.internal.foreign.abi.SharedUtils; + +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SegmentScope; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; + +class LibFallback { + static final boolean SUPPORTED = tryLoadLibrary(); + + private static boolean tryLoadLibrary() { + try { + System.loadLibrary("fallbackLinker"); + } catch (UnsatisfiedLinkError ule) { + return false; + } + init(); + return true; + } + + static final int DEFAULT_ABI = ffi_default_abi(); + + static final MemorySegment UINT8_TYPE = MemorySegment.ofAddress(ffi_type_uint8()); + static final MemorySegment SINT8_TYPE = MemorySegment.ofAddress(ffi_type_sint8()); + static final MemorySegment UINT16_TYPE = MemorySegment.ofAddress(ffi_type_uint16()); + static final MemorySegment SINT16_TYPE = MemorySegment.ofAddress(ffi_type_sint16()); + static final MemorySegment SINT32_TYPE = MemorySegment.ofAddress(ffi_type_sint32()); + static final MemorySegment SINT64_TYPE = MemorySegment.ofAddress(ffi_type_sint64()); + static final MemorySegment FLOAT_TYPE = MemorySegment.ofAddress(ffi_type_float()); + static final MemorySegment DOUBLE_TYPE = MemorySegment.ofAddress(ffi_type_double()); + static final MemorySegment POINTER_TYPE = MemorySegment.ofAddress(ffi_type_pointer()); + + static final MemorySegment VOID_TYPE = MemorySegment.ofAddress(ffi_type_void()); + static final short STRUCT_TAG = ffi_type_struct(); + + private static final long SIZEOF_CIF = sizeofCif(); + + private static final MethodType UPCALL_TARGET_TYPE = MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class); + + /** + * Do a libffi based downcall. This method wraps the {@code ffi_call} function + * + * @param cif a pointer to a {@code ffi_cif} struct + * @param target the address of the target function + * @param retPtr a pointer to a buffer into which the return value shall be written, or {@code null} if the target + * function does not return a value + * @param argPtrs a pointer to an array of pointers, which each point to an argument value + * @param capturedState a pointer to a buffer into which captured state is written, or {@code null} if no state is + * to be captured + * @param capturedStateMask the bit mask indicating which state to capture + * + * @see jdk.internal.foreign.abi.CapturableState + */ + static void doDowncall(MemorySegment cif, MemorySegment target, MemorySegment retPtr, MemorySegment argPtrs, + MemorySegment capturedState, int capturedStateMask) { + doDowncall(cif.address(), target.address(), + retPtr == null ? 0 : retPtr.address(), argPtrs.address(), + capturedState == null ? 0 : capturedState.address(), capturedStateMask); + } + + /** + * Wrapper for {@code ffi_prep_cif} + * + * @param returnType a pointer to an @{code ffi_type} describing the return type + * @param numArgs the number of arguments + * @param paramTypes a pointer to an array of pointers, which each point to an {@code ffi_type} describing a + * parameter type + * @param abi the abi to be used + * @param scope the scope into which to allocate the returned {@code ffi_cif} struct + * @return a pointer to a prepared {@code ffi_cif} struct + * + * @throws IllegalStateException if the call to {@code ffi_prep_cif} returns a non-zero status code + */ + static MemorySegment prepCif(MemorySegment returnType, int numArgs, MemorySegment paramTypes, FFIABI abi, + SegmentScope scope) throws IllegalStateException { + MemorySegment cif = MemorySegment.allocateNative(SIZEOF_CIF, scope); + checkStatus(ffi_prep_cif(cif.address(), abi.value(), numArgs, returnType.address(), paramTypes.address())); + return cif; + } + + /** + * Create an upcallStub-style closure. This method wraps the {@code ffi_closure_alloc} + * and {@code ffi_prep_closure_loc} functions. + * <p> + * The closure will end up calling into {@link #doUpcall(long, long, MethodHandle)} + * <p> + * The target method handle should have the type {@code (MemorySegment, MemorySegment) -> void}. The first + * argument is a pointer to the buffer into which the native return value should be written. The second argument + * is a pointer to an array of pointers, which each point to a native argument value. + * + * @param cif a pointer to a {@code ffi_cif} struct + * @param target a method handle that points to the target function + * @param scope the scope to which to attach the created upcall stub + * @return the created upcall stub + * + * @throws IllegalStateException if the call to {@code ffi_prep_closure_loc} returns a non-zero status code + * @throws IllegalArgumentException if {@code target} does not have the right type + */ + static MemorySegment createClosure(MemorySegment cif, MethodHandle target, SegmentScope scope) + throws IllegalStateException, IllegalArgumentException { + if (target.type() != UPCALL_TARGET_TYPE) { + throw new IllegalArgumentException("Target handle has wrong type: " + target.type() + " != " + UPCALL_TARGET_TYPE); + } + + long[] ptrs = new long[3]; + checkStatus(createClosure(cif.address(), target, ptrs)); + long closurePtr = ptrs[0]; + long execPtr = ptrs[1]; + long globalTarget = ptrs[2]; + + return MemorySegment.ofAddress(execPtr, 0, scope, () -> freeClosure(closurePtr, globalTarget)); + } + + // the target function for a closure call + private static void doUpcall(long retPtr, long argPtrs, MethodHandle target) { + try { + target.invokeExact(MemorySegment.ofAddress(retPtr), MemorySegment.ofAddress(argPtrs)); + } catch (Throwable t) { + SharedUtils.handleUncaughtException(t); + } + } + + /** + * Wrapper for {@code ffi_get_struct_offsets} + * + * @param structType a pointer to an {@code ffi_type} representing a struct + * @param offsetsOut a pointer to an array of {@code size_t}, with one element for each element of the struct. + * This is an 'out' parameter that will be filled in by this call + * @param abi the abi to be used + * + * @throws IllegalStateException if the call to {@code ffi_get_struct_offsets} returns a non-zero status code + */ + static void getStructOffsets(MemorySegment structType, MemorySegment offsetsOut, FFIABI abi) + throws IllegalStateException { + checkStatus(ffi_get_struct_offsets(abi.value(), structType.address(), offsetsOut.address())); + } + + private static void checkStatus(int code) { + FFIStatus status = FFIStatus.of(code); + if (status != FFIStatus.FFI_OK) { + throw new IllegalStateException("libffi call failed with status: " + status); + } + } + + private static native void init(); + + private static native long sizeofCif(); + + private static native int createClosure(long cif, Object userData, long[] ptrs); + private static native void freeClosure(long closureAddress, long globalTarget); + private static native void doDowncall(long cif, long fn, long rvalue, long avalues, long capturedState, int capturedStateMask); + + private static native int ffi_prep_cif(long cif, int abi, int nargs, long rtype, long atypes); + private static native int ffi_get_struct_offsets(int abi, long type, long offsets); + + private static native int ffi_default_abi(); + private static native short ffi_type_struct(); + + private static native long ffi_type_void(); + private static native long ffi_type_uint8(); + private static native long ffi_type_sint8(); + private static native long ffi_type_uint16(); + private static native long ffi_type_sint16(); + private static native long ffi_type_uint32(); + private static native long ffi_type_sint32(); + private static native long ffi_type_uint64(); + private static native long ffi_type_sint64(); + private static native long ffi_type_float(); + private static native long ffi_type_double(); + private static native long ffi_type_pointer(); +} diff --git a/src/java.base/share/classes/jdk/internal/vm/ForeignLinkerSupport.java b/src/java.base/share/classes/jdk/internal/vm/ForeignLinkerSupport.java new file mode 100644 index 00000000000..5310b251bbd --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/vm/ForeignLinkerSupport.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023, 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 jdk.internal.vm; + +/** + * Defines a static method to test if the VM has support for the foreign java.lang.foreign.Linker. + */ +public class ForeignLinkerSupport { + private static final boolean SUPPORTED = isSupported0(); + + private ForeignLinkerSupport() { + } + + /** + * Return true if the VM has support for the foreign Linker. + */ + public static boolean isSupported() { + return SUPPORTED; + } + + /** + * Ensures that VM has support for the foreign Linker. + * @throws UnsupportedOperationException if not supported + */ + public static void ensureSupported() { + if (!isSupported()) { + throw new UnsupportedOperationException("VM does not support linker"); + } + } + + private static native boolean isSupported0(); +} diff --git a/src/java.base/share/native/libfallbackLinker/fallbackLinker.c b/src/java.base/share/native/libfallbackLinker/fallbackLinker.c new file mode 100644 index 00000000000..761045b1f21 --- /dev/null +++ b/src/java.base/share/native/libfallbackLinker/fallbackLinker.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2023, 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. + */ + +#include "jdk_internal_foreign_abi_fallback_LibFallback.h" + +#include <ffi.h> + +#include <errno.h> +#ifdef _WIN64 +#include <Windows.h> +#include <Winsock2.h> +#endif + +#include "jlong.h" + +static JavaVM* VM; +static jclass LibFallback_class; +static jmethodID LibFallback_doUpcall_ID; +static const char* LibFallback_doUpcall_sig = "(JJLjava/lang/invoke/MethodHandle;)V"; + +JNIEXPORT void JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_init(JNIEnv* env, jclass cls) { + (*env)->GetJavaVM(env, &VM); + LibFallback_class = (*env)->FindClass(env, "jdk/internal/foreign/abi/fallback/LibFallback"); + LibFallback_doUpcall_ID = (*env)->GetStaticMethodID(env, + LibFallback_class, "doUpcall", LibFallback_doUpcall_sig); +} + +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_sizeofCif(JNIEnv* env, jclass cls) { + return sizeof(ffi_cif); +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1prep_1cif(JNIEnv* env, jclass cls, jlong cif, jint abi, jint nargs, jlong rtype, jlong atypes) { + return ffi_prep_cif(jlong_to_ptr(cif), (ffi_abi) abi, (unsigned int) nargs, jlong_to_ptr(rtype), jlong_to_ptr(atypes)); +} +JNIEXPORT jint JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1get_1struct_1offsets(JNIEnv* env, jclass cls, jint abi, jlong type, jlong offsets) { + return ffi_get_struct_offsets((ffi_abi) abi, jlong_to_ptr(type), jlong_to_ptr(offsets)); +} + +static void do_capture_state(int32_t* value_ptr, int captured_state_mask) { + // keep in synch with jdk.internal.foreign.abi.CapturableState + enum PreservableValues { + NONE = 0, + GET_LAST_ERROR = 1, + WSA_GET_LAST_ERROR = 1 << 1, + ERRNO = 1 << 2 + }; +#ifdef _WIN64 + if (captured_state_mask & GET_LAST_ERROR) { + *value_ptr = GetLastError(); + value_ptr++; + } + if (captured_state_mask & WSA_GET_LAST_ERROR) { + *value_ptr = WSAGetLastError(); + value_ptr++; + } +#endif + if (captured_state_mask & ERRNO) { + *value_ptr = errno; + } +} + +JNIEXPORT void JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_doDowncall(JNIEnv* env, jclass cls, jlong cif, jlong fn, jlong rvalue, jlong avalues, jlong jcaptured_state, jint captured_state_mask) { + ffi_call(jlong_to_ptr(cif), jlong_to_ptr(fn), jlong_to_ptr(rvalue), jlong_to_ptr(avalues)); + + if (captured_state_mask != 0) { + int32_t* captured_state = jlong_to_ptr(jcaptured_state); + do_capture_state(captured_state, captured_state_mask); + } +} + +static void do_upcall(ffi_cif* cif, void* ret, void** args, void* user_data) { + // attach thread + JNIEnv* env; + jint result = (*VM)->AttachCurrentThreadAsDaemon(VM, (void**) &env, NULL); + + // call into doUpcall in LibFallback + jobject upcall_data = (jobject) user_data; + (*env)->CallStaticVoidMethod(env, LibFallback_class, LibFallback_doUpcall_ID, + ptr_to_jlong(ret), ptr_to_jlong(args), upcall_data); + + // always detach for now + (*VM)->DetachCurrentThread(VM); +} + +static void free_closure(JNIEnv* env, void* closure, jobject upcall_data) { + ffi_closure_free(closure); + (*env)->DeleteGlobalRef(env, upcall_data); +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_createClosure(JNIEnv* env, jclass cls, jlong cif, jobject upcall_data, jlongArray jptrs) { + void* code; + void* closure = ffi_closure_alloc(sizeof(ffi_closure), &code); + + jobject global_upcall_data = (*env)->NewGlobalRef(env, upcall_data); + + ffi_status status = ffi_prep_closure_loc(closure, jlong_to_ptr(cif), &do_upcall, (void*) global_upcall_data, code); + + if (status != FFI_OK) { + free_closure(env,closure, global_upcall_data); + return status; + } + + jlong* ptrs = (*env)->GetLongArrayElements(env, jptrs, NULL); + ptrs[0] = ptr_to_jlong(closure); + ptrs[1] = ptr_to_jlong(code); + ptrs[2] = ptr_to_jlong(global_upcall_data); + (*env)->ReleaseLongArrayElements(env, jptrs, ptrs, JNI_COMMIT); + + return status; +} + +JNIEXPORT void JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_freeClosure(JNIEnv* env, jclass cls, jlong closure, jlong upcall_data) { + free_closure(env, jlong_to_ptr(closure), jlong_to_ptr(upcall_data)); +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1default_1abi(JNIEnv* env, jclass cls) { + return (jint) FFI_DEFAULT_ABI; +} + +JNIEXPORT jshort JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1struct(JNIEnv* env, jclass cls) { + return (jshort) FFI_TYPE_STRUCT; +} + +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1void(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_void); +} + +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1uint8(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_uint8); +} +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1sint8(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_sint8); +} +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1uint16(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_uint16); +} +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1sint16(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_sint16); +} +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1uint32(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_uint32); +} +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1sint32(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_sint32); +} +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1uint64(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_uint64); +} +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1sint64(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_sint64); +} +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1float(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_float); +} +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1double(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_double); +} +JNIEXPORT jlong JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1pointer(JNIEnv* env, jclass cls) { + return ptr_to_jlong(&ffi_type_pointer); +} diff --git a/src/java.base/share/native/libjava/ForeignLinkerSupport.c b/src/java.base/share/native/libjava/ForeignLinkerSupport.c new file mode 100644 index 00000000000..9d989c09d3b --- /dev/null +++ b/src/java.base/share/native/libjava/ForeignLinkerSupport.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023, 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. + */ + +#include "jni.h" +#include "jvm.h" + +#include "jdk_internal_vm_ForeignLinkerSupport.h" + +JNIEXPORT jboolean JNICALL +Java_jdk_internal_vm_ForeignLinkerSupport_isSupported0(JNIEnv *env, jclass cls) { + return JVM_IsForeignLinkerSupported(); +} diff --git a/test/jdk/java/foreign/TestUnsupportedLinker.java b/test/jdk/java/foreign/TestUnsupportedLinker.java index 286aef17b63..7ebd83a8a99 100644 --- a/test/jdk/java/foreign/TestUnsupportedLinker.java +++ b/test/jdk/java/foreign/TestUnsupportedLinker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -25,7 +25,7 @@ * @test * @enablePreview * - * @run testng/othervm -Dos.arch=unknown -Dos.name=unknown --enable-native-access=ALL-UNNAMED TestUnsupportedLinker + * @run testng/othervm -Djdk.internal.foreign.CABI=UNSUPPORTED --enable-native-access=ALL-UNNAMED TestUnsupportedLinker */ import java.lang.foreign.Linker;