summaryrefslogblamecommitdiffstats
path: root/libsyscalls_get_syscall_display_info.c
blob: add65e2f147fc0fe2682c9205f8415380cd72e76 (plain) (tree)
1
2
3

                                                         
 















                                                             

      




                                                    
 
                                                                                                
 



                            
 





                                     

      


 




                                                                                                                                    







                                                                          
             







                                                                          



































































































                                                                                                                        
                   
                                                                                                                      



                                                                    
                      
 
                                                                               

                            

                                                             
                                                                                           



                                                                                                   


                            
                            
 




















                                                                                               
                                          
 

                                                                          


                          
                                                           













                                                                   

                                                                                                          






                                                          

                                                                                                  












                                                                                        
                                                                                     
























                                                                                                                 
/* See LICENSE file for copyright and license details. */
#include "common.h"


static inline signed char
trailing_zeroes(unsigned long long int x)
{
#ifndef HAVE_BUILTIN_FFSLL
# if defined(__HAVE_BUILTIN_FFSLL)
#  define HAVE_BUILTIN_FFSLL
# elif defined(__clang__)
#  if __clang_major__ >= 5
#   define HAVE_BUILTIN_FFSLL
#  endif
# elif defined(__GNUC__)
#   if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
#    define HAVE_BUILTIN_FFSLL
#   endif
# endif
#endif

#ifdef HAVE_BUILTIN_FFSLL
# if defined(__GNUC__)
#  pragma GCC diagnostic push
#  pragma GCC diagnostic ignored "-Wsign-conversion"
# endif

	return (signed char)(__builtin_ffsll(x) - 1); /* the type for x has changed over time */

# if defined(__GNUC__)
#  pragma GCC diagnostic pop
# endif
#else

	int r = 0;
	if (x == 0)
		return -1;
	for (; (x & 1) == 0; x >>= 1)
		r += 1;
	return (signed char)r;

#endif
}


#define USE_INTERPOLATION_SEARCH /* TODO validate; should be configurable and (if good) default on systems with intrinsic divsion */
#ifndef USE_INTERPOLATION_SEARCH


PURE_FUNCTION
static int
signed_named_number_cmp(const void *a_, const void *b_)
{
	const struct libsyscalls_named_number *a = a_, *b = b_;
	return a->number.s < b->number.s ? -1 : a->number.s > b->number.s;
}


PURE_FUNCTION
static int
unsigned_named_number_cmp(const void *a_, const void *b_)
{
	const struct libsyscalls_named_number *a = a_, *b = b_;
	return a->number.u < b->number.u ? -1 : a->number.u > b->number.u;
}


#else


/* convertion to unsigned is a modulo (unsigned maximum + 1) operation */
#define DIFF(TYPE, A, B) ((unsigned TYPE)(A) - (unsigned TYPE)(B))

#define INTERPOL_SEARCH(KEY, BASE, N, READ)\
	do {\
		double guess_d;\
		unsigned long long int guess;\
		size_t h = (N);\
		\
		if (!h--)\
			return NULL;\
		\
		if ((KEY) <= READ((BASE), 0))\
			return (KEY) == READ((BASE), 0) ? (BASE) : NULL;\
		if ((KEY) >= READ((BASE), h))\
			return (KEY) == READ((BASE), h) ? (BASE) : NULL;\
		if (READ((BASE), 0) == READ((BASE), h))\
			return NULL;\
		\
		guess = DIFF(long long int, (KEY), READ((BASE), 0));\
		if (h > ULLONG_MAX / guess)\
			goto use_double;\
		\
		for (;;) {\
			guess = DIFF(long long int, (KEY), READ((BASE), 0));\
			guess *= (unsigned long long int)h;\
			guess /= DIFF(long long int, READ((BASE), h), READ((BASE), 0));\
			\
			if (READ((BASE), guess) < (KEY)) {\
				h -= guess += 1;\
				(BASE) = &(BASE)[guess];\
			} else if (READ((BASE), guess) > (KEY)) {\
				h -= guess -= 1;\
			} else {\
				return &(BASE)[guess];\
			}\
			\
			if (READ((BASE), 0) == READ((BASE), h))\
				return (KEY) == READ((BASE), 0) ? (BASE) : NULL;\
			if ((KEY) < READ((BASE), 0))\
				return NULL;\
			if ((KEY) > READ((BASE), h))\
				return NULL;\
		}\
		\
	use_double:\
		for (;;) {\
			guess = DIFF(long long int, (KEY), READ((BASE), 0));\
			guess_d = (double)guess * (double)h;\
			guess = DIFF(long long int, READ((BASE), h), READ((BASE), 0));\
			guess_d /= (double)guess;\
			guess = (unsigned long long int)guess_d;\
			\
			if (READ((BASE), guess) < (KEY)) {\
				h -= guess += 1;\
				(BASE) = &(BASE)[guess];\
			} else if (READ((BASE), guess) > (KEY)) {\
				h -= guess -= 1;\
			} else {\
				return &(BASE)[guess];\
			}\
			\
			if (READ((BASE), 0) == READ((BASE), h))\
				return (KEY) == READ((BASE), 0) ? (BASE) : NULL;\
			if ((KEY) < READ((BASE), 0))\
				return NULL;\
			if ((KEY) > READ((BASE), h))\
				return NULL;\
		}\
	} while (0)


