1 /*
  2  * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 package jdk.internal.foreign.abi.fallback;
 26 
 27 import jdk.internal.foreign.abi.SharedUtils;
 28 
 29 import java.lang.foreign.Arena;
 30 import java.lang.foreign.MemorySegment;
 31 import java.lang.invoke.MethodHandle;
 32 import java.lang.invoke.MethodType;
 33 
 34 final class LibFallback {
 35     private LibFallback() {}
 36 
 37     static final boolean SUPPORTED = tryLoadLibrary();
 38 
 39     @SuppressWarnings("removal")
 40     private static boolean tryLoadLibrary() {
 41         return java.security.AccessController.doPrivileged(
 42                 new java.security.PrivilegedAction<>() {
 43                     public Boolean run() {
 44                         try {
 45                             System.loadLibrary("fallbackLinker");
 46                             init();
 47                             return true;
 48                         } catch (UnsatisfiedLinkError ule) {
 49                             return false;
 50                         }
 51                     }
 52                 });
 53     }
 54 
 55     static int defaultABI() { return NativeConstants.DEFAULT_ABI; }
 56 
 57     static MemorySegment uint8Type() { return NativeConstants.UINT8_TYPE; }
 58     static MemorySegment sint8Type() { return NativeConstants.SINT8_TYPE; }
 59     static MemorySegment uint16Type() { return NativeConstants.UINT16_TYPE; }
 60     static MemorySegment sint16Type() { return NativeConstants.SINT16_TYPE; }
 61     static MemorySegment sint32Type() { return NativeConstants.SINT32_TYPE; }
 62     static MemorySegment sint64Type() { return NativeConstants.SINT64_TYPE; }
 63     static MemorySegment floatType() { return NativeConstants.FLOAT_TYPE; }
 64     static MemorySegment doubleType() { return NativeConstants.DOUBLE_TYPE; }
 65     static MemorySegment pointerType() { return NativeConstants.POINTER_TYPE; }
 66     static MemorySegment voidType() { return NativeConstants.VOID_TYPE; }
 67 
 68     static short structTag() { return NativeConstants.STRUCT_TAG; }
 69 
 70     private static final MethodType UPCALL_TARGET_TYPE = MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class);
 71 
 72     /**
 73      * Do a libffi based downcall. This method wraps the {@code ffi_call} function
 74      *
 75      * @param cif a pointer to a {@code ffi_cif} struct
 76      * @param target the address of the target function
 77      * @param retPtr a pointer to a buffer into which the return value shall be written, or {@code null} if the target
 78      *               function does not return a value
 79      * @param argPtrs a pointer to an array of pointers, which each point to an argument value
 80      * @param capturedState a pointer to a buffer into which captured state is written, or {@code null} if no state is
 81      *                      to be captured
 82      * @param capturedStateMask the bit mask indicating which state to capture
 83      *
 84      * @see jdk.internal.foreign.abi.CapturableState
 85      */
 86     static void doDowncall(MemorySegment cif, MemorySegment target, MemorySegment retPtr, MemorySegment argPtrs,
 87                                   MemorySegment capturedState, int capturedStateMask) {
 88             doDowncall(cif.address(), target.address(),
 89                     retPtr == null ? 0 : retPtr.address(), argPtrs.address(),
 90                     capturedState == null ? 0 : capturedState.address(), capturedStateMask);
 91     }
 92 
 93     /**
 94      * Wrapper for {@code ffi_prep_cif}
 95      *
 96      * @param returnType a pointer to an @{code ffi_type} describing the return type
 97      * @param numArgs the number of arguments
 98      * @param paramTypes a pointer to an array of pointers, which each point to an {@code ffi_type} describing a
 99      *                parameter type
100      * @param abi the abi to be used
101      * @param scope the scope into which to allocate the returned {@code ffi_cif} struct
102      * @return a pointer to a prepared {@code ffi_cif} struct
103      *
104      * @throws IllegalStateException if the call to {@code ffi_prep_cif} returns a non-zero status code
105      */
106     static MemorySegment prepCif(MemorySegment returnType, int numArgs, MemorySegment paramTypes, FFIABI abi,
107                                          Arena scope) throws IllegalStateException {
108         MemorySegment cif = scope.allocate(NativeConstants.SIZEOF_CIF);
109         checkStatus(ffi_prep_cif(cif.address(), abi.value(), numArgs, returnType.address(), paramTypes.address()));
110         return cif;
111     }
112 
113     /**
114      * Wrapper for {@code ffi_prep_cif_var}. The variadic version of prep_cif
115      *
116      * @param returnType a pointer to an @{code ffi_type} describing the return type
117      * @param numFixedArgs the number of fixed arguments
118      * @param numTotalArgs the number of total arguments
119      * @param paramTypes a pointer to an array of pointers, which each point to an {@code ffi_type} describing a
120      *                parameter type
121      * @param abi the abi to be used
122      * @param scope the scope into which to allocate the returned {@code ffi_cif} struct
123      * @return a pointer to a prepared {@code ffi_cif} struct
124      *
125      * @throws IllegalStateException if the call to {@code ffi_prep_cif} returns a non-zero status code
126      */
127     static MemorySegment prepCifVar(MemorySegment returnType, int numFixedArgs, int numTotalArgs, MemorySegment paramTypes, FFIABI abi,
128                                     Arena scope) throws IllegalStateException {
129         MemorySegment cif = scope.allocate(NativeConstants.SIZEOF_CIF);
130         checkStatus(ffi_prep_cif_var(cif.address(), abi.value(), numFixedArgs, numTotalArgs, returnType.address(), paramTypes.address()));
131         return cif;
132     }
133 
134     /**
135      * Create an upcallStub-style closure. This method wraps the {@code ffi_closure_alloc}
136      * and {@code ffi_prep_closure_loc} functions.
137      * <p>
138      * The closure will end up calling into {@link #doUpcall(long, long, MethodHandle)}
139      * <p>
140      * The target method handle should have the type {@code (MemorySegment, MemorySegment) -> void}. The first
141      * argument is a pointer to the buffer into which the native return value should be written. The second argument
142      * is a pointer to an array of pointers, which each point to a native argument value.
143      *
144      * @param cif a pointer to a {@code ffi_cif} struct
145      * @param target a method handle that points to the target function
146      * @param arena the scope to which to attach the created upcall stub
147      * @return the created upcall stub
148      *
149      * @throws IllegalStateException if the call to {@code ffi_prep_closure_loc} returns a non-zero status code
150      * @throws IllegalArgumentException if {@code target} does not have the right type
151      */
152     static MemorySegment createClosure(MemorySegment cif, MethodHandle target, Arena arena)
153             throws IllegalStateException, IllegalArgumentException {
154         if (target.type() != UPCALL_TARGET_TYPE) {
155             throw new IllegalArgumentException("Target handle has wrong type: " + target.type() + " != " + UPCALL_TARGET_TYPE);
156         }
157 
158         long[] ptrs = new long[3];
159         checkStatus(createClosure(cif.address(), target, ptrs));
160         long closurePtr = ptrs[0];
161         long execPtr = ptrs[1];
162         long globalTarget = ptrs[2];
163 
164         return MemorySegment.ofAddress(execPtr).reinterpret(arena, unused -> freeClosure(closurePtr, globalTarget));
165     }
166 
167     // the target function for a closure call
168     private static void doUpcall(long retPtr, long argPtrs, MethodHandle target) {
169         try {
170             target.invokeExact(MemorySegment.ofAddress(retPtr), MemorySegment.ofAddress(argPtrs));
171         } catch (Throwable t) {
172             SharedUtils.handleUncaughtException(t);
173         }
174     }
175 
176     /**
177      * Wrapper for {@code ffi_get_struct_offsets}
178      *
179      * @param structType a pointer to an {@code ffi_type} representing a struct
180      * @param offsetsOut a pointer to an array of {@code size_t}, with one element for each element of the struct.
181      *                   This is an 'out' parameter that will be filled in by this call
182      * @param abi the abi to be used
183      *
184      * @throws IllegalStateException if the call to {@code ffi_get_struct_offsets} returns a non-zero status code
185      */
186     static void getStructOffsets(MemorySegment structType, MemorySegment offsetsOut, FFIABI abi)
187             throws IllegalStateException {
188         checkStatus(ffi_get_struct_offsets(abi.value(), structType.address(), offsetsOut.address()));
189     }
190 
191     private static void checkStatus(int code) {
192         FFIStatus status = FFIStatus.of(code);
193         if (status != FFIStatus.FFI_OK) {
194             throw new IllegalStateException("libffi call failed with status: " + status);
195         }
196     }
197 
198     private static native void init();
199 
200     private static native long sizeofCif();
201 
202     private static native int createClosure(long cif, Object userData, long[] ptrs);
203     private static native void freeClosure(long closureAddress, long globalTarget);
204     private static native void doDowncall(long cif, long fn, long rvalue, long avalues, long capturedState, int capturedStateMask);
205 
206     private static native int ffi_prep_cif(long cif, int abi, int nargs, long rtype, long atypes);
207     private static native int ffi_prep_cif_var(long cif, int abi, int nfixedargs, int ntotalargs, long rtype, long atypes);
208     private static native int ffi_get_struct_offsets(int abi, long type, long offsets);
209 
210     private static native int ffi_default_abi();
211     private static native short ffi_type_struct();
212 
213     private static native long ffi_type_void();
214     private static native long ffi_type_uint8();
215     private static native long ffi_type_sint8();
216     private static native long ffi_type_uint16();
217     private static native long ffi_type_sint16();
218     private static native long ffi_type_uint32();
219     private static native long ffi_type_sint32();
220     private static native long ffi_type_uint64();
221     private static native long ffi_type_sint64();
222     private static native long ffi_type_float();
223     private static native long ffi_type_double();
224     private static native long ffi_type_pointer();
225 
226     // put these in a separate class to avoid an UnsatisfiedLinkError
227     // when LibFallback is initialized but the library is not present
228     private static final class NativeConstants {
229         private NativeConstants() {}
230 
231         static final int DEFAULT_ABI = ffi_default_abi();
232 
233         static final MemorySegment UINT8_TYPE = MemorySegment.ofAddress(ffi_type_uint8());
234         static final MemorySegment SINT8_TYPE = MemorySegment.ofAddress(ffi_type_sint8());
235         static final MemorySegment UINT16_TYPE = MemorySegment.ofAddress(ffi_type_uint16());
236         static final MemorySegment SINT16_TYPE = MemorySegment.ofAddress(ffi_type_sint16());
237         static final MemorySegment SINT32_TYPE = MemorySegment.ofAddress(ffi_type_sint32());
238         static final MemorySegment SINT64_TYPE = MemorySegment.ofAddress(ffi_type_sint64());
239         static final MemorySegment FLOAT_TYPE = MemorySegment.ofAddress(ffi_type_float());
240         static final MemorySegment DOUBLE_TYPE = MemorySegment.ofAddress(ffi_type_double());
241         static final MemorySegment POINTER_TYPE = MemorySegment.ofAddress(ffi_type_pointer());
242 
243         static final MemorySegment VOID_TYPE = MemorySegment.ofAddress(ffi_type_void());
244         static final short STRUCT_TAG = ffi_type_struct();
245         static final long SIZEOF_CIF = sizeofCif();
246     }
247 }