diff options
author | Ludovic Courtès <ludo@gnu.org> | 2010-05-30 22:54:39 +0200 |
---|---|---|
committer | Ludovic Courtès <ludo@gnu.org> | 2010-05-30 22:54:39 +0200 |
commit | 86425e26539fc1ffc145f74730c846cb724179a6 (patch) | |
tree | ff3b1d105618ada9b4a30d10959fb768ff0492ab | |
parent | 1880c97df10770eba8dbf1f068903e713dbf3f7e (diff) | |
download | guile-86425e26539fc1ffc145f74730c846cb724179a6.tar.gz |
Fix argument and return value alignment in `scm_i_foreign_call'.
* libguile/foreign.c (scm_i_foreign_call): Fix the computation of
ARG_SIZE wrt. alignment. Arrange so that the address ARGS[i] is
aligned, rather than checking whether OFF is aligned. Have the RVALUE
be at least word-aligned, which fixes calls to `char'-returning
functions on `armv5tel-*-linux-gnueabi'.
-rw-r--r-- | libguile/foreign.c | 37 |
1 files changed, 21 insertions, 16 deletions
diff --git a/libguile/foreign.c b/libguile/foreign.c index 8b3c9be7e..aae4c670e 100644 --- a/libguile/foreign.c +++ b/libguile/foreign.c @@ -24,6 +24,8 @@ #include <alignof.h> #include <string.h> +#include <assert.h> + #include "libguile/_scm.h" #include "libguile/bytevectors.h" #include "libguile/instructions.h" @@ -985,32 +987,35 @@ scm_i_foreign_call (SCM foreign, const SCM *argv) /* Argument pointers. */ args = alloca (sizeof (void *) * cif->nargs); - /* Compute the amount of memory needed to store all the argument values. - Note: as of libffi 3.0.9 `cif->bytes' is undocumented and is zero, so it - can't be used for that purpose. */ - for (i = 0, arg_size = 0; - i < cif->nargs; - i++, arg_size) - arg_size += ROUND_UP (cif->arg_types[i]->size, - cif->arg_types[i]->alignment); + /* Compute the worst-case amount of memory needed to store all the argument + values. Note: as of libffi 3.0.9 `cif->bytes' is undocumented and is zero, + so it can't be used for that purpose. */ + for (i = 0, arg_size = 0; i < cif->nargs; i++) + arg_size += cif->arg_types[i]->size + cif->arg_types[i]->alignment - 1; /* Space for argument values, followed by return value. */ - data = alloca (arg_size - + ROUND_UP (cif->rtype->size, cif->rtype->alignment)); + data = alloca (arg_size + cif->rtype->size + + max (sizeof (void *), cif->rtype->alignment)); /* Unpack ARGV to native values, setting ARGV pointers. */ for (i = 0, off = 0; i < cif->nargs; - off += cif->arg_types[i]->size, i++) + off = (scm_t_uint8 *) args[i] - data + cif->arg_types[i]->size, + i++) { - off = ROUND_UP (off, cif->arg_types[i]->alignment); - args[i] = data + off; + /* Suitably align the storage area for argument I. */ + args[i] = (void *) ROUND_UP ((scm_t_uintptr) data + off, + cif->arg_types[i]->alignment); + assert ((scm_t_uintptr) args[i] % cif->arg_types[i]->alignment == 0); unpack (cif->arg_types[i], args[i], argv[i]); } - /* Prepare space for the return value. */ - off = ROUND_UP (off, cif->rtype->alignment); - rvalue = data + off; + /* Prepare space for the return value. On some platforms, such as + `armv5tel-*-linux-gnueabi', the return value has to be at least + word-aligned, even if its type doesn't have any alignment requirement as is + the case with `char'. */ + rvalue = (void *) ROUND_UP ((scm_t_uintptr) data + off, + max (sizeof (void *), cif->rtype->alignment)); /* off we go! */ ffi_call (cif, func, rvalue, args); |