/* See LICENSE file for copyright and license details. */ #include "common.h" #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_BUFFER_9: datatype = LIBSYSCALLS_TYPE_CHAR; description_out->array_size = 9; break; case LIBSYSCALLS_TYPE_BUFFER_65: datatype = LIBSYSCALLS_TYPE_CHAR; description_out->array_size = 65; 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_LIST(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_LIST(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 "-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 long long int bytebits; bytebits = (unsigned long long int)larger_type.width_in_bits; for (i = 0; !LIBSYSCALLS_IS_BYTEORDER_END_AT(&larger_type, i); i++) if (larger_type.byteorder[i] && (unsigned long long int)larger_type.byteorder[i] < bytebits) bytebits = (unsigned long long int)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_AT(&larger_type, i); i++) { coverage |= (remmask & bytemask) << larger_type.byteorder[i]; remmask >>= bytebits; } /* 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; }