summaryrefslogblamecommitdiffstats
path: root/libsyscalls_get_syscall_display_info.c
blob: 5b3928403c731ab99b4c4ebece04fc0f76bb3ba7 (plain) (tree)



















































































































































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


#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) {
	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),
	                is_signed ? &signed_named_number_cmp : &unsigned_named_number_cmp);
	if (!found)
		return NULL;

	*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 += nsyms * data_size;

	if (size & (alignof(*ret->params) - 1)) {
		size |= alignof(*ret->params) - 1;
		size += 1;
	}
	paramoff = size;
	size += (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 + nsyms * data_size];
			memcpy(ret->params[i].data, &((char *)data)[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 + nsyms * data_size];
		memcpy(ret->retvalue->data, &((char *)data)[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,
                                     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;
}