Pass large structs by value for Linux x86_64 and Aarch64. Aarch patch by Andreas Schwab. https://github.com/libffi/libffi/commit/482b37f00467325e3389bab322525099860dd9aa
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
diff --git a/src/aarch64/ffi.c b/src/aarch64/ffi.c
index 5c85fcd..8f8c140 100644
--- a/src/aarch64/ffi.c
+++ b/src/aarch64/ffi.c
@@ -248,13 +248,18 @@ is_vfp_type (const ffi_type *ty)
state.
The terse state variable names match the names used in the AARCH64
- PCS. */
+ PCS.
+
+ The struct area is allocated downwards from the top of the argument
+ area. It is used to hold copies of structures passed by value that are
+ bigger than 16 bytes. */
struct arg_state
{
unsigned ngrn; /* Next general-purpose register number. */
unsigned nsrn; /* Next vector register number. */
size_t nsaa; /* Next stack offset. */
+ size_t next_struct_area; /* Place to allocate big structs. */
#if defined (__APPLE__)
unsigned allocating_variadic;
@@ -263,11 +268,12 @@ struct arg_state
/* Initialize a procedure call argument marshalling state. */
static void
-arg_init (struct arg_state *state)
+arg_init (struct arg_state *state, size_t size)
{
state->ngrn = 0;
state->nsrn = 0;
state->nsaa = 0;
+ state->next_struct_area = size;
#if defined (__APPLE__)
state->allocating_variadic = 0;
#endif
@@ -296,6 +302,21 @@ allocate_to_stack (struct arg_state *state, void *stack,
return (char *)stack + nsaa;
}
+/* Allocate and copy a structure that is passed by value on the stack and
+ return a pointer to it. */
+static void *
+allocate_and_copy_struct_to_stack (struct arg_state *state, void *stack,
+ size_t alignment, size_t size, void *value)
+{
+ size_t dest = state->next_struct_area - size;
+
+ /* Round down to the natural alignment of the value. */
+ dest = ALIGN_DOWN (dest, alignment);
+ state->next_struct_area = dest;
+
+ return memcpy ((char *) stack + dest, value, size);
+}
+
static ffi_arg
extend_integer_type (void *source, int type)
{
@@ -624,13 +645,14 @@ ffi_call_int (ffi_cif *cif, void (*fn)(void), void *orig_rvalue,
frame = (void*)((uintptr_t)stack + (uintptr_t)stack_bytes);
rvalue = (rsize ? (void*)((uintptr_t)frame + 40) : orig_rvalue);
- arg_init (&state);
+ arg_init (&state, stack_bytes);
for (i = 0, nargs = cif->nargs; i < nargs; i++)
{
ffi_type *ty = cif->arg_types[i];
size_t s = ty->size;
void *a = avalue[i];
int h, t;
+ void *dest;
t = ty->type;
switch (t)
@@ -678,8 +700,6 @@ ffi_call_int (ffi_cif *cif, void (*fn)(void), void *orig_rvalue,
case FFI_TYPE_STRUCT:
case FFI_TYPE_COMPLEX:
{
- void *dest;
-
h = is_vfp_type (ty);
if (h)
{
@@ -712,9 +732,12 @@ ffi_call_int (ffi_cif *cif, void (*fn)(void), void *orig_rvalue,
else if (s > 16)
{
/* If the argument is a composite type that is larger than 16
- bytes, then the argument has been copied to memory, and
+ bytes, then the argument is copied to memory, and
the argument is replaced by a pointer to the copy. */
- a = &avalue[i];
+ dest = allocate_and_copy_struct_to_stack (&state, stack,
+ ty->alignment, s,
+ avalue[i]);
+ a = &dest;
t = FFI_TYPE_POINTER;
s = sizeof (void *);
goto do_pointer;
@@ -917,7 +940,7 @@ ffi_closure_SYSV_inner (ffi_cif *cif,
int i, h, nargs, flags, isvariadic = 0;
struct arg_state state;
- arg_init (&state);
+ arg_init (&state, cif->bytes);
flags = cif->flags;
if (flags & AARCH64_FLAG_VARARG)
diff --git a/src/x86/ffi64.c b/src/x86/ffi64.c
index 438b374..74a3003 100644
--- a/src/x86/ffi64.c
+++ b/src/x86/ffi64.c
@@ -1,5 +1,5 @@
/* -----------------------------------------------------------------------
- ffi64.c - Copyright (c) 2011, 2018 Anthony Green
+ ffi64.c - Copyright (c) 2011, 2018, 2022 Anthony Green
Copyright (c) 2013 The Written Word, Inc.
Copyright (c) 2008, 2010 Red Hat, Inc.
Copyright (c) 2002, 2007 Bo Thorsen <bo@suse.de>
@@ -681,6 +681,23 @@ ffi_call_efi64(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue);
void
ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
{
+ ffi_type **arg_types = cif->arg_types;
+ int i, nargs = cif->nargs;
+
+ /* If we have any large structure arguments, make a copy so we are passing
+ by value. */
+ for (i = 0; i < nargs; i++)
+ {
+ ffi_type *at = arg_types[i];
+ int size = at->size;
+ if (at->type == FFI_TYPE_STRUCT && size > 16)
+ {
+ char *argcopy = alloca (size);
+ memcpy (argcopy, avalue[i], size);
+ avalue[i] = argcopy;
+ }
+ }
+
#ifndef __ILP32__
if (cif->abi == FFI_EFI64 || cif->abi == FFI_GNUW64)
{