PURE_FUNCTION
static const struct libsyscalls_named_number *
interpol_search_signed_named_number(signed long long int key, const struct libsyscalls_named_number *base, size_t n)
{
#define X(ARR, I) ((ARR)[I].number.s)
	INTERPOL_SEARCH(key, base, n, X);
#undef X
}


PURE_FUNCTION
static const struct libsyscalls_named_number *
interpol_search_unsigned_named_number(unsigned long long int key, const struct libsyscalls_named_number *base, size_t n)
{
#define X(ARR, I) ((ARR)[I].number.u)
	INTERPOL_SEARCH(key, base, n, X);
#undef X
}

#undef DIFF


#endif


static const char *
extract_signal(enum libsyscalls_os os, enum libsyscalls_arch arch, unsigned long long int *valuep, char *fallback_out)
{
	const struct libsyscalls_named_number *signals, *found;
	size_t nsignals;
	struct libsyscalls_named_number key = {.number.u = *valuep};
	int is_signed;

	if (libsyscalls_get_signals(os, arch, &signals, &nsignals, &is_signed))
		return NULL;

#ifndef USE_INTERPOLATION_SEARCH
	found = bsearch(&key, signals, nsignals, sizeof(key),
	                is_signed ? &signed_named_number_cmp : &unsigned_named_number_cmp);
#else
	found = is_signed ? interpol_search_signed_named_number(key.number.s, signals, nsignals)
	                  : interpol_search_unsigned_named_number(key.number.u, signals, nsignals);
#endif
	if (!found)
		return NULL;

	(void) fallback_out;

	*valuep = 0;
	return found->name;
}


static struct libsyscalls_syscall_display_info *
build_syscall_display_info(void *data, size_t data_size, size_t data_align,
                           libsyscalls_symbol_printer_function **funcs,
			   const struct libsyscalls_syscall_abi *syscall, int nargs, int nsyms)
{
	struct libsyscalls_syscall_display_info *ret;
	size_t size, dataoff, paramoff;
	int i;

	size = sizeof(*ret);

	if (size & (data_align - 1)) {
		size |= data_align - 1;
		size += 1;
	}
	dataoff = size;
	size += (size_t)nsyms * data_size;

	if (size & (alignof(struct libsyscalls_syscall_type_info) - 1)) {
		size |= alignof(struct libsyscalls_syscall_type_info) - 1;
		size += 1;
	}
	paramoff = size;
	size += (size_t)(nargs + 1) * sizeof(*ret->params);

	ret = calloc(1, size);
	if (!ret)
		return NULL;

	ret->size = sizeof(*ret);
	ret->params = (void *)&((char *)ret)[paramoff];
	ret->retvalue = &ret->params[nargs];

	nsyms = 0;
	for (i = 0; i < nargs; i++) {
		ret->params[i].type = syscall->parameters_types[i];
		if (funcs[i]) {
			ret->params[i].function = funcs[i];
			ret->params[i].data = (void *)&((char *)ret)[dataoff + (size_t)nsyms * data_size];
			memcpy(ret->params[i].data, &((char *)data)[(size_t)i * data_size], data_size);
			nsyms++;
		}
	}
	ret->retvalue->type = syscall->return_type;
	ret->retvalue->expect_zero = syscall->expect_zero;
	if (funcs[i]) {
		ret->retvalue->function = funcs[i];
		ret->retvalue->data = (void *)&((char *)ret)[dataoff + (size_t)nsyms * data_size];
		memcpy(ret->retvalue->data, &((char *)data)[(size_t)i * data_size], data_size);
	}

	return ret;
}


#include "generated/symbols.c"


enum libsyscalls_error
libsyscalls_get_syscall_display_info(enum libsyscalls_os os, enum libsyscalls_arch arch,
                                     const struct libsyscalls_syscall_abi *syscall,
                                     long long int syscall_number,
                                     const unsigned long long int *syscall_arguments,
                                     struct libsyscalls_syscall_display_info **info_out)
{
	struct libsyscalls_syscall_display_info *info;

	if (!syscall || !syscall_arguments || !info_out)
		return LIBSYSCALLS_E_INVAL;

#define CASE(UPPERCASE, LOWERCASE)\
	case LIBSYSCALLS_OS_##UPPERCASE:\
		info = get_##LOWERCASE##_syscall_display_info(arch, syscall, syscall_number, syscall_arguments);\
		break

	switch ((int)os) {
	LIST_OSES(CASE, ;);
	default:
		return LIBSYSCALLS_E_OSNOSUP;
	}

#undef CASE

	if (!info)
		return LIBSYSCALLS_E_NOMEM;
	*info_out = info;
	return LIBSYSCALLS_E_OK;
}