/* * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #include "jdk_internal_foreign_abi_fallback_LibFallback.h" #include #include #include #include #include #include #ifdef _WIN64 #include #include #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"; #define CHECK_NULL(expr) \ if (expr == NULL) { \ return JNI_FALSE; \ } JNIEXPORT jboolean JNICALL Java_jdk_internal_foreign_abi_fallback_LibFallback_init(JNIEnv* env, jclass cls) { jint result = (*env)->GetJavaVM(env, &VM); if (result != 0) { return JNI_FALSE; } jclass LibFallback_class_local = (*env)->FindClass(env, "jdk/internal/foreign/abi/fallback/LibFallback"); CHECK_NULL(LibFallback_class_local) LibFallback_class = (*env)->NewGlobalRef(env, LibFallback_class_local); CHECK_NULL(LibFallback_class) LibFallback_doUpcall_ID = (*env)->GetStaticMethodID(env, LibFallback_class, "doUpcall", LibFallback_doUpcall_sig); CHECK_NULL(LibFallback_doUpcall_ID) return JNI_TRUE; } 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_1prep_1cif_1var(JNIEnv* env, jclass cls, jlong cif, jint abi, jint nfixedargs, jint ntotalargs, jlong rtype, jlong atypes) { return ffi_prep_cif_var(jlong_to_ptr(cif), (ffi_abi) abi, (unsigned int) nfixedargs, (unsigned int) ntotalargs, 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, jarray capture_state_heap_base, jlong captured_state_offset, jint captured_state_mask, jarray heapBases, jint numArgs) { void** carrays; int capture_state_hb_offset = numArgs; int32_t* captured_state_addr = jlong_to_ptr(captured_state_offset); if (heapBases != NULL) { void** aptrs = jlong_to_ptr(avalues); carrays = malloc(sizeof(void*) * (numArgs + 1)); for (int i = 0; i < numArgs; i++) { jarray hb = (jarray) (*env)->GetObjectArrayElement(env, heapBases, i); if (hb != NULL) { // *(aptrs[i]) is the offset into the segment (from MS::address) // we add the base address of the array to it here jboolean isCopy; jbyte* arrayPtr = (*env)->GetPrimitiveArrayCritical(env, hb, &isCopy); carrays[i] = arrayPtr; int offset = *((int*)aptrs[i]); *((void**)aptrs[i]) = arrayPtr + offset; } } if (capture_state_heap_base != NULL) { jboolean isCopy; jbyte* arrayPtr = (*env)->GetPrimitiveArrayCritical(env, capture_state_heap_base, &isCopy); carrays[capture_state_hb_offset] = arrayPtr; captured_state_addr = (int32_t*) (arrayPtr + captured_state_offset); } } ffi_call(jlong_to_ptr(cif), jlong_to_ptr(fn), jlong_to_ptr(rvalue), jlong_to_ptr(avalues)); if (captured_state_mask != 0) { do_capture_state(captured_state_addr, captured_state_mask); } if (heapBases != NULL) { for (int i = 0; i < numArgs; i++) { jarray hb = (jarray) (*env)->GetObjectArrayElement(env, heapBases, i); if (hb != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, hb, carrays[i], JNI_COMMIT); } } if (capture_state_heap_base != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, capture_state_heap_base, carrays[capture_state_hb_offset], JNI_COMMIT); } free(carrays); } } static void do_upcall(ffi_cif* cif, void* ret, void** args, void* user_data) { // attach thread JNIEnv* env; (*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); } JNIEXPORT jint JNICALL Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1sizeof_1short(JNIEnv* env, jclass cls) { return sizeof(short); } JNIEXPORT jint JNICALL Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1sizeof_1int(JNIEnv* env, jclass cls) { return sizeof(int); } JNIEXPORT jint JNICALL Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1sizeof_1long(JNIEnv* env, jclass cls) { return sizeof(long); } JNIEXPORT jint JNICALL Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1sizeof_1wchar(JNIEnv* env, jclass cls) { return sizeof(wchar_t); } JNIEXPORT jint JNICALL Java_jdk_internal_foreign_abi_fallback_LibFallback_alignof_1long_1long(JNIEnv* env, jclass cls) { return alignof(long long); } JNIEXPORT jint JNICALL Java_jdk_internal_foreign_abi_fallback_LibFallback_alignof_1double(JNIEnv* env, jclass cls) { return alignof(double); }