summaryrefslogblamecommitdiffstats
path: root/libsyscalls_get_datatype_description.c
blob: 71a217093a0b927b8cb8918ed2bddd92778a01f0 (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 / * 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,\
		                                           &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 ((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;
}