summaryrefslogblamecommitdiffstats
path: root/libsyscalls_get_datatype_description.c
blob: d790531e7c0e929220a35f544dddefb80d2107ff (plain) (tree)







































































































































































































































































































































































































                                                                                                                                 
                                                                                   
























                                                                                                                                 
/* 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\
	X(LIBSYSCALLS_ARCH_ARM_EABI,      8,    TODO,   TODO,   TODO(bi), TWOS_COMPLEMENT) D\
	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\
	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 /* mips64, 32-bit convention * /\
	X(LIBSYSCALLS_ARCH_MIPS_N64,      8,    64,     64,     TODO(bi), TWOS_COMPLEMENT) D /* 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\
	X(LIBSYSCALLS_ARCH_POWERPC_NOSPU, 8,    64,     64,     TODO(bi), TODO) D\
	X(LIBSYSCALLS_ARCH_POWERPC_SPU,   8,    64,     64,     TODO(bi), TODO) D\
	X(LIBSYSCALLS_ARCH_S390_32,       8,    32,     32,     Big,      TODO) D\
	X(LIBSYSCALLS_ARCH_S390_64,       8,    64,     64,     Big,      TODO) D\
	X(LIBSYSCALLS_ARCH_SH,            8,    32,     32,     TODO(bi), TODO) D /* 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 */


#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 (datatype & ~(LIBSYSCALLS_TYPEBITSMASK | (LIBSYSCALLS_TYPEBITSMASK - 1)))
		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, ~0U, 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:
		break;
	}

	if (datatype >= LIBSYSCALLS_TYPEOFFSET_FIXED_ARRAYS) {
		if (class != LIBSYSCALLS_TYPEBITS_SCALAR)
			return LIBSYSCALLS_E_INVAL;
		switch (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 (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 &= ~1;
		}
		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) {
		if (class != LIBSYSCALLS_TYPEBITS_SCALAR)
			return LIBSYSCALLS_E_INVAL;
		enum libsyscalls_datatype t = datatype;
		switch (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 (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 (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 (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,\
		                                           &divide_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 (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)UCHAR_MAX * sizeof(*description_out->byteorder) ||
	    description_out->width_in_bits > sizeof(description_out->byteorder) / sizeof(*description_out->byteorder) * charbits)
		abort();

	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 (half) {
		unsigned long long int bytemask, coverage = 0;
		unsigned long long int remmask;
		unsigned char bytebits;

		for (i = 0; larger_type.byteorder[i]; i++)
			if (LIBSYSCALLS_IS_BYTEORDER_END(larger_type.byteorder[i]))
				abort();
		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; larger_type.byteorder[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      (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;
}