/* 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;
}