/* See LICENSE file for copyright and license details. */
#include "common.h"
#include <stdlib.h>
#include <string.h>
#define COUNT_(...) 1
#define COUNT(LIST_MACRO) (LIST_MACRO(COUNT_,, +,))
enum endian {
Big,
Little,
};
#define LIST_ARCH_SPECS(X, D) /* byte intptr size_t endian sign */\
/*
X(LIBSYSCALLS_ARCH_ALPHA, 8, 64, 64, TODO(bi), TWOS_COMPLEMENT) D\
*/\
X(LIBSYSCALLS_ARCH_AMD64, 8, 64, 64, Little, TWOS_COMPLEMENT) D\
X(LIBSYSCALLS_ARCH_AMD64_X32, 8, 32, 32, Little, TWOS_COMPLEMENT) D /* amd64, 32-bit convesion */\
/*
X(LIBSYSCALLS_ARCH_ARM_OABI, 8, TODO, TODO, TODO(bi), TWOS_COMPLEMENT) D / * TODO (alignment) * / \
X(LIBSYSCALLS_ARCH_ARM_EABI, 8, TODO, TODO, TODO(bi), TWOS_COMPLEMENT) D / * TODO (alignment) * /\
X(LIBSYSCALLS_ARCH_IA64, 8, 64, 64, TODO, TWOS_COMPLEMENT) D\
*/\
X(LIBSYSCALLS_ARCH_M68K, 8, 32, 32, Big, TWOS_COMPLEMENT) D\
/*
X(LIBSYSCALLS_ARCH_MICROBLAZE, 8, TODO, TODO, TODO, TODO) D / * TODO (alignment) * /\
X(LIBSYSCALLS_ARCH_MIPS_O32, 8, 32, 32, TODO(bi), TWOS_COMPLEMENT) D / * mips32 * /\
X(LIBSYSCALLS_ARCH_MIPS_N32, 8, 32, 32, TODO(bi), TWOS_COMPLEMENT) D / * TODO (alignment) * / / * mips64, 32-bit convention * /\
X(LIBSYSCALLS_ARCH_MIPS_N64, 8, 64, 64, TODO(bi), TWOS_COMPLEMENT) D / * TODO (alignment) * / / * mips64 * /\
*/\
X(LIBSYSCALLS_ARCH_PARISC_32, 8, 32, 32, Big, TWOS_COMPLEMENT) D\
X(LIBSYSCALLS_ARCH_PARISC_64, 8, 64, 64, Big, TWOS_COMPLEMENT) D\
/*
X(LIBSYSCALLS_ARCH_POWERPC_32, 8, 32, 32, TODO(bi), TODO) D\
X(LIBSYSCALLS_ARCH_POWERPC_64, 8, 64, 64, TODO(bi), TODO) D / * TODO (alignment) * /\
X(LIBSYSCALLS_ARCH_POWERPC_NOSPU, 8, 64, 64, TODO(bi), TODO) D / * TODO (alignment) * /\
X(LIBSYSCALLS_ARCH_POWERPC_SPU, 8, 64, 64, TODO(bi), TODO) D / * TODO (alignment) * /\
X(LIBSYSCALLS_ARCH_S390_32, 8, 32, 32, Big, TODO) D / * TODO (alignment) * /\
X(LIBSYSCALLS_ARCH_S390_64, 8, 64, 64, Big, TODO) D / * TODO (alignment) * /\
X(LIBSYSCALLS_ARCH_SH, 8, 32, 32, TODO(bi), TODO) D / * TODO (alignment) * / / * not sh-5 * /\
*/\
X(LIBSYSCALLS_ARCH_SPARC_32, 8, 32, 32, Big, TWOS_COMPLEMENT) D\
/*
X(LIBSYSCALLS_ARCH_SPARC_64, 8, 64, 64, TODO(bi), TWOS_COMPLEMENT) D\
*/\
X(LIBSYSCALLS_ARCH_I386, 8, 32, 32, Little, TWOS_COMPLEMENT) D\
/*
X(LIBSYSCALLS_ARCH_XTENSA, 8, 32, 32, TODO, TODO)
*/
/* Don't forget to update SUPPORTED_ARCHES in Makefile */
/* TODO (alignment) means that it is missing in libsyscalls_get_integer_alignment.c and must also be added thither */
#include "generated/types.c"
enum libsyscalls_error
libsyscalls_get_datatype_description(enum libsyscalls_os os, enum libsyscalls_arch arch, enum libsyscalls_datatype datatype,
struct libsyscalls_datatype_description *description_out)
{
struct libsyscalls_datatype_description description_discard, larger_type;
enum libsyscalls_error r;
unsigned class;
int half = 0;
enum endian endian;
size_t i, j, charbits;
int divide_array_size_with_type_size_out;
if (!description_out)
description_out = &description_discard;
if ((unsigned)datatype & ~(LIBSYSCALLS_TYPEBITSMASK | (LIBSYSCALLS_TYPEBITSMASK - 1U)))
return LIBSYSCALLS_E_INVAL;
datatype ^= class = datatype & LIBSYSCALLS_TYPEBITSMASK;
if (class == LIBSYSCALLS_TYPEBITS_SCALAR) {
description_out->array_size = 1;
description_out->relative_position_of_array_size = 0;
description_out->absolute_position_of_array_size = -1;
description_out->fill_is_known = 1;
description_out->is_signed = 0;
description_out->is_unsigned = 0;
description_out->min_is_minus_max = 0;
description_out->padding__ = 0;
} else if (class == LIBSYSCALLS_TYPEBITS_ARRAY) {
description_out->array_size = 0;
description_out->relative_position_of_array_size = +1;
description_out->absolute_position_of_array_size = -1;
description_out->fill_is_known = 1;
description_out->is_signed = 0;
description_out->is_unsigned = 0;
description_out->min_is_minus_max = 0;
description_out->padding__ = 0;
} else if (class == LIBSYSCALLS_TYPEBITS_ARRAY_UNKNOWN_FILL) {
description_out->array_size = 0;
description_out->relative_position_of_array_size = +1;
description_out->absolute_position_of_array_size = -1;
description_out->fill_is_known = 0;
description_out->is_signed = 0;
description_out->is_unsigned = 0;
description_out->min_is_minus_max = 0;
description_out->padding__ = 0;
} else {
return LIBSYSCALLS_E_INVAL;
}
if (datatype >= LIBSYSCALLS_TYPEOFFSET_STRUCTS_AND_UNIONS)
return LIBSYSCALLS_E_ISSTRUCT;
memset(description_out->byteorder, ~0, sizeof(description_out->byteorder));
#define CASE(ARCH, CHARBITS, INTPTR_BITS, SIZE_BITS, ENDIAN, SIGN)\
case ARCH: description_out->sign_representation = LIBSYSCALLS_SIGN_##SIGN; break
switch ((int)arch) {
LIST_ARCH_SPECS(CASE, ;);
default:
return LIBSYSCALLS_E_ARCHNOSUP;
}
#undef CASE
description_out->annotation = LIBSYSCALLS_ANNOTATION_NONE;
description_out->section = LIBSYSCALLS_SECTION_WHOLE;
switch (description_out->sign_representation) {
case LIBSYSCALLS_SIGN_ONES_COMPLEMENT:
case LIBSYSCALLS_SIGN_SIGN_MAGNITUDE:
description_out->min_is_minus_max = 1;
break;
case LIBSYSCALLS_SIGN_TWOS_COMPLEMENT:
case LIBSYSCALLS_SIGN_EXCESS_HALF:
case LIBSYSCALLS_SIGN_UNDETERMINED:
break;
}
if (datatype >= LIBSYSCALLS_TYPEOFFSET_FIXED_ARRAYS) {
if (class != LIBSYSCALLS_TYPEBITS_SCALAR)
return LIBSYSCALLS_E_INVAL;
switch ((int)datatype) {
case LIBSYSCALLS_TYPE_2_INTS:
datatype = LIBSYSCALLS_TYPE_INT;
description_out->array_size = 2;
break;
case LIBSYSCALLS_TYPE_2_INTS_FD:
datatype = LIBSYSCALLS_TYPE_INT;
description_out->annotation = LIBSYSCALLS_ANNOTATION_FD;
description_out->array_size = 2;
break;
case LIBSYSCALLS_TYPE_2_UINT32S:
datatype = LIBSYSCALLS_TYPE_UINT32;
description_out->array_size = 2;
break;
case LIBSYSCALLS_TYPE_FD_SET:
goto os_dependent;
default:
return LIBSYSCALLS_E_INVAL;
}
goto unannotated;
} else if (datatype >= LIBSYSCALLS_TYPEOFFSET_ANNOTATED_NUMERICALS) {
switch ((int)datatype) {
case LIBSYSCALLS_TYPE_INT_SIGNAL:
datatype = LIBSYSCALLS_TYPE_INT;
description_out->annotation = LIBSYSCALLS_ANNOTATION_SIGNAL;
break;
case LIBSYSCALLS_TYPE_INT_FD:
datatype = LIBSYSCALLS_TYPE_INT;
description_out->annotation = LIBSYSCALLS_ANNOTATION_FD;
break;
case LIBSYSCALLS_TYPE_INT_ATFD:
datatype = LIBSYSCALLS_TYPE_INT;
description_out->annotation = LIBSYSCALLS_ANNOTATION_ATFD;
break;
case LIBSYSCALLS_TYPE_LONG_FD:
datatype = LIBSYSCALLS_TYPE_LONG;
description_out->annotation = LIBSYSCALLS_ANNOTATION_FD;
break;
case LIBSYSCALLS_TYPE_LONG_ATFD:
datatype = LIBSYSCALLS_TYPE_LONG;
description_out->annotation = LIBSYSCALLS_ANNOTATION_ATFD;
break;
default:
return LIBSYSCALLS_E_INVAL;
}
goto unannotated;
} else if (datatype >= LIBSYSCALLS_TYPEOFFSET_UNANNOTATED_NUMERICALS) {
unannotated:
if (datatype - LIBSYSCALLS_TYPEOFFSET_UNANNOTATED_NUMERICALS >= COUNT(LIBSYSCALLS_LIST_UNANNOTATED_NUMERICALS)) {
return LIBSYSCALLS_E_INVAL;
} else if (datatype == LIBSYSCALLS_TYPE_MEMORY_ADDRESS) {
datatype = LIBSYSCALLS_TYPE_INTPTR;
} else if (datatype == LIBSYSCALLS_TYPE_CHAR) {
datatype = LIBSYSCALLS_TYPE_SCHAR;
} else {
description_out->is_unsigned = (datatype - LIBSYSCALLS_TYPE_SCHAR) & 1;
description_out->is_signed = 1 ^ description_out->is_unsigned;
datatype -= description_out->is_unsigned;
}
goto arch_or_os_dependent;
} else if (datatype >= LIBSYSCALLS_TYPEOFFSET_COMPOSITE_PRIMITIVES) {
if (class != LIBSYSCALLS_TYPEBITS_SCALAR)
return LIBSYSCALLS_E_INVAL;
if (datatype - LIBSYSCALLS_TYPEOFFSET_COMPOSITE_PRIMITIVES >= COUNT(LIBSYSCALLS_LIST_COMPOSITE_PRIMITIVES))
return LIBSYSCALLS_E_INVAL;
datatype = LIBSYSCALLS_TYPE_MEMORY_ADDRESS;
goto unannotated;
} else if (datatype >= LIBSYSCALLS_TYPEOFFSET_SPLIT_PRIMITIVES) {
enum libsyscalls_datatype t = datatype;
if (class != LIBSYSCALLS_TYPEBITS_SCALAR)
return LIBSYSCALLS_E_INVAL;
switch ((int)datatype) {
case LIBSYSCALLS_TYPE_UINT64_HIGH_32:
case LIBSYSCALLS_TYPE_UINT64_LOW_32:
case LIBSYSCALLS_TYPE_UINT64_FRONT_32:
case LIBSYSCALLS_TYPE_UINT64_BACK_32:
description_out->is_unsigned = 1;
datatype = LIBSYSCALLS_TYPE_UINT32;
break;
case LIBSYSCALLS_TYPE_INT64_HIGH_32:
case LIBSYSCALLS_TYPE_INT64_LOW_32:
case LIBSYSCALLS_TYPE_INT64_FRONT_32:
case LIBSYSCALLS_TYPE_INT64_BACK_32:
t -= LIBSYSCALLS_TYPE_INT64_HIGH_32;
t += LIBSYSCALLS_TYPE_UINT64_HIGH_32;
description_out->is_signed = 1;
datatype = LIBSYSCALLS_TYPE_INT32;
break;
default:
return LIBSYSCALLS_E_INVAL;
}
switch ((int)t) {
case LIBSYSCALLS_TYPE_UINT64_HIGH_32:
description_out->section = LIBSYSCALLS_SECTION_UPPER_HALF;
break;
case LIBSYSCALLS_TYPE_UINT64_LOW_32:
description_out->section = LIBSYSCALLS_SECTION_LOWER_HALF;
break;
case LIBSYSCALLS_TYPE_UINT64_FRONT_32:
case LIBSYSCALLS_TYPE_UINT64_BACK_32:
half = t == LIBSYSCALLS_TYPE_UINT64_FRONT_32 ? -1 : +1;
r = libsyscalls_get_datatype_description(os, arch, LIBSYSCALLS_TYPE_UINT64, &larger_type);
if (r)
return r;
break;
default:
abort();
}
goto unannotated;
} else {
if (class)
return LIBSYSCALLS_E_INVAL;
switch ((int)datatype) {
case LIBSYSCALLS_TYPE_UNKNOWN:
case LIBSYSCALLS_TYPE_DYNAMIC:
datatype = LIBSYSCALLS_TYPE_DYNAMIC;
description_out->is_signed = 1;
description_out->is_unsigned = 1;
description_out->sign_representation = LIBSYSCALLS_SIGN_UNDETERMINED;
description_out->annotation = LIBSYSCALLS_ANNOTATION_UNDETERMINED;
description_out->section = LIBSYSCALLS_SECTION_UNDETERMINED;
goto os_dependent;
case LIBSYSCALLS_TYPE_NO_RETURN:
case LIBSYSCALLS_TYPE_VOID:
description_out->width_in_bits = 0;
break;
default:
return LIBSYSCALLS_E_INVAL;
}
}
return LIBSYSCALLS_E_OK;
arch_or_os_dependent:
divide_array_size_with_type_size_out = 0;
arch_dependent:
switch ((int)datatype) {
/* Note that LIBSYSCALLS_TYPE_INTN does not have the same semantics
* as POSIX's intN_t: these are not necessarily two's complement
* encoded and can have any range of valid numbers as long as that
* type has the correct width */
case LIBSYSCALLS_TYPE_INT8:
description_out->width_in_bits = 8;
goto not_os_dependent;
case LIBSYSCALLS_TYPE_INT16:
description_out->width_in_bits = 16;
goto not_os_dependent;
case LIBSYSCALLS_TYPE_INT32:
description_out->width_in_bits = 32;
goto not_os_dependent;
case LIBSYSCALLS_TYPE_INT64:
description_out->width_in_bits = 64;
goto not_os_dependent;
case LIBSYSCALLS_TYPE_INTPTR:
case LIBSYSCALLS_TYPE_PTRDIFF:
#define CASE(ARCH, CHARBITS, INTPTR_BITS, SIZE_BITS, ENDIAN, SIGN)\
case ARCH: description_out->width_in_bits = INTPTR_BITS; break
switch ((int)arch) {
LIST_ARCH_SPECS(CASE, ;);
default:
abort();
}
goto not_os_dependent;
#undef CASE
case LIBSYSCALLS_TYPE_SSIZE:
#define CASE(ARCH, CHARBITS, INTPTR_BITS, SIZE_BITS, ENDIAN, SIGN)\
case ARCH: description_out->width_in_bits = SIZE_BITS; break
switch ((int)arch) {
LIST_ARCH_SPECS(CASE, ;);
default:
abort();
}
goto not_os_dependent;
#undef CASE
default:
break;
}
os_dependent:
#define CASE(UPPERCASE, LOWERCASE)\
case LIBSYSCALLS_OS_##UPPERCASE:\
r = get_##LOWERCASE##_datatype_description(arch, &datatype, description_out,\
÷_array_size_with_type_size_out);\
break
switch ((int)os) {
LIST_OSES(CASE, ;);
default:
return LIBSYSCALLS_E_OSNOSUP;
}
if (r)
return r;
#undef CASE
switch ((int)datatype) {
case LIBSYSCALLS_TYPE_INT8:
case LIBSYSCALLS_TYPE_INT16:
case LIBSYSCALLS_TYPE_INT32:
case LIBSYSCALLS_TYPE_INT64:
case LIBSYSCALLS_TYPE_INTPTR:
case LIBSYSCALLS_TYPE_PTRDIFF:
case LIBSYSCALLS_TYPE_SSIZE:
goto arch_dependent;
default:
break;
}
not_os_dependent:
if (divide_array_size_with_type_size_out)
description_out->array_size /= description_out->width_in_bits;
#define CASE(ARCH, CHARBITS, INTPTR_BITS, SIZE_BITS, ENDIAN, SIGN)\
case ARCH: charbits = CHARBITS; endian = ENDIAN; break
switch ((int)arch) {
LIST_ARCH_SPECS(CASE, ;);
default:
abort();
}
#undef CASE
if (description_out->width_in_bits % charbits)
return LIBSYSCALLS_E_NOSUCHTYPE;
if (description_out->width_in_bits > (size_t)1 << LIBSYSCALLS_BITSIZEOF_(*description_out->byteorder) ||
description_out->width_in_bits > ELEMSOF(description_out->byteorder) * charbits) {
abort();
}
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wimplicit-int-conversion" /* we don't want to hardcode the type here */
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wconversion"
#endif
switch (endian) {
case Big:
for (i = 0, j = description_out->width_in_bits; j;)
description_out->byteorder[i++] = j -= charbits;
break;
case Little:
for (i = 0, j = 0; j < description_out->width_in_bits; j += charbits)
description_out->byteorder[i++] = j;
break;
default:
abort();
}
#if defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
if (half) {
unsigned long long int bytemask, coverage = 0;
unsigned long long int remmask;
unsigned char bytebits = UCHAR_MAX;
for (i = 0; !LIBSYSCALLS_IS_BYTEORDER_END(larger_type.byteorder[i]); i++)
if (larger_type.byteorder[i] && larger_type.byteorder[i] < bytebits)
bytebits = larger_type.byteorder[i];
bytemask = (1ULL << bytebits) - 1ULL;
remmask = 0xFFFFFFFFULL; /* we known from the code above that we are working with 32-bit sections */
for (i = 0; !LIBSYSCALLS_IS_BYTEORDER_END(larger_type.byteorder[i]); i++) {
coverage |= (remmask & bytemask) << larger_type.byteorder[i];
remmask >>= bytebits;
}
#if defined(__clang__)
# pragma clang diagnostic pop
#endif
/* we known from the code above that we are working with split 64-bit integers */
if (half == +1)
coverage ^= 0xFFFFFFFFFFFFFFFFull;
if (coverage == 0xFFFFFFFF00000000ull) description_out->section = LIBSYSCALLS_SECTION_UPPER_HALF;
else if (coverage == 0x00000000FFFFFFFFull) description_out->section = LIBSYSCALLS_SECTION_LOWER_HALF;
else if (coverage == 0x0000FFFFFFFF0000ull) description_out->section = LIBSYSCALLS_SECTION_INNER_HALF;
else if (coverage == 0xFFFF00000000FFFFull) description_out->section = LIBSYSCALLS_SECTION_OUTER_HALF;
else if (coverage == 0x0000FFFF0000FFFFull) description_out->section = LIBSYSCALLS_SECTION_EVEN_QUARTERS_AS_HALF;
else if (coverage == 0xFFFF0000FFFF0000ull) description_out->section = LIBSYSCALLS_SECTION_ODD_QUARTERS_AS_HALF;
else if (coverage == 0x00FF00FF00FF00FFull) description_out->section = LIBSYSCALLS_SECTION_EVEN_BYTES_AS_HALF;
else if (coverage == 0xFF00FF00FF00FF00ull) description_out->section = LIBSYSCALLS_SECTION_ODD_BYTES_AS_HALF;
else
abort();
}
return LIBSYSCALLS_E_OK;
}