diff options
Diffstat (limited to '')
-rw-r--r-- | libsyscalls_get_struct_description.c | 656 |
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; +} |