/* 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; const struct libsyscalls_datatype_description *type_description; unsigned splits, expected_splits, end; unsigned long long int result = 0, subresult; signed long long int sresult; int is_negative; type = description->fields[i].type; type_description = description->fields[i].type_description.nonstructure; 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]], USAT_MINUS(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, field, 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 & ~SET_MASK_TRAIL(LIBSYSCALLS_TYPEBITSMASK)) 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 += ALIGNMENT_ADJUSTMENT(size, align); 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; }