diff --git a/build.xml b/build.xml index 300a157ea7..b4ae637016 100644 --- a/build.xml +++ b/build.xml @@ -529,6 +529,8 @@ com/sun/jna/linux-mips64el/libjnidispatch.so; processor=mips64el;osname=linux, com/sun/jna/linux-s390x/libjnidispatch.so; processor=S390x;osname=linux, +com/sun/jna/linux-loongarch64/libjnidispatch.so; +processor=loongarch64;osname=linux, com/sun/jna/freebsd-x86/libjnidispatch.so; processor=x86;osname=freebsd, @@ -638,6 +640,9 @@ osname=macosx;processor=aarch64 + @@ -851,6 +856,7 @@ osname=macosx;processor=aarch64 + diff --git a/native/Makefile b/native/Makefile index 0c45a85d52..6933041a85 100644 --- a/native/Makefile +++ b/native/Makefile @@ -111,6 +111,18 @@ endif STRIP=strip -x # end defaults + +OS_PLATFORM = $(shell uname -m) +ifeq ($(OS_PLATFORM), mips64) + LDFLAGS += -Wl,-z,noexecstack +endif + +OS_PLATFORM = $(shell uname -m) +ifeq ($(OS_PLATFORM), loongarch64) + LDFLAGS += -Wl,-z,noexecstack +endif + + # Android build (cross-compile) requires the android NDK. # Ensure the following tools are in your path and adjust NDK_PLATFORM as needed ifeq ($(OS),android) diff --git a/native/libffi/Makefile.am b/native/libffi/Makefile.am index 1b18198ad1..4631b23b0e 100644 --- a/native/libffi/Makefile.am +++ b/native/libffi/Makefile.am @@ -51,6 +51,7 @@ noinst_HEADERS = src/aarch64/ffitarget.h src/aarch64/internal.h \ src/avr32/ffitarget.h src/bfin/ffitarget.h \ src/cris/ffitarget.h src/csky/ffitarget.h src/frv/ffitarget.h \ src/ia64/ffitarget.h src/ia64/ia64_flags.h \ + src/loongarch/ffitarget.h \ src/m32r/ffitarget.h src/m68k/ffitarget.h \ src/m88k/ffitarget.h src/metag/ffitarget.h \ src/microblaze/ffitarget.h src/mips/ffitarget.h \ @@ -72,7 +73,8 @@ EXTRA_libffi_la_SOURCES = src/aarch64/ffi.c src/aarch64/sysv.S \ src/avr32/ffi.c src/avr32/sysv.S src/bfin/ffi.c \ src/bfin/sysv.S src/cris/ffi.c src/cris/sysv.S src/frv/ffi.c \ src/csky/ffi.c src/csky/sysv.S src/frv/eabi.S src/ia64/ffi.c \ - src/ia64/unix.S src/m32r/ffi.c src/m32r/sysv.S src/m68k/ffi.c \ + src/ia64/unix.S src/loongarch/ffi.c src/loongarch/sysv.S \ + src/m32r/ffi.c src/m32r/sysv.S src/m68k/ffi.c \ src/m68k/sysv.S src/m88k/ffi.c src/m88k/obsd.S \ src/metag/ffi.c src/metag/sysv.S src/microblaze/ffi.c \ src/microblaze/sysv.S src/mips/ffi.c src/mips/o32.S \ diff --git a/native/libffi/config.guess b/native/libffi/config.guess index e94095c5fb..a86b16ba21 100644 --- a/native/libffi/config.guess +++ b/native/libffi/config.guess @@ -985,6 +985,9 @@ EOF k1om:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; + loongarch32:Linux:*:* | loongarch64:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; m32r*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; diff --git a/native/libffi/config.sub b/native/libffi/config.sub index 14b5150c42..5604168fc7 100644 --- a/native/libffi/config.sub +++ b/native/libffi/config.sub @@ -1185,6 +1185,7 @@ case $cpu-$vendor in | k1om \ | le32 | le64 \ | lm32 \ + | loongarch32 | loongarch64 \ | m32c | m32r | m32rle \ | m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \ | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \ diff --git a/native/libffi/configure.host b/native/libffi/configure.host index 268267183a..d2a02c97f8 100644 --- a/native/libffi/configure.host +++ b/native/libffi/configure.host @@ -140,6 +140,11 @@ case "${host}" in SOURCES="ffi.c sysv.S" ;; + loongarch64-*-*) + TARGET=LOONGARCH; TARGETDIR=loongarch + SOURCES="ffi.c sysv.S" + ;; + m32r*-*-*) TARGET=M32R; TARGETDIR=m32r SOURCES="ffi.c sysv.S" diff --git a/native/libffi/src/loongarch/ffi.c b/native/libffi/src/loongarch/ffi.c new file mode 100644 index 0000000000..4d4a947007 --- /dev/null +++ b/native/libffi/src/loongarch/ffi.c @@ -0,0 +1,574 @@ +/* ----------------------------------------------------------------------- + ffi.c - Copyright (c) + + Based on RISC-V port + + LoongArch Foreign Function Interface + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + ``Software''), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + ----------------------------------------------------------------------- */ + +#include +#include + +#include +#include + +#if __loongarch_single_float +# define ABI_FLEN 32 +# define ABI_FLOAT float +#elif __loongarch_hard_float +# define ABI_FLEN 64 +# define ABI_FLOAT double +#else +# define ABI_FLEN 0 +#endif + +#define NARGREG 8 +#define STKALIGN 16 +#define MAXCOPYARG (2 * sizeof (double)) + +/* call_context registers + - 8 floating point parameter/result registers. + - 8 integer parameter/result registers. + - 2 registers used by the assembly code to in-place construct its own + stack frame + - frame register + - return register +*/ +typedef struct call_context +{ + ABI_FLOAT fa[8]; + size_t a[10]; +} call_context; + +typedef struct call_builder +{ + call_context *aregs; + int used_integer; + int used_float; + size_t *used_stack; +} call_builder; + +/* Integer (not pointer) less than ABI XLEN. */ +/* FFI_TYPE_INT does not appear to be used. */ +#if __SIZEOF_POINTER__ == 8 +# define IS_INT(type) ((type) >= FFI_TYPE_UINT8 && (type) <= FFI_TYPE_SINT64) +#else +# define IS_INT(type) ((type) >= FFI_TYPE_UINT8 && (type) <= FFI_TYPE_SINT32) +#endif + +#if ABI_FLEN +typedef struct float_struct_info +{ + char as_elements; + char type1; + char offset2; + char type2; +} float_struct_info; + +#if ABI_FLEN >= 64 +# define IS_FLOAT(type) ((type) >= FFI_TYPE_FLOAT && (type) <= FFI_TYPE_DOUBLE) +#else +# define IS_FLOAT(type) ((type) == FFI_TYPE_FLOAT) +#endif + +static ffi_type ** +flatten_struct (ffi_type *in, ffi_type **out, ffi_type **out_end) +{ + int i; + + if (out == out_end) + return out; + if (in->type != FFI_TYPE_STRUCT) + *(out++) = in; + else + for (i = 0; in->elements[i]; i++) + out = flatten_struct (in->elements[i], out, out_end); + return out; +} + +/* Structs with at most two fields after flattening, one of which is of + floating point type, are passed in multiple registers if sufficient + registers are available. */ +static float_struct_info +struct_passed_as_elements (call_builder *cb, ffi_type *top) +{ + float_struct_info ret = {0, 0, 0, 0}; + ffi_type *fields[3]; + int num_floats, num_ints; + int num_fields = flatten_struct (top, fields, fields + 3) - fields; + + if (num_fields == 1) + { + if (IS_FLOAT (fields[0]->type)) + { + ret.as_elements = 1; + ret.type1 = fields[0]->type; + } + } + else if (num_fields == 2) + { + num_floats = IS_FLOAT (fields[0]->type) + IS_FLOAT (fields[1]->type); + num_ints = IS_INT (fields[0]->type) + IS_INT (fields[1]->type); + if (num_floats == 0 || num_floats + num_ints != 2) + return ret; + if (cb->used_float + num_floats > NARGREG + || cb->used_integer + (2 - num_floats) > NARGREG) + return ret; + if (!IS_FLOAT (fields[0]->type) && !IS_FLOAT (fields[1]->type)) + return ret; + + ret.type1 = fields[0]->type; + ret.type2 = fields[1]->type; + ret.offset2 = FFI_ALIGN (fields[0]->size, fields[1]->alignment); + ret.as_elements = 1; + } + return ret; +} +#endif + +/* Allocates a single register, float register, or XLEN-sized stack slot to a + datum. */ +static void +marshal_atom (call_builder *cb, int type, void *data) +{ + size_t value = 0; + switch (type) + { + case FFI_TYPE_UINT8: + value = *(uint8_t *) data; + break; + case FFI_TYPE_SINT8: + value = *(int8_t *) data; + break; + case FFI_TYPE_UINT16: + value = *(uint16_t *) data; + break; + case FFI_TYPE_SINT16: + value = *(int16_t *) data; + break; + /* 32-bit quantities are always sign-extended in the ABI. */ + case FFI_TYPE_UINT32: + value = *(int32_t *) data; + break; + case FFI_TYPE_SINT32: + value = *(int32_t *) data; + break; +#if __SIZEOF_POINTER__ == 8 + case FFI_TYPE_UINT64: + value = *(uint64_t *) data; + break; + case FFI_TYPE_SINT64: + value = *(int64_t *) data; + break; +#endif + case FFI_TYPE_POINTER: + value = *(size_t *) data; + break; + +#if ABI_FLEN >= 32 + case FFI_TYPE_FLOAT: + asm("" : "=f"(cb->aregs->fa[cb->used_float++]) : "0"(*(float *) data)); + return; +#endif +#if ABI_FLEN >= 64 + case FFI_TYPE_DOUBLE: + asm("" : "=f"(cb->aregs->fa[cb->used_float++]) : "0"(*(double *) data)); + return; +#endif + default: + FFI_ASSERT (0); + break; + } + + if (cb->used_integer == NARGREG) + *cb->used_stack++ = value; + else + cb->aregs->a[cb->used_integer++] = value; +} + +static void +unmarshal_atom (call_builder *cb, int type, void *data) +{ + size_t value; + switch (type) + { +#if ABI_FLEN >= 32 + case FFI_TYPE_FLOAT: + asm("" : "=f"(*(float *) data) : "0"(cb->aregs->fa[cb->used_float++])); + return; +#endif +#if ABI_FLEN >= 64 + case FFI_TYPE_DOUBLE: + asm("" : "=f"(*(double *) data) : "0"(cb->aregs->fa[cb->used_float++])); + return; +#endif + } + + if (cb->used_integer == NARGREG) + value = *cb->used_stack++; + else + value = cb->aregs->a[cb->used_integer++]; + + switch (type) + { + case FFI_TYPE_UINT8: + case FFI_TYPE_SINT8: + case FFI_TYPE_UINT16: + case FFI_TYPE_SINT16: + case FFI_TYPE_UINT32: + case FFI_TYPE_SINT32: +#if __SIZEOF_POINTER__ == 8 + case FFI_TYPE_UINT64: + case FFI_TYPE_SINT64: +#endif + case FFI_TYPE_POINTER: + *(ffi_arg *)data = value; + break; + default: + FFI_ASSERT (0); + break; + } +} + +/* Adds an argument to a call, or a not by reference return value. */ +static void +marshal (call_builder *cb, ffi_type *type, int var, void *data) +{ + size_t realign[2]; + +#if ABI_FLEN + if (!var && type->type == FFI_TYPE_STRUCT) + { + float_struct_info fsi = struct_passed_as_elements (cb, type); + if (fsi.as_elements) + { + marshal_atom (cb, fsi.type1, data); + if (fsi.offset2) + marshal_atom (cb, fsi.type2, ((char *) data) + fsi.offset2); + return; + } + } + + if (!var && cb->used_float < NARGREG + && IS_FLOAT (type->type)) + { + marshal_atom (cb, type->type, data); + return; + } + + double promoted; + if (var && type->type == FFI_TYPE_FLOAT) + { + /* C standard requires promoting float -> double for variable arg. */ + promoted = *(float *) data; + type = &ffi_type_double; + data = &promoted; + } +#endif + + if (type->size > 2 * __SIZEOF_POINTER__) + /* Pass by reference. */ + marshal_atom (cb, FFI_TYPE_POINTER, &data); + else if (IS_INT (type->type) || type->type == FFI_TYPE_POINTER) + marshal_atom (cb, type->type, data); + else + { + /* Overlong integers, soft-float floats, and structs without special + float handling are treated identically from this point on. */ + + /* Variadics are aligned even in registers. */ + if (type->alignment > __SIZEOF_POINTER__) + { + if (var) + cb->used_integer = FFI_ALIGN (cb->used_integer, 2); + cb->used_stack + = (size_t *) FFI_ALIGN (cb->used_stack, 2 * __SIZEOF_POINTER__); + } + + memcpy (realign, data, type->size); + if (type->size > 0) + marshal_atom (cb, FFI_TYPE_POINTER, realign); + if (type->size > __SIZEOF_POINTER__) + marshal_atom (cb, FFI_TYPE_POINTER, realign + 1); + } +} + +/* For arguments passed by reference returns the pointer, otherwise the arg + is copied (up to MAXCOPYARG bytes). */ +static void * +unmarshal (call_builder *cb, ffi_type *type, int var, void *data) +{ + size_t realign[2]; + void *pointer; + +#if ABI_FLEN + if (!var && type->type == FFI_TYPE_STRUCT) + { + float_struct_info fsi = struct_passed_as_elements (cb, type); + if (fsi.as_elements) + { + unmarshal_atom (cb, fsi.type1, data); + if (fsi.offset2) + unmarshal_atom (cb, fsi.type2, ((char *) data) + fsi.offset2); + return data; + } + } + + if (!var && cb->used_float < NARGREG + && IS_FLOAT (type->type)) + { + unmarshal_atom (cb, type->type, data); + return data; + } + + if (var && type->type == FFI_TYPE_FLOAT) + { + int m = cb->used_integer; + void *promoted + = m < NARGREG ? cb->aregs->a + m : cb->used_stack + m - NARGREG + 1; + *(float *) promoted = *(double *) promoted; + } +#endif + + if (type->size > 2 * __SIZEOF_POINTER__) + { + /* Pass by reference. */ + unmarshal_atom (cb, FFI_TYPE_POINTER, (char *) &pointer); + return pointer; + } + else if (IS_INT (type->type) || type->type == FFI_TYPE_POINTER) + { + unmarshal_atom (cb, type->type, data); + return data; + } + else + { + /* Overlong integers, soft-float floats, and structs without special + float handling are treated identically from this point on. */ + + /* Variadics are aligned even in registers. */ + if (type->alignment > __SIZEOF_POINTER__) + { + if (var) + cb->used_integer = FFI_ALIGN (cb->used_integer, 2); + cb->used_stack + = (size_t *) FFI_ALIGN (cb->used_stack, 2 * __SIZEOF_POINTER__); + } + + if (type->size > 0) + unmarshal_atom (cb, FFI_TYPE_POINTER, realign); + if (type->size > __SIZEOF_POINTER__) + unmarshal_atom (cb, FFI_TYPE_POINTER, realign + 1); + memcpy (data, realign, type->size); + return data; + } +} + +static int +passed_by_ref (call_builder *cb, ffi_type *type, int var) +{ +#if ABI_FLEN + if (!var && type->type == FFI_TYPE_STRUCT) + { + float_struct_info fsi = struct_passed_as_elements (cb, type); + if (fsi.as_elements) + return 0; + } +#endif + + return type->size > 2 * __SIZEOF_POINTER__; +} + +/* Perform machine dependent cif processing. */ +ffi_status +ffi_prep_cif_machdep (ffi_cif *cif) +{ + cif->loongarch_nfixedargs = cif->nargs; + return FFI_OK; +} + +/* Perform machine dependent cif processing when we have a variadic + function. */ +ffi_status +ffi_prep_cif_machdep_var (ffi_cif *cif, unsigned int nfixedargs, + unsigned int ntotalargs) +{ + cif->loongarch_nfixedargs = nfixedargs; + return FFI_OK; +} + +/* Low level routine for calling functions. */ +extern void ffi_call_asm (void *stack, struct call_context *regs, + void (*fn) (void), void *closure) FFI_HIDDEN; + +static void +ffi_call_int (ffi_cif *cif, void (*fn) (void), void *rvalue, void **avalue, + void *closure) +{ + /* This is a conservative estimate, assuming a complex return value and + that all remaining arguments are long long / __int128 */ + size_t arg_bytes + = cif->nargs <= 3 + ? 0 + : FFI_ALIGN (2 * sizeof (size_t) * (cif->nargs - 3), STKALIGN); + size_t rval_bytes = 0; + if (rvalue == NULL && cif->rtype->size > 2 * __SIZEOF_POINTER__) + rval_bytes = FFI_ALIGN (cif->rtype->size, STKALIGN); + size_t alloc_size = arg_bytes + rval_bytes + sizeof (call_context); + + /* The assembly code will deallocate all stack data at lower addresses + than the argument region, so we need to allocate the frame and the + return value after the arguments in a single allocation. */ + size_t alloc_base; + /* Argument region must be 16-byte aligned in mabi=lp64. */ + if (_Alignof(max_align_t) >= STKALIGN) + /* Since sizeof long double is normally 16, the compiler will + guarantee alloca alignment to at least that much. */ + alloc_base = (size_t) alloca (alloc_size); + else + alloc_base = FFI_ALIGN (alloca (alloc_size + STKALIGN - 1), STKALIGN); + + if (rval_bytes) + rvalue = (void *) (alloc_base + arg_bytes); + + call_builder cb; + cb.used_float = cb.used_integer = 0; + cb.aregs = (call_context *) (alloc_base + arg_bytes + rval_bytes); + cb.used_stack = (void *) alloc_base; + + int return_by_ref = passed_by_ref (&cb, cif->rtype, 0); + if (return_by_ref) + cb.aregs->a[cb.used_integer++] = (size_t)rvalue; + + int i; + for (i = 0; i < cif->nargs; i++) + marshal (&cb, cif->arg_types[i], i >= cif->loongarch_nfixedargs, + avalue[i]); + + ffi_call_asm ((void *) alloc_base, cb.aregs, fn, closure); + + cb.used_float = cb.used_integer = 0; + if (!return_by_ref && rvalue) + unmarshal (&cb, cif->rtype, 0, rvalue); +} + +void +ffi_call (ffi_cif *cif, void (*fn) (void), void *rvalue, void **avalue) +{ + ffi_call_int (cif, fn, rvalue, avalue, NULL); +} + +void +ffi_call_go (ffi_cif *cif, void (*fn) (void), void *rvalue, void **avalue, + void *closure) +{ + ffi_call_int (cif, fn, rvalue, avalue, closure); +} + +extern void ffi_closure_asm (void) FFI_HIDDEN; + +ffi_status +ffi_prep_closure_loc (ffi_closure *closure, ffi_cif *cif, + void (*fun) (ffi_cif *, void *, void **, void *), + void *user_data, void *codeloc) +{ + uint32_t *tramp = (uint32_t *) &closure->tramp[0]; + uint64_t fn = (uint64_t) (uintptr_t) ffi_closure_asm; + + if (cif->abi <= FFI_FIRST_ABI || cif->abi >= FFI_LAST_ABI) + return FFI_BAD_ABI; + + /* We will call ffi_closure_inner with codeloc, not closure, but as long + as the memory is readable it should work. */ + tramp[0] = 0x1800000c; /* pcaddi $t0, 0 (i.e. $t0 <- tramp) */ + tramp[1] = 0x28c0418d; /* ld.d $t1, $t0, 16 */ + tramp[2] = 0x4c0001a0; /* jirl $zero, $t1, 0 */ + tramp[3] = 0x03400000; /* nop */ + tramp[4] = fn; + tramp[5] = fn >> 32; + + closure->cif = cif; + closure->fun = fun; + closure->user_data = user_data; + + __builtin___clear_cache (codeloc, codeloc + FFI_TRAMPOLINE_SIZE); + return FFI_OK; +} + +extern void ffi_go_closure_asm (void) FFI_HIDDEN; + +ffi_status +ffi_prep_go_closure (ffi_go_closure *closure, ffi_cif *cif, + void (*fun) (ffi_cif *, void *, void **, void *)) +{ + if (cif->abi <= FFI_FIRST_ABI || cif->abi >= FFI_LAST_ABI) + return FFI_BAD_ABI; + + closure->tramp = (void *) ffi_go_closure_asm; + closure->cif = cif; + closure->fun = fun; + return FFI_OK; +} + +/* Called by the assembly code with aregs pointing to saved argument registers + and stack pointing to the stacked arguments. Return values passed in + registers will be reloaded from aregs. */ +void FFI_HIDDEN +ffi_closure_inner (ffi_cif *cif, + void (*fun) (ffi_cif *, void *, void **, void *), + void *user_data, size_t *stack, call_context *aregs) +{ + void **avalue = alloca (cif->nargs * sizeof (void *)); + /* Storage for arguments which will be copied by unmarshal(). We could + theoretically avoid the copies in many cases and use at most 128 bytes + of memory, but allocating disjoint storage for each argument is + simpler. */ + char *astorage = alloca (cif->nargs * MAXCOPYARG); + void *rvalue; + call_builder cb; + int return_by_ref; + int i; + + cb.aregs = aregs; + cb.used_integer = cb.used_float = 0; + cb.used_stack = stack; + + return_by_ref = passed_by_ref (&cb, cif->rtype, 0); + if (return_by_ref) + unmarshal (&cb, &ffi_type_pointer, 0, &rvalue); + else + rvalue = alloca (cif->rtype->size); + + for (i = 0; i < cif->nargs; i++) + avalue[i] + = unmarshal (&cb, cif->arg_types[i], i >= cif->loongarch_nfixedargs, + astorage + i * MAXCOPYARG); + + fun (cif, rvalue, avalue, user_data); + + if (!return_by_ref && cif->rtype->type != FFI_TYPE_VOID) + { + cb.used_integer = cb.used_float = 0; + marshal (&cb, cif->rtype, 0, rvalue); + } +} diff --git a/native/libffi/src/loongarch/ffitarget.h b/native/libffi/src/loongarch/ffitarget.h new file mode 100644 index 0000000000..01f55091e6 --- /dev/null +++ b/native/libffi/src/loongarch/ffitarget.h @@ -0,0 +1,80 @@ +/* -----------------------------------------------------------------*-C-*- + ffitarget.h + + Target configuration macros for LoongArch. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + ``Software''), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + ----------------------------------------------------------------------- */ + +#ifndef LIBFFI_TARGET_H +#define LIBFFI_TARGET_H + +#ifndef LIBFFI_H +#error \ + "Please do not include ffitarget.h directly into your source. Use ffi.h instead." +#endif + +#ifndef __loongarch__ +#error \ + "libffi was configured for a LoongArch target but this does not appear to be a LoongArch compiler." +#endif + +#ifndef LIBFFI_ASM + +typedef unsigned long ffi_arg; +typedef signed long ffi_sarg; + +typedef enum ffi_abi +{ + FFI_FIRST_ABI = 0, + FFI_LP64_SOFT_FLOAT, + FFI_LP64_SINGLE_FLOAT, + FFI_LP64, + FFI_UNUSED_1, + FFI_UNUSED_2, + FFI_UNUSED_3, + FFI_LAST_ABI, + +#ifdef __loongarch64 +# ifdef __loongarch_soft_float + FFI_DEFAULT_ABI = FFI_LP64_SOFT_FLOAT +# elif __loongarch_single_float + FFI_DEFAULT_ABI = FFI_LP64_SINGLE_FLOAT +# else + FFI_DEFAULT_ABI = FFI_LP64 +# endif +#endif +} ffi_abi; + +#endif /* LIBFFI_ASM */ + +/* ---- Definitions for closures ----------------------------------------- */ + +#define FFI_CLOSURES 1 +#define FFI_GO_CLOSURES 1 +#define FFI_TRAMPOLINE_SIZE 24 +#define FFI_NATIVE_RAW_API 0 +#define FFI_EXTRA_CIF_FIELDS \ + unsigned loongarch_nfixedargs; \ + unsigned loongarch_unused; +#define FFI_TARGET_SPECIFIC_VARIADIC +#endif diff --git a/native/libffi/src/loongarch/sysv.S b/native/libffi/src/loongarch/sysv.S new file mode 100644 index 0000000000..59e741db1e --- /dev/null +++ b/native/libffi/src/loongarch/sysv.S @@ -0,0 +1,300 @@ +/* ----------------------------------------------------------------------- + ffi.c - Copyright (c) + + LoongArch Foreign Function Interface + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + ``Software''), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + ----------------------------------------------------------------------- */ + +#define LIBFFI_ASM +#include +#include + +/* Define aliases so that we can handle all ABIs uniformly. */ + +#if __SIZEOF_POINTER__ == 8 +# define PTRS 8 +# define LARG ld.d +# define SARG st.d +#else +# define PTRS 4 +# define LARG ld.w +# define SARG st.w +#endif + +#ifdef __loongarch_hard_float +# if defined __loongarch_single_float +# define FLTS 4 +# define FLD fld.w +# define FST fst.w +# error "need check" +# else +# define FLTS 8 +# define FLARG fld.d +# define FSARG fst.d +# endif +#else +# define FLTS 0 +# error "need check" +#endif + + + + + .text + .globl ffi_call_asm + .type ffi_call_asm, @function + .hidden ffi_call_asm +/* struct call_context + { + ABI_FLOAT fa[8]; + size_t a[10]; + } + + - 8 floating point parameter/result registers (fa[0] - fa[7]) + - 8 integer parameter/result registers (a[0] - a[7]) + - 2 registers used by the assembly code to in-place construct its own stack + frame. + - frame pointer (a[8]) + - return address (a[9]) + + void ffi_call_asm (size_t *stackargs, struct call_context *regargs, + void (*fn)(void), void *closure); */ + +#define FRAME_LEN (8 * FLTS + 10 * PTRS) + +ffi_call_asm: + .cfi_startproc + + /* We are NOT going to set up an ordinary stack frame. In order to pass + the stacked args to the called function, we adjust our stack pointer + to a0, which is in the _caller's_ alloca area. We establish our own + stack frame at the end of the call_context. + + Anything below the arguments will be freed at this point, although + we preserve the call_context so that it can be read back in the + caller. */ + + .cfi_def_cfa 5, FRAME_LEN # Interim CFA based on a1. + SARG $fp, $a1, FRAME_LEN - 2*PTRS + .cfi_offset 22, -2*PTRS + SARG $ra, $a1, FRAME_LEN - 1*PTRS + .cfi_offset 1, -1*PTRS + + addi.d $fp, $a1, FRAME_LEN + move $sp, $a0 + .cfi_def_cfa 22, 0 # Our frame is fully set up. + + # Load arguments. + move $t1, $a2 + move $t2, $a3 + +#if FLTS + FLARG $fa0, $fp, -FRAME_LEN+0*FLTS + FLARG $fa1, $fp, -FRAME_LEN+1*FLTS + FLARG $fa2, $fp, -FRAME_LEN+2*FLTS + FLARG $fa3, $fp, -FRAME_LEN+3*FLTS + FLARG $fa4, $fp, -FRAME_LEN+4*FLTS + FLARG $fa5, $fp, -FRAME_LEN+5*FLTS + FLARG $fa6, $fp, -FRAME_LEN+6*FLTS + FLARG $fa7, $fp, -FRAME_LEN+7*FLTS +#endif + + LARG $a0, $fp, -FRAME_LEN+8*FLTS+0*PTRS + LARG $a1, $fp, -FRAME_LEN+8*FLTS+1*PTRS + LARG $a2, $fp, -FRAME_LEN+8*FLTS+2*PTRS + LARG $a3, $fp, -FRAME_LEN+8*FLTS+3*PTRS + LARG $a4, $fp, -FRAME_LEN+8*FLTS+4*PTRS + LARG $a5, $fp, -FRAME_LEN+8*FLTS+5*PTRS + LARG $a6, $fp, -FRAME_LEN+8*FLTS+6*PTRS + LARG $a7, $fp, -FRAME_LEN+8*FLTS+7*PTRS + + /* Call */ + jirl $ra,$t1,0 + +#if FLTS + /* Save return values - only a0/a1 (fa0/fa1) are used. */ + FSARG $fa0, $fp, -FRAME_LEN+0*FLTS + FSARG $fa1, $fp, -FRAME_LEN+1*FLTS +#endif + + SARG $a0, $fp, -FRAME_LEN+8*FLTS+0*PTRS + SARG $a1, $fp, -FRAME_LEN+8*FLTS+1*PTRS + + /* Restore and return. */ + addi.d $sp, $fp, -FRAME_LEN + .cfi_def_cfa 3, FRAME_LEN + LARG $ra, $fp, -1*PTRS + .cfi_restore 1 + LARG $fp, $fp, -2*PTRS + .cfi_restore 22 + jirl $r0, $ra, 0 + .cfi_endproc + .size ffi_call_asm, .-ffi_call_asm + + +/* ffi_closure_asm. Expects address of the passed-in ffi_closure in t1. + void ffi_closure_inner (ffi_cif *cif, + void (*fun)(ffi_cif *, void *, void **, void *), + void *user_data, + size_t *stackargs, struct call_context *regargs) */ + + .globl ffi_closure_asm + .hidden ffi_closure_asm + .type ffi_closure_asm, @function + +ffi_closure_asm: + .cfi_startproc + addi.d $sp, $sp, -FRAME_LEN + .cfi_def_cfa_offset FRAME_LEN + + /* Make a frame. */ + SARG $fp, $sp, FRAME_LEN - 2*PTRS + .cfi_offset 22, -2*PTRS + SARG $ra, $sp, FRAME_LEN - 1*PTRS + .cfi_offset 1, -1*PTRS + addi.d $fp, $sp, FRAME_LEN + + /* Save arguments. */ +#if FLTS + FSARG $fa0, $sp, 0*FLTS + FSARG $fa1, $sp, 1*FLTS + FSARG $fa2, $sp, 2*FLTS + FSARG $fa3, $sp, 3*FLTS + FSARG $fa4, $sp, 4*FLTS + FSARG $fa5, $sp, 5*FLTS + FSARG $fa6, $sp, 6*FLTS + FSARG $fa7, $sp, 7*FLTS +#endif + + SARG $a0, $sp, 8*FLTS+0*PTRS + SARG $a1, $sp, 8*FLTS+1*PTRS + SARG $a2, $sp, 8*FLTS+2*PTRS + SARG $a3, $sp, 8*FLTS+3*PTRS + SARG $a4, $sp, 8*FLTS+4*PTRS + SARG $a5, $sp, 8*FLTS+5*PTRS + SARG $a6, $sp, 8*FLTS+6*PTRS + SARG $a7, $sp, 8*FLTS+7*PTRS + + /* Enter C */ + LARG $a0, $t0, FFI_TRAMPOLINE_SIZE+0*PTRS + LARG $a1, $t0, FFI_TRAMPOLINE_SIZE+1*PTRS + LARG $a2, $t0, FFI_TRAMPOLINE_SIZE+2*PTRS + addi.d $a3, $sp, FRAME_LEN + move $a4, $sp + + bl ffi_closure_inner + + /* Return values. */ +#if FLTS + FLARG $fa0, $sp, 0*FLTS + FLARG $fa1, $sp, 1*FLTS +#endif + + LARG $a0, $sp, 8*FLTS+0*PTRS + LARG $a1, $sp, 8*FLTS+1*PTRS + + /* Restore and return. */ + LARG $ra, $sp, FRAME_LEN-1*PTRS + .cfi_restore 1 + LARG $fp, $sp, FRAME_LEN-2*PTRS + .cfi_restore 22 + addi.d $sp, $sp, FRAME_LEN + .cfi_def_cfa_offset 0 + jirl $r0, $ra, 0 + .cfi_endproc + .size ffi_closure_asm, .-ffi_closure_asm + +/* ffi_go_closure_asm. Expects address of the passed-in ffi_go_closure in t2. + void ffi_closure_inner (ffi_cif *cif, + void (*fun)(ffi_cif *, void *, void **, void *), + void *user_data, + size_t *stackargs, struct call_context *regargs) */ + + .globl ffi_go_closure_asm + .hidden ffi_go_closure_asm + .type ffi_go_closure_asm, @function + +ffi_go_closure_asm: + .cfi_startproc + addi.d $sp, $sp, -FRAME_LEN + .cfi_def_cfa_offset FRAME_LEN + + /* Make a frame. */ + SARG $fp, $sp, FRAME_LEN - 2*PTRS + .cfi_offset 22, -2*PTRS + SARG $ra, $sp, FRAME_LEN - 1*PTRS + .cfi_offset 1, -1*PTRS + addi.d $fp, $sp, FRAME_LEN + + /* Save arguments. */ +#if FLTS + FSARG $fa0, $sp, 0*FLTS + FSARG $fa1, $sp, 1*FLTS + FSARG $fa2, $sp, 2*FLTS + FSARG $fa3, $sp, 3*FLTS + FSARG $fa4, $sp, 4*FLTS + FSARG $fa5, $sp, 5*FLTS + FSARG $fa6, $sp, 6*FLTS + FSARG $fa7, $sp, 7*FLTS +#endif + + SARG $a0, $sp, 8*FLTS+0*PTRS + SARG $a1, $sp, 8*FLTS+1*PTRS + SARG $a2, $sp, 8*FLTS+2*PTRS + SARG $a3, $sp, 8*FLTS+3*PTRS + SARG $a4, $sp, 8*FLTS+4*PTRS + SARG $a5, $sp, 8*FLTS+5*PTRS + SARG $a6, $sp, 8*FLTS+6*PTRS + SARG $a7, $sp, 8*FLTS+7*PTRS + + /* Enter C */ + LARG $a0, $t2, 1*PTRS + LARG $a1, $t2, 2*PTRS + move $a2, $t2 + addi.d $a3, $sp, FRAME_LEN + move $a4, $sp + + bl ffi_closure_inner + + /* Return values. */ +#if FLTS + FLARG $fa0, $sp, 0*FLTS + FLARG $fa1, $sp, 1*FLTS +#endif + + LARG $a0, $sp, 8*FLTS+0*PTRS + LARG $a1, $sp, 8*FLTS+1*PTRS + + /* Restore and return. */ + LARG $ra, $sp, FRAME_LEN-1*PTRS + .cfi_restore 1 + LARG $fp, $sp, FRAME_LEN-2*PTRS + .cfi_restore 22 + addi.d $sp, $sp, FRAME_LEN + .cfi_def_cfa_offset 0 + jirl $r0, $ra, 0 + .cfi_endproc + .size ffi_go_closure_asm, .-ffi_go_closure_asm + +#if defined __ELF__ && defined __linux__ + .section .note.GNU-stack,"",%progbits +#endif diff --git a/src/com/sun/jna/Native.java b/src/com/sun/jna/Native.java index b832d4052f..e3c3d8ebef 100644 --- a/src/com/sun/jna/Native.java +++ b/src/com/sun/jna/Native.java @@ -250,7 +250,7 @@ static boolean isCompatibleVersion(String expectedVersion, String nativeVersion) setProtected(true); } MAX_ALIGNMENT = Platform.isSPARC() || Platform.isWindows() - || (Platform.isLinux() && (Platform.isARM() || Platform.isPPC() || Platform.isMIPS())) + || (Platform.isLinux() && (Platform.isARM() || Platform.isPPC() || Platform.isMIPS() || Platform.isLoongArch())) || Platform.isAIX() || (Platform.isAndroid() && !Platform.isIntel()) ? 8 : LONG_SIZE; diff --git a/src/com/sun/jna/NativeLibrary.java b/src/com/sun/jna/NativeLibrary.java index d79cff619b..c901d3adde 100644 --- a/src/com/sun/jna/NativeLibrary.java +++ b/src/com/sun/jna/NativeLibrary.java @@ -984,6 +984,9 @@ else if (Platform.isARM()) { else if (Platform.ARCH.equals("mips64el")) { libc = "-gnuabi64"; } + else if (Platform.ARCH.equals("loongarch64")) { + libc = "-gnuabi64"; // TODO: not sure + } return cpu + kernel + libc; } diff --git a/src/com/sun/jna/Platform.java b/src/com/sun/jna/Platform.java index 7ee19e4a5b..ac1395e95d 100644 --- a/src/com/sun/jna/Platform.java +++ b/src/com/sun/jna/Platform.java @@ -195,6 +195,7 @@ public static final boolean is64Bit() { || "ppc64".equals(ARCH) || "ppc64le".equals(ARCH) || "sparcv9".equals(ARCH) || "mips64".equals(ARCH) || "mips64el".equals(ARCH) + || "loongarch64".equals(ARCH) || "amd64".equals(ARCH) || "aarch64".equals(ARCH)) { return true; @@ -234,6 +235,10 @@ public static final boolean isMIPS() { return false; } + public static final boolean isLoongArch() { + return ARCH.startsWith("loongarch"); + } + static String getCanonicalArchitecture(String arch, int platform) { arch = arch.toLowerCase().trim(); if ("powerpc".equals(arch)) {