summaryrefslogblamecommitdiffstats
path: root/libsyscalls_get_syscall_display_info.c
blob: f9bbf1aff1da6c328a0a4c552964e8c66f1bb43f (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11






                                                         



                                          





                                                                   
                                                                                        



































                                                                                 
                                                                                                           



                                                                                           

                           




                           




                                                         















                                                                                               
                                          
 

                                                                          


                          
                                                           













                                                                   

                                                                                                          






                                                          

                                                                                                  




                   



                             



                              




                                                                                                                      





























                                                                                                                 
/* See LICENSE file for copyright and license details. */
#include "common.h"
#include <stdalign.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if defined(__GNUC__)
# pragma GCC diagnostic ignored "-Winline"
#endif


#define LOWEST_BIT(X) ((X) & ~((X) - 1))
#define POST_HIGHEST_OF_CONSECUTIVELY_BITS(X) ((X) + LOWEST_BIT(X))


static inline signed char
trailing_zeroes(unsigned long long int x) { /* TODO use builtin function if available */
	int r = 0;
	if (x == 0)
		return -1;
	for (; (x & 1) == 0; x >>= 1)
		r += 1;
	return (signed char)r;
}


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;
}


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;
}


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

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

	found = bsearch(&key, signals, nsignals, sizeof(key), /* TODO interpolation search may be better */
	                is_signed ? &signed_named_number_cmp : &unsigned_named_number_cmp);
	if (!found)
		return NULL;

	(void)fallback_out;

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


#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
#endif

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;
}

#if defined(__clang__)
# pragma clang diagnostic pop
#endif


#include "generated/symbols.c"



#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic ignored "-Wnonnull-compare" /* Why should I trust that the user is using the same compiler? */
#endif

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,
                                     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;
}