summaryrefslogtreecommitdiffstats
path: root/libsyscalls_get_struct_description.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsyscalls_get_struct_description.c')
-rw-r--r--libsyscalls_get_struct_description.c656
1 files changed, 656 insertions, 0 deletions
diff --git a/libsyscalls_get_struct_description.c b/libsyscalls_get_struct_description.c
new file mode 100644
index 0000000..4d9ce68
--- /dev/null
+++ b/libsyscalls_get_struct_description.c
@@ -0,0 +1,656 @@
+/* See LICENSE file for copyright and license details. */
+#define REQUIRE_FLEXABLE_OR_NFIELDS
+#include "common.h"
+
+
+#if defined(__GNUC__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wunused-macros"
+#endif
+
+#define IN .input_field = 1
+#define OUT .output_field = 1
+#define ERR .error_field = 1
+#define SYM .symbolic_field = 1
+#define PARTIAL .partial_init_ok = 1
+#define INPTR .input_pointer = 1
+#define OUTPTR .output_pointer = 1
+
+#define BITFIELD(N) .bitlength = (N)
+#define FIELD(NAME, TYPE, ...)\
+ {.name = NAME,\
+ .type = LIBSYSCALLS_TYPE_##TYPE\
+ __VA_OPT__(, __VA_ARGS__)}
+#define STRUCT(PRINTER, ...) STRUCT_OR_UNION_(0, PRINTER __VA_OPT__(, __VA_ARGS__))
+#define UNION(PRINTER, ...) STRUCT_OR_UNION_(1, PRINTER __VA_OPT__(, __VA_ARGS__))
+#define STRUCT_OR_UNION_(IS_UNION, PRINTER, ...)\
+ {.is_union = (IS_UNION),\
+ .symbol_printer = (PRINTER),\
+ .num_fields = COUNT_ARGS(__VA_ARGS__)\
+ __VA_OPT__(, .fields = {__VA_ARGS__})}
+
+#if defined(__GNUC__)
+# pragma GCC diagnostic pop
+#endif
+
+
+struct padding {
+ unsigned short int size;
+ unsigned short int after;
+};
+
+
+static enum libsyscalls_error
+get_struct_description(enum libsyscalls_os os, enum libsyscalls_arch arch, enum libsyscalls_datatype datatype, const void *data,
+ size_t data_size, struct libsyscalls_structure_description **description_out, size_t *extra_sizep);
+
+
+static struct libsyscalls_structure_description *
+copy_struct(const struct libsyscalls_structure_description *prototype)
+{
+ struct libsyscalls_structure_description *ret;
+ size_t size = offsetof(struct libsyscalls_structure_description, fields);
+ size += prototype->num_fields * sizeof(*prototype->fields);
+ ret = malloc(size);
+ if (!ret)
+ return NULL;
+ memcpy(ret, prototype, size);
+ return ret;
+}
+
+
+static struct libsyscalls_datatype_description *
+create_pad_description(enum libsyscalls_os os, enum libsyscalls_arch arch, unsigned short int size)
+{
+ struct libsyscalls_datatype_description *ret;
+ ret = calloc(1, sizeof(*ret));
+ if (!ret)
+ return NULL;
+ if (libsyscalls_get_datatype_description(os, arch, LIBSYSCALLS_TYPE_CHAR, ret))
+ abort();
+ ret->width_in_bits = size;
+ return ret;
+}
+
+
+static enum libsyscalls_error
+shift_fields(enum libsyscalls_os os, enum libsyscalls_arch arch, struct libsyscalls_structure_description *description,
+ const struct padding *paddings, size_t npaddings)
+{
+ signed short int shift, map[description->num_fields], abs, rel;
+ unsigned short int i, j, k;
+ int is_struct;
+
+ /* Adjust field references */
+ for (i = 0; i < description->num_fields; i++)
+ map[i] = (signed short int)i;
+ shift = 0;
+ i = j = 0;
+ while (i < (unsigned short int)npaddings && j < description->num_fields) {
+ if (j > paddings[i].after)
+ shift += 1;
+ map[j] += shift;
+ }
+ for (; j < description->num_fields; j++)
+ map[j] += shift;
+ for (i = 0; i < description->num_fields; i++) {
+ is_struct = (description->fields[i].type & ~LIBSYSCALLS_TYPEBITSMASK) >= LIBSYSCALLS_TYPEOFFSET_STRUCTS_AND_UNIONS;
+
+ if (is_struct) {
+ abs = description->fields[i].type_description.structure->absolute_position_of_size;
+ rel = description->fields[i].type_description.structure->relative_position_of_size;
+ } else {
+ abs = description->fields[i].type_description.nonstructure->absolute_position_of_array_size;
+ rel = description->fields[i].type_description.nonstructure->relative_position_of_array_size;
+ }
+
+ if (abs >= 0) {
+ if ((unsigned short int)abs > description->num_fields)
+ abort();
+ abs = map[abs];
+ }
+ if (rel != 0) {
+ if (rel + (signed short int)i < 0)
+ abort();
+ if ((unsigned short int)(rel + (signed short int)i) > description->num_fields)
+ abort();
+ rel = map[rel + (signed short int)i];
+ rel -= (signed short int)i;
+ }
+
+#if defined(__GNUC__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wconversion"
+#endif
+ if (is_struct) {
+ description->fields[i].type_description.structure->absolute_position_of_size = abs;
+ description->fields[i].type_description.structure->relative_position_of_size = rel;
+ } else {
+ description->fields[i].type_description.nonstructure->absolute_position_of_array_size = abs;
+ description->fields[i].type_description.nonstructure->relative_position_of_array_size = rel;
+ }
+#if defined(__GNUC__)
+# pragma GCC diagnostic pop
+#endif
+ }
+
+ /* Insert padding fields */
+ i = description->num_fields;
+ j = i + (unsigned short int)shift;
+ k = (unsigned short int)(npaddings - 1);
+ while (i) {
+ memcpy(&description->fields[--j], &description->fields[--i], sizeof(*description->fields));
+ while (i <= paddings[k].after) {
+ j--;
+ description->size += paddings[k].size;
+ memset(&description->fields[j], 0, sizeof(*description->fields));
+ description->fields[j].name = NULL;
+ description->fields[j].type = LIBSYSCALLS_TYPE_VOID;
+ description->fields[j].unused = 1;
+ description->fields[j].type_description.nonstructure = create_pad_description(os, arch, paddings[k].size);
+ if (!description->fields[j].type_description.structure)
+ return LIBSYSCALLS_E_NOMEM;
+ if (!k--)
+ return LIBSYSCALLS_E_OK;
+ }
+ }
+
+ return LIBSYSCALLS_E_OK;
+}
+
+
+static void
+free_subdescriptions(struct libsyscalls_structure_description *description)
+{
+ unsigned short int i;
+ for (i = 0; i < description->num_fields; i++) {
+ if ((description->fields[i].type & ~LIBSYSCALLS_TYPEBITSMASK) >= LIBSYSCALLS_TYPEOFFSET_STRUCTS_AND_UNIONS)
+ free_subdescriptions(description->fields[i].type_description.structure);
+ free(description->fields[i].type_description.structure);
+ }
+}
+
+
+static void
+move_in_subdescriptions(char *buffer, size_t *offsetp, struct libsyscalls_structure_description *description,
+ int include_nonstructures, int include_structures)
+{
+ unsigned short int i;
+ size_t size;
+ for (i = 0; i < description->num_fields; i++) {
+ if ((description->fields[i].type & ~LIBSYSCALLS_TYPEBITSMASK) >= LIBSYSCALLS_TYPEOFFSET_STRUCTS_AND_UNIONS) {
+ move_in_subdescriptions(buffer, offsetp, description->fields[i].type_description.structure,
+ include_nonstructures, include_structures);
+ if (!include_structures)
+ continue;
+ size = (size_t)description->fields[i].type_description.structure->num_fields;
+ size *= sizeof(*description->fields[i].type_description.structure->fields);
+ size += offsetof(struct libsyscalls_structure_description, fields);
+ } else {
+ if (!include_nonstructures)
+ continue;
+ size = sizeof(struct libsyscalls_datatype_description);
+ }
+ memcpy(&buffer[*offsetp], description->fields[i].type_description.structure, size);
+ free(description->fields[i].type_description.structure);
+ description->fields[i].type_description.structure = (void *)&buffer[*offsetp];
+ *offsetp += size;
+ }
+}
+
+
+static unsigned long long int
+read_field(const void *data, size_t data_size, const size_t *field_offsets,
+ const struct libsyscalls_structure_description *description, unsigned short int i, int *undetermined_out)
+{
+ size_t offset, width, bits = 0;
+ enum libsyscalls_datatype type = description->fields[i].type;
+ const struct libsyscalls_datatype_description *type_description = description->fields[i].type_description.nonstructure;
+ unsigned splits, expected_splits, end;
+ unsigned long long int result = 0, subresult;
+ signed long long int sresult;
+ int is_negative;
+
+ if ((type & LIBSYSCALLS_TYPEBITSMASK) != LIBSYSCALLS_TYPEBITS_SCALAR)
+ abort();
+ type ^= LIBSYSCALLS_TYPEBITS_SCALAR;
+ if (type >= LIBSYSCALLS_TYPEOFFSET_SPLIT_PRIMITIVES && type < LIBSYSCALLS_TYPEOFFSET_COMPOSITE_PRIMITIVES)
+ splits = LIBSYSCALLS_GET_SECTION_FRACTION(type_description->section);
+ else if (type >= LIBSYSCALLS_TYPEOFFSET_UNANNOTATED_NUMERICALS && type < LIBSYSCALLS_TYPEOFFSET_FIXED_ARRAYS)
+ splits = 1;
+ else
+ abort();
+
+ expected_splits = splits;
+ end = i + splits;
+ goto start;
+
+ for (; i < end; i++) {
+ if ((type & LIBSYSCALLS_TYPEBITSMASK) != LIBSYSCALLS_TYPEBITS_SCALAR)
+ abort();
+ type ^= LIBSYSCALLS_TYPEBITS_SCALAR;
+ if (type >= LIBSYSCALLS_TYPEOFFSET_SPLIT_PRIMITIVES && type < LIBSYSCALLS_TYPEOFFSET_COMPOSITE_PRIMITIVES)
+ splits = LIBSYSCALLS_GET_SECTION_FRACTION(type_description->section);
+ else if (type >= LIBSYSCALLS_TYPEOFFSET_UNANNOTATED_NUMERICALS && type < LIBSYSCALLS_TYPEOFFSET_FIXED_ARRAYS)
+ splits = 1;
+ else
+ abort();
+
+ if (splits != expected_splits)
+ abort();
+
+ start:
+ offset = field_offsets[i];
+ width = (size_t)type_description->width_in_bits;
+ bits += width;
+
+ if (offset > data_size || width > data_size - offset)
+ goto undetermined;
+
+ if (libsyscalls_to_tracer_endian(&((const char *)data)[offset / CHAR_BIT], offset % CHAR_BIT,
+ type_description, &subresult))
+ abort();
+
+ if (libsyscalls_unsection_value(subresult, width, type_description->section, &subresult))
+ abort();
+
+ result |= subresult;
+ }
+
+ if (type_description->is_signed) {
+ if (libsyscalls_parse_signed_integer(result, type_description->sign_representation, bits, &result, &is_negative))
+ abort();
+ if (is_negative) {
+#if LLONG_MIN == -LLONG_MAX
+ if (result >> (bits - 1))
+ goto undetermined;
+#endif
+ sresult = (signed long long int)result;
+ sresult = -sresult;
+ }
+ result = *(unsigned long long int *)&sresult;
+ }
+
+ return result;
+
+undetermined:
+ *undetermined_out = 1;
+ return 0;
+}
+
+
+static enum libsyscalls_error
+pad_structure_into_alignment(struct padding **paddingsp, size_t *npaddingsp, size_t *paddings_sizep,
+ const struct libsyscalls_structure_description *description)
+{
+ unsigned short int alignment_mask, misalignment;
+ void *new;
+
+ alignment_mask = (unsigned short int)(description->alignment - 1U);
+ misalignment = (unsigned short int)(description->size & alignment_mask);
+ if (misalignment) {
+ if (*npaddingsp == *paddings_sizep) {
+ *paddings_sizep += 16;
+ new = realloc(*paddingsp, *paddings_sizep * sizeof(**paddingsp));
+ if (!new)
+ return LIBSYSCALLS_E_NOMEM;
+ *paddingsp = new;
+ }
+ (*paddingsp)[*npaddingsp].size = (unsigned short int)(description->alignment - misalignment);
+ (*paddingsp)[*npaddingsp].after = description->num_fields;
+ *npaddingsp += 1;
+ }
+
+ return LIBSYSCALLS_E_OK;
+}
+
+
+static enum libsyscalls_error
+pad_field_into_alignment(struct padding **paddingsp, size_t *npaddingsp, size_t *paddings_sizep,
+ const struct libsyscalls_structure_description *description,
+ unsigned short int i, unsigned subalign, size_t *field_offsetp)
+{
+ unsigned short int misalignment, alignment_mask, padding;
+ void *new;
+
+ alignment_mask = (unsigned short int)(subalign - 1U);
+ misalignment = (unsigned short int)(description->size & alignment_mask);
+
+ if (misalignment) {
+ if (i == 0)
+ abort();
+ padding = (unsigned short int)(subalign - (unsigned)misalignment);
+ if (*npaddingsp == *paddings_sizep) {
+ *paddings_sizep += 16;
+ new = realloc(*paddingsp, *paddings_sizep * sizeof(**paddingsp));
+ if (!new)
+ return LIBSYSCALLS_E_NOMEM;
+ *paddingsp = new;
+ }
+ *field_offsetp += (size_t)padding;
+ (*paddingsp)[*npaddingsp].size = (unsigned short int)padding;
+ (*paddingsp)[*npaddingsp].after = (unsigned short int)(i - 1U);
+ *npaddingsp += 1;
+ }
+
+ return LIBSYSCALLS_E_OK;
+}
+
+
+static enum libsyscalls_error
+apply_padding(enum libsyscalls_os os, enum libsyscalls_arch arch, struct padding *paddings,
+ size_t npaddings, struct libsyscalls_structure_description **descriptionp)
+{
+ struct libsyscalls_structure_description *description = *descriptionp;
+ enum libsyscalls_error r;
+ size_t size;
+
+ if (npaddings && description->alignment > 1) {
+ size = offsetof(struct libsyscalls_structure_description, fields);
+ size += (size_t)(description->num_fields + npaddings) * sizeof(*description->fields);
+ description = realloc(description, size);
+ if (!description)
+ return LIBSYSCALLS_E_NOMEM;
+ *descriptionp = description;
+
+ r = shift_fields(os, arch, description, paddings, npaddings);
+ if (r)
+ return r;
+ }
+
+ return LIBSYSCALLS_E_OK;
+}
+
+
+static enum libsyscalls_error
+adjust_for_nonstruct(enum libsyscalls_os os, enum libsyscalls_arch arch,
+ const struct libsyscalls_datatype_description *int_description,
+ struct libsyscalls_structure_description *description, unsigned short int i,
+ unsigned *subsizep, unsigned *subalignp, size_t *extra_sizep)
+{
+ enum libsyscalls_error r;
+
+ *subsizep = int_description->width_in_bits;
+ /* TODO deal with array_size, keep in mind that it can be 0 because we have (type[]) or (type *); be aware of padding */
+ r = libsyscalls_get_integer_alignment(os, arch, *subsizep, subalignp);
+ if (r)
+ return r;
+
+ description->fields[i].type_description.nonstructure = malloc(sizeof(*int_description));
+ if (!description->fields[i].type_description.nonstructure)
+ return LIBSYSCALLS_E_NOMEM;
+ memcpy(description->fields[i].type_description.nonstructure, int_description, sizeof(*int_description));
+ *extra_sizep += sizeof(*int_description);
+
+ return LIBSYSCALLS_E_OK;
+}
+
+
+static enum libsyscalls_error
+adjust_for_struct(enum libsyscalls_os os, enum libsyscalls_arch arch, const void *data, size_t data_size,
+ struct libsyscalls_structure_description *description, unsigned short int i, size_t *field_offsets,
+ unsigned *subsizep, unsigned *subalignp, size_t *extra_sizep, int *undetermined_sizep)
+{
+ struct libsyscalls_structure_description *struct_description;
+ size_t misalignment, subextra_size;
+ unsigned long long int uvalue;
+ unsigned short int field;
+ enum libsyscalls_error r;
+
+ r = get_struct_description(os, arch, description->fields[i].type, &((const char *)data)[field_offsets[i]],
+ data_size < field_offsets[i] ? 0 : data_size - field_offsets[i],
+ &struct_description, extra_sizep);
+ if (r)
+ return r;
+
+ *subsizep = struct_description->size * struct_description->array_size;
+ *subalignp = struct_description->alignment;
+ if (struct_description->array_size <= 1) {
+ if (struct_description->relative_position_of_size != 0) {
+ if (struct_description->relative_position_of_size > 1)
+ abort();
+ if ((unsigned short int)-struct_description->relative_position_of_size > i)
+ abort();
+ field = (unsigned short int)((ssize_t)i + (ssize_t)struct_description->relative_position_of_size);
+ goto set_subsize;
+ } else if (struct_description->absolute_position_of_size != -1) {
+ field = (unsigned short int)struct_description->absolute_position_of_size;
+ if (field > i)
+ abort();
+ set_subsize:
+ uvalue = read_field(data, data_size, field_offsets, description, i, undetermined_sizep);
+ /* TODO set `*subsizep` to `uvalue` (may overflow) */
+ }
+ if (!struct_description->array_size)
+ *subsizep *= struct_description->size;
+ }
+ if (!*subsizep)
+ *undetermined_sizep = 1;
+
+ description->fields[i].type_description.structure = struct_description;
+ subextra_size = offsetof(struct libsyscalls_structure_description, fields);
+ subextra_size += (size_t)struct_description->num_fields * sizeof(*struct_description->fields);
+ misalignment = subextra_size % alignof(struct libsyscalls_structure_description);
+ misalignment = alignof(struct libsyscalls_structure_description) - misalignment;
+ misalignment %= alignof(struct libsyscalls_structure_description);
+ subextra_size += misalignment;
+ *extra_sizep += subextra_size;
+
+ return LIBSYSCALLS_E_OK;
+}
+
+
+static enum libsyscalls_error
+adjust_for_fields(enum libsyscalls_os os, enum libsyscalls_arch arch, const void *data, size_t data_size,
+ struct padding **paddingsp, size_t *npaddingsp, size_t *paddings_sizep,
+ struct libsyscalls_structure_description *description, int dont_align_fields, size_t *extra_sizep)
+{
+ size_t field_offsets[description->num_fields];
+ struct libsyscalls_datatype_description int_description;
+ int undetermined_size = 0;
+ size_t field_offset = 0;
+ unsigned short int i;
+ unsigned subalign, subsize;
+ enum libsyscalls_error r;
+
+ /* TODO deal with pointer fields */
+ for (i = 0; i < description->num_fields && !undetermined_size; i++) {
+ field_offsets[i] = field_offset;
+ r = libsyscalls_get_datatype_description(os, arch, description->fields[i].type, &int_description);
+ if (r == LIBSYSCALLS_E_OK)
+ r = adjust_for_nonstruct(os, arch, &int_description, description, i,
+ &subsize, &subalign, extra_sizep);
+ else if (r == LIBSYSCALLS_E_ISSTRUCT)
+ r = adjust_for_struct(os, arch, data, data_size, description, i, field_offsets,
+ &subsize, &subalign, extra_sizep, &undetermined_size);
+ else
+ return r;
+ if (r)
+ return r;
+
+ /* TODO deal with bitlength */
+
+ if (!dont_align_fields) {
+ r = pad_field_into_alignment(paddingsp, npaddingsp, paddings_sizep,
+ description, i, subalign, &field_offset);
+ if (r)
+ return r;
+ }
+
+ field_offset += subsize;
+ description->size += (unsigned short int)subsize;
+ if (!dont_align_fields)
+ description->alignment = (unsigned short int)MAX(description->alignment, subalign);
+ }
+
+ if (undetermined_size)
+ description->size = 0;
+
+ return LIBSYSCALLS_E_OK;
+}
+
+
+#include "generated/structs.c"
+
+
+static enum libsyscalls_error
+get_struct_description(enum libsyscalls_os os, enum libsyscalls_arch arch, enum libsyscalls_datatype datatype, const void *data,
+ size_t data_size, struct libsyscalls_structure_description **description_out, size_t *extra_sizep)
+{
+ unsigned char fill_is_known;
+ unsigned short int array_size;
+ signed short int relative_position_of_size;
+ signed short int absolute_position_of_size;
+ struct libsyscalls_structure_description *description = NULL;
+ enum libsyscalls_error r;
+ unsigned class;
+ int dont_align_fields = 0;
+ size_t npaddings = 0, paddings_size = 0;
+ struct padding *paddings = NULL;
+
+ if ((unsigned)datatype & ~(LIBSYSCALLS_TYPEBITSMASK | (LIBSYSCALLS_TYPEBITSMASK - 1U)))
+ return LIBSYSCALLS_E_INVAL;
+
+ datatype ^= class = datatype & LIBSYSCALLS_TYPEBITSMASK;
+
+ if (class == LIBSYSCALLS_TYPEBITS_SCALAR) {
+ fill_is_known = 1;
+ array_size = 1;
+ relative_position_of_size = 0;
+ absolute_position_of_size = -1;
+
+ } else if (class == LIBSYSCALLS_TYPEBITS_ARRAY) {
+ fill_is_known = 1;
+ array_size = 0;
+ relative_position_of_size = +1;
+ absolute_position_of_size = -1;
+
+ } else if (class == LIBSYSCALLS_TYPEBITS_ARRAY_UNKNOWN_FILL) {
+ fill_is_known = 0;
+ array_size = 0;
+ relative_position_of_size = +1;
+ absolute_position_of_size = -1;
+
+ } else {
+ return LIBSYSCALLS_E_INVAL;
+ }
+
+ if (datatype < LIBSYSCALLS_TYPEOFFSET_STRUCTS_AND_UNIONS)
+ return LIBSYSCALLS_E_ISNOTSTRUCT;
+
+#define CASE(UPPERCASE, LOWERCASE)\
+ case LIBSYSCALLS_OS_##UPPERCASE:\
+ r = get_##LOWERCASE##_struct_description(arch, datatype, class, data, data_size,\
+ &description, &dont_align_fields);\
+ break
+
+ switch ((int)os) {
+ LIST_OSES(CASE, ;);
+ default:
+ r = LIBSYSCALLS_E_OSNOSUP;
+ goto fail;
+ }
+ if (r)
+ goto fail;
+
+#undef CASE
+
+#if defined(__GNUC__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wconversion"
+#endif
+
+ description->fill_is_known = fill_is_known;
+ description->array_size = array_size;
+ description->relative_position_of_size = relative_position_of_size;
+ description->absolute_position_of_size = absolute_position_of_size;
+ description->alignment = MAX(description->alignment, 1);
+
+#if defined(__GNUC__)
+# pragma GCC diagnostic pop
+#endif
+
+ r = adjust_for_fields(os, arch, data, data_size, &paddings, &npaddings, &paddings_size,
+ description, dont_align_fields, extra_sizep);
+ if (r)
+ goto fail;
+
+#define CASE(UPPERCASE, LOWERCASE)\
+ case LIBSYSCALLS_OS_##UPPERCASE:\
+ r = fix_##LOWERCASE##_struct_description(arch, datatype, class, data, data_size, description);\
+ break
+
+ switch ((int)os) {
+ LIST_OSES(CASE, ;);
+ default:
+ abort();
+ }
+ if (r)
+ goto fail;
+
+#undef CASE
+
+ r = pad_structure_into_alignment(&paddings, &npaddings, &paddings_size, description);
+ if (r)
+ goto fail;
+
+ r = apply_padding(os, arch, paddings, npaddings, &description);
+ if (r)
+ goto fail;
+
+ free(paddings);
+ *description_out = description;
+ return LIBSYSCALLS_E_OK;
+
+fail:
+ if (description) {
+ free_subdescriptions(description);
+ free(description);
+ }
+ free(paddings);
+ return r;
+}
+
+
+enum libsyscalls_error
+libsyscalls_get_struct_description(enum libsyscalls_os os, enum libsyscalls_arch arch, enum libsyscalls_datatype datatype,
+ const void *data, size_t data_size, struct libsyscalls_structure_description **description_out)
+{
+ struct libsyscalls_structure_description *description, *new;
+ size_t extra_size = 0, size, nalign, salign, align;
+ enum libsyscalls_error r;
+
+ if (!description_out)
+ return LIBSYSCALLS_E_INVAL;
+
+ r = get_struct_description(os, arch, datatype, data, data_size, &description, &extra_size);
+ if (r)
+ return r;
+ if (!extra_size) {
+ *description_out = description;
+ return LIBSYSCALLS_E_OK;
+ }
+
+ nalign = alignof(struct libsyscalls_datatype_description);
+ salign = alignof(struct libsyscalls_structure_description);
+ align = MAX(nalign, salign);
+
+ size = offsetof(struct libsyscalls_structure_description, fields);
+ size += (size_t)description->num_fields * sizeof(*description->fields);
+ size += (align - (size & (align - 1))) & (align - 1);
+
+ new = realloc(description, size + extra_size);
+ if (!new) {
+ free_subdescriptions(description);
+ free(description);
+ return LIBSYSCALLS_E_NOMEM;
+ }
+ description = new;
+
+ move_in_subdescriptions((void *)description, &size, description, nalign == align, salign == align);
+ if (nalign != align || salign != align)
+ move_in_subdescriptions((void *)description, &size, description, nalign != align, salign != align);
+
+ *description_out = description;
+ return LIBSYSCALLS_E_OK;
+}