diff options
-rw-r--r-- | doc/libffi.texi | 30 | ||||
-rw-r--r-- | include/ffi.h.in | 3 | ||||
-rw-r--r-- | src/prep_cif.c | 27 | ||||
-rw-r--r-- | testsuite/libffi.call/offsets.c | 46 |
4 files changed, 99 insertions, 7 deletions
diff --git a/doc/libffi.texi b/doc/libffi.texi index 5c9fddd..94b7a9e 100644 --- a/doc/libffi.texi +++ b/doc/libffi.texi @@ -440,7 +440,8 @@ on the chosen ABI. @item The size and alignment of a new structure type will not be set by -@code{libffi} until it has been passed to @code{ffi_prep_cif}. +@code{libffi} until it has been passed to @code{ffi_prep_cif} or +@code{ffi_get_struct_offsets}. @item A structure type cannot be shared across ABIs. Instead each ABI needs @@ -448,8 +449,9 @@ its own copy of the structure type. @end itemize So, before examining these fields, it is safest to pass the -@code{ffi_type} object to @code{ffi_prep_cif} first. This function -will do all the needed setup. +@code{ffi_type} object to @code{ffi_prep_cif} or +@code{ffi_get_struct_offsets} first. This function will do all the +needed setup. @example ffi_type *desired_type; @@ -463,6 +465,28 @@ if (ffi_prep_cif (&cif, desired_abi, 0, desired_type, NULL) == FFI_OK) @} @end example +@code{libffi} also provides a way to get the offsets of the members of +a structure. + +@findex ffi_get_struct_offsets +@defun ffi_status ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type, size_t *offsets) +Compute the offset of each element of the given structure type. +@var{abi} is the ABI to use; this is needed because in some cases the +layout depends on the ABI. + +@var{sizes} is an out parameter. The caller is responsible for +providing enough space for all the results to be written -- one +element per element type in @var{struct_type}. If @var{sizes} is +@code{NULL}, then the type will be laid out but not otherwise +modified. This can be useful for accessing the type's size or layout, +as mentioned above. + +This function returns @code{FFI_OK} on success; @code{FFI_BAD_ABI} if +@var{abi} is invalid; or @code{FFI_BAD_TYPEDEF} if @var{struct_type} +is invalid in some way. Note that only @code{FFI_STRUCT} types are +valid here. +@end defun + @node Arrays Unions Enums @subsection Arrays, Unions, and Enumerations diff --git a/include/ffi.h.in b/include/ffi.h.in index cd3358f..9e65277 100644 --- a/include/ffi.h.in +++ b/include/ffi.h.in @@ -458,6 +458,9 @@ void ffi_call(ffi_cif *cif, void *rvalue, void **avalue); +ffi_status ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type, + size_t *offsets); + /* Useful for eliminating compiler warnings */ #define FFI_FN(f) ((void (*)(void))f) diff --git a/src/prep_cif.c b/src/prep_cif.c index 5881ceb..43f0487 100644 --- a/src/prep_cif.c +++ b/src/prep_cif.c @@ -34,7 +34,7 @@ /* Perform machine independent initialization of aggregate type specifications. */ -static ffi_status initialize_aggregate(ffi_type *arg) +static ffi_status initialize_aggregate(ffi_type *arg, size_t *offsets) { ffi_type **ptr; @@ -52,13 +52,15 @@ static ffi_status initialize_aggregate(ffi_type *arg) while ((*ptr) != NULL) { if (UNLIKELY(((*ptr)->size == 0) - && (initialize_aggregate((*ptr)) != FFI_OK))) + && (initialize_aggregate((*ptr), NULL) != FFI_OK))) return FFI_BAD_TYPEDEF; /* Perform a sanity check on the argument type */ FFI_ASSERT_VALID_TYPE(*ptr); arg->size = ALIGN(arg->size, (*ptr)->alignment); + if (offsets) + *offsets++ = arg->size; arg->size += (*ptr)->size; arg->alignment = (arg->alignment > (*ptr)->alignment) ? @@ -133,7 +135,8 @@ ffi_status FFI_HIDDEN ffi_prep_cif_core(ffi_cif *cif, ffi_abi abi, #endif /* Initialize the return type if necessary */ - if ((cif->rtype->size == 0) && (initialize_aggregate(cif->rtype) != FFI_OK)) + if ((cif->rtype->size == 0) + && (initialize_aggregate(cif->rtype, NULL) != FFI_OK)) return FFI_BAD_TYPEDEF; #ifndef FFI_TARGET_HAS_COMPLEX_TYPE @@ -164,7 +167,8 @@ ffi_status FFI_HIDDEN ffi_prep_cif_core(ffi_cif *cif, ffi_abi abi, { /* Initialize any uninitialized aggregate type definitions */ - if (((*ptr)->size == 0) && (initialize_aggregate((*ptr)) != FFI_OK)) + if (((*ptr)->size == 0) + && (initialize_aggregate((*ptr), NULL) != FFI_OK)) return FFI_BAD_TYPEDEF; #ifndef FFI_TARGET_HAS_COMPLEX_TYPE @@ -240,3 +244,18 @@ ffi_prep_closure (ffi_closure* closure, } #endif + +ffi_status +ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type, size_t *offsets) +{ + if (! (abi > FFI_FIRST_ABI && abi < FFI_LAST_ABI)) + return FFI_BAD_ABI; + if (struct_type->type != FFI_TYPE_STRUCT) + return FFI_BAD_TYPEDEF; + +#if HAVE_LONG_DOUBLE_VARIANT + ffi_prep_types (abi); +#endif + + return initialize_aggregate(struct_type, offsets); +} diff --git a/testsuite/libffi.call/offsets.c b/testsuite/libffi.call/offsets.c new file mode 100644 index 0000000..23d88b3 --- /dev/null +++ b/testsuite/libffi.call/offsets.c @@ -0,0 +1,46 @@ +/* Area: Struct layout + Purpose: Test ffi_get_struct_offsets + Limitations: none. + PR: none. + Originator: Tom Tromey. */ + +/* { dg-do run } */ +#include "ffitest.h" +#include <stddef.h> + +struct test_1 +{ + char c; + float f; + char c2; + int i; +}; + +int +main (void) +{ + ffi_type test_1_type; + ffi_type *test_1_elements[5]; + size_t test_1_offsets[4]; + + test_1_elements[0] = &ffi_type_schar; + test_1_elements[1] = &ffi_type_float; + test_1_elements[2] = &ffi_type_schar; + test_1_elements[3] = &ffi_type_sint; + test_1_elements[4] = NULL; + + test_1_type.size = 0; + test_1_type.alignment = 0; + test_1_type.type = FFI_TYPE_STRUCT; + test_1_type.elements = test_1_elements; + + CHECK (ffi_get_struct_offsets (FFI_DEFAULT_ABI, &test_1_type, test_1_offsets) + == FFI_OK); + CHECK (test_1_type.size == sizeof (struct test_1)); + CHECK (offsetof (struct test_1, c) == test_1_offsets[0]); + CHECK (offsetof (struct test_1, f) == test_1_offsets[1]); + CHECK (offsetof (struct test_1, c2) == test_1_offsets[2]); + CHECK (offsetof (struct test_1, i) == test_1_offsets[3]); + + return 0; +} |