cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

main.c (11443B)


      1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
      2/* Copyright (C) 2017-2018 Netronome Systems, Inc. */
      3
      4#include <ctype.h>
      5#include <errno.h>
      6#include <getopt.h>
      7#include <linux/bpf.h>
      8#include <stdio.h>
      9#include <stdlib.h>
     10#include <string.h>
     11
     12#include <bpf/bpf.h>
     13#include <bpf/btf.h>
     14#include <bpf/hashmap.h>
     15#include <bpf/libbpf.h>
     16
     17#include "main.h"
     18
     19#define BATCH_LINE_LEN_MAX 65536
     20#define BATCH_ARG_NB_MAX 4096
     21
     22const char *bin_name;
     23static int last_argc;
     24static char **last_argv;
     25static int (*last_do_help)(int argc, char **argv);
     26json_writer_t *json_wtr;
     27bool pretty_output;
     28bool json_output;
     29bool show_pinned;
     30bool block_mount;
     31bool verifier_logs;
     32bool relaxed_maps;
     33bool use_loader;
     34bool legacy_libbpf;
     35struct btf *base_btf;
     36struct hashmap *refs_table;
     37
     38static void __noreturn clean_and_exit(int i)
     39{
     40	if (json_output)
     41		jsonw_destroy(&json_wtr);
     42
     43	exit(i);
     44}
     45
     46void usage(void)
     47{
     48	last_do_help(last_argc - 1, last_argv + 1);
     49
     50	clean_and_exit(-1);
     51}
     52
     53static int do_help(int argc, char **argv)
     54{
     55	if (json_output) {
     56		jsonw_null(json_wtr);
     57		return 0;
     58	}
     59
     60	fprintf(stderr,
     61		"Usage: %s [OPTIONS] OBJECT { COMMAND | help }\n"
     62		"       %s batch file FILE\n"
     63		"       %s version\n"
     64		"\n"
     65		"       OBJECT := { prog | map | link | cgroup | perf | net | feature | btf | gen | struct_ops | iter }\n"
     66		"       " HELP_SPEC_OPTIONS " |\n"
     67		"                    {-V|--version} }\n"
     68		"",
     69		bin_name, bin_name, bin_name);
     70
     71	return 0;
     72}
     73
     74#ifndef BPFTOOL_VERSION
     75/* bpftool's major and minor version numbers are aligned on libbpf's. There is
     76 * an offset of 6 for the version number, because bpftool's version was higher
     77 * than libbpf's when we adopted this scheme. The patch number remains at 0
     78 * for now. Set BPFTOOL_VERSION to override.
     79 */
     80#define BPFTOOL_MAJOR_VERSION (LIBBPF_MAJOR_VERSION + 6)
     81#define BPFTOOL_MINOR_VERSION LIBBPF_MINOR_VERSION
     82#define BPFTOOL_PATCH_VERSION 0
     83#endif
     84
     85static int do_version(int argc, char **argv)
     86{
     87#ifdef HAVE_LIBBFD_SUPPORT
     88	const bool has_libbfd = true;
     89#else
     90	const bool has_libbfd = false;
     91#endif
     92#ifdef BPFTOOL_WITHOUT_SKELETONS
     93	const bool has_skeletons = false;
     94#else
     95	const bool has_skeletons = true;
     96#endif
     97
     98	if (json_output) {
     99		jsonw_start_object(json_wtr);	/* root object */
    100
    101		jsonw_name(json_wtr, "version");
    102#ifdef BPFTOOL_VERSION
    103		jsonw_printf(json_wtr, "\"%s\"", BPFTOOL_VERSION);
    104#else
    105		jsonw_printf(json_wtr, "\"%d.%d.%d\"", BPFTOOL_MAJOR_VERSION,
    106			     BPFTOOL_MINOR_VERSION, BPFTOOL_PATCH_VERSION);
    107#endif
    108		jsonw_name(json_wtr, "libbpf_version");
    109		jsonw_printf(json_wtr, "\"%d.%d\"",
    110			     libbpf_major_version(), libbpf_minor_version());
    111
    112		jsonw_name(json_wtr, "features");
    113		jsonw_start_object(json_wtr);	/* features */
    114		jsonw_bool_field(json_wtr, "libbfd", has_libbfd);
    115		jsonw_bool_field(json_wtr, "libbpf_strict", !legacy_libbpf);
    116		jsonw_bool_field(json_wtr, "skeletons", has_skeletons);
    117		jsonw_end_object(json_wtr);	/* features */
    118
    119		jsonw_end_object(json_wtr);	/* root object */
    120	} else {
    121		unsigned int nb_features = 0;
    122
    123#ifdef BPFTOOL_VERSION
    124		printf("%s v%s\n", bin_name, BPFTOOL_VERSION);
    125#else
    126		printf("%s v%d.%d.%d\n", bin_name, BPFTOOL_MAJOR_VERSION,
    127		       BPFTOOL_MINOR_VERSION, BPFTOOL_PATCH_VERSION);
    128#endif
    129		printf("using libbpf %s\n", libbpf_version_string());
    130		printf("features:");
    131		if (has_libbfd) {
    132			printf(" libbfd");
    133			nb_features++;
    134		}
    135		if (!legacy_libbpf) {
    136			printf("%s libbpf_strict", nb_features++ ? "," : "");
    137			nb_features++;
    138		}
    139		if (has_skeletons)
    140			printf("%s skeletons", nb_features++ ? "," : "");
    141		printf("\n");
    142	}
    143	return 0;
    144}
    145
    146int cmd_select(const struct cmd *cmds, int argc, char **argv,
    147	       int (*help)(int argc, char **argv))
    148{
    149	unsigned int i;
    150
    151	last_argc = argc;
    152	last_argv = argv;
    153	last_do_help = help;
    154
    155	if (argc < 1 && cmds[0].func)
    156		return cmds[0].func(argc, argv);
    157
    158	for (i = 0; cmds[i].cmd; i++) {
    159		if (is_prefix(*argv, cmds[i].cmd)) {
    160			if (!cmds[i].func) {
    161				p_err("command '%s' is not supported in bootstrap mode",
    162				      cmds[i].cmd);
    163				return -1;
    164			}
    165			return cmds[i].func(argc - 1, argv + 1);
    166		}
    167	}
    168
    169	help(argc - 1, argv + 1);
    170
    171	return -1;
    172}
    173
    174bool is_prefix(const char *pfx, const char *str)
    175{
    176	if (!pfx)
    177		return false;
    178	if (strlen(str) < strlen(pfx))
    179		return false;
    180
    181	return !memcmp(str, pfx, strlen(pfx));
    182}
    183
    184/* Last argument MUST be NULL pointer */
    185int detect_common_prefix(const char *arg, ...)
    186{
    187	unsigned int count = 0;
    188	const char *ref;
    189	char msg[256];
    190	va_list ap;
    191
    192	snprintf(msg, sizeof(msg), "ambiguous prefix: '%s' could be '", arg);
    193	va_start(ap, arg);
    194	while ((ref = va_arg(ap, const char *))) {
    195		if (!is_prefix(arg, ref))
    196			continue;
    197		count++;
    198		if (count > 1)
    199			strncat(msg, "' or '", sizeof(msg) - strlen(msg) - 1);
    200		strncat(msg, ref, sizeof(msg) - strlen(msg) - 1);
    201	}
    202	va_end(ap);
    203	strncat(msg, "'", sizeof(msg) - strlen(msg) - 1);
    204
    205	if (count >= 2) {
    206		p_err("%s", msg);
    207		return -1;
    208	}
    209
    210	return 0;
    211}
    212
    213void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep)
    214{
    215	unsigned char *data = arg;
    216	unsigned int i;
    217
    218	for (i = 0; i < n; i++) {
    219		const char *pfx = "";
    220
    221		if (!i)
    222			/* nothing */;
    223		else if (!(i % 16))
    224			fprintf(f, "\n");
    225		else if (!(i % 8))
    226			fprintf(f, "  ");
    227		else
    228			pfx = sep;
    229
    230		fprintf(f, "%s%02hhx", i ? pfx : "", data[i]);
    231	}
    232}
    233
    234/* Split command line into argument vector. */
    235static int make_args(char *line, char *n_argv[], int maxargs, int cmd_nb)
    236{
    237	static const char ws[] = " \t\r\n";
    238	char *cp = line;
    239	int n_argc = 0;
    240
    241	while (*cp) {
    242		/* Skip leading whitespace. */
    243		cp += strspn(cp, ws);
    244
    245		if (*cp == '\0')
    246			break;
    247
    248		if (n_argc >= (maxargs - 1)) {
    249			p_err("too many arguments to command %d", cmd_nb);
    250			return -1;
    251		}
    252
    253		/* Word begins with quote. */
    254		if (*cp == '\'' || *cp == '"') {
    255			char quote = *cp++;
    256
    257			n_argv[n_argc++] = cp;
    258			/* Find ending quote. */
    259			cp = strchr(cp, quote);
    260			if (!cp) {
    261				p_err("unterminated quoted string in command %d",
    262				      cmd_nb);
    263				return -1;
    264			}
    265		} else {
    266			n_argv[n_argc++] = cp;
    267
    268			/* Find end of word. */
    269			cp += strcspn(cp, ws);
    270			if (*cp == '\0')
    271				break;
    272		}
    273
    274		/* Separate words. */
    275		*cp++ = 0;
    276	}
    277	n_argv[n_argc] = NULL;
    278
    279	return n_argc;
    280}
    281
    282static int do_batch(int argc, char **argv);
    283
    284static const struct cmd cmds[] = {
    285	{ "help",	do_help },
    286	{ "batch",	do_batch },
    287	{ "prog",	do_prog },
    288	{ "map",	do_map },
    289	{ "link",	do_link },
    290	{ "cgroup",	do_cgroup },
    291	{ "perf",	do_perf },
    292	{ "net",	do_net },
    293	{ "feature",	do_feature },
    294	{ "btf",	do_btf },
    295	{ "gen",	do_gen },
    296	{ "struct_ops",	do_struct_ops },
    297	{ "iter",	do_iter },
    298	{ "version",	do_version },
    299	{ 0 }
    300};
    301
    302static int do_batch(int argc, char **argv)
    303{
    304	char buf[BATCH_LINE_LEN_MAX], contline[BATCH_LINE_LEN_MAX];
    305	char *n_argv[BATCH_ARG_NB_MAX];
    306	unsigned int lines = 0;
    307	int n_argc;
    308	FILE *fp;
    309	char *cp;
    310	int err = 0;
    311	int i;
    312
    313	if (argc < 2) {
    314		p_err("too few parameters for batch");
    315		return -1;
    316	} else if (!is_prefix(*argv, "file")) {
    317		p_err("expected 'file', got: %s", *argv);
    318		return -1;
    319	} else if (argc > 2) {
    320		p_err("too many parameters for batch");
    321		return -1;
    322	}
    323	NEXT_ARG();
    324
    325	if (!strcmp(*argv, "-"))
    326		fp = stdin;
    327	else
    328		fp = fopen(*argv, "r");
    329	if (!fp) {
    330		p_err("Can't open file (%s): %s", *argv, strerror(errno));
    331		return -1;
    332	}
    333
    334	if (json_output)
    335		jsonw_start_array(json_wtr);
    336	while (fgets(buf, sizeof(buf), fp)) {
    337		cp = strchr(buf, '#');
    338		if (cp)
    339			*cp = '\0';
    340
    341		if (strlen(buf) == sizeof(buf) - 1) {
    342			errno = E2BIG;
    343			break;
    344		}
    345
    346		/* Append continuation lines if any (coming after a line ending
    347		 * with '\' in the batch file).
    348		 */
    349		while ((cp = strstr(buf, "\\\n")) != NULL) {
    350			if (!fgets(contline, sizeof(contline), fp) ||
    351			    strlen(contline) == 0) {
    352				p_err("missing continuation line on command %d",
    353				      lines);
    354				err = -1;
    355				goto err_close;
    356			}
    357
    358			cp = strchr(contline, '#');
    359			if (cp)
    360				*cp = '\0';
    361
    362			if (strlen(buf) + strlen(contline) + 1 > sizeof(buf)) {
    363				p_err("command %d is too long", lines);
    364				err = -1;
    365				goto err_close;
    366			}
    367			buf[strlen(buf) - 2] = '\0';
    368			strcat(buf, contline);
    369		}
    370
    371		n_argc = make_args(buf, n_argv, BATCH_ARG_NB_MAX, lines);
    372		if (!n_argc)
    373			continue;
    374		if (n_argc < 0) {
    375			err = n_argc;
    376			goto err_close;
    377		}
    378
    379		if (json_output) {
    380			jsonw_start_object(json_wtr);
    381			jsonw_name(json_wtr, "command");
    382			jsonw_start_array(json_wtr);
    383			for (i = 0; i < n_argc; i++)
    384				jsonw_string(json_wtr, n_argv[i]);
    385			jsonw_end_array(json_wtr);
    386			jsonw_name(json_wtr, "output");
    387		}
    388
    389		err = cmd_select(cmds, n_argc, n_argv, do_help);
    390
    391		if (json_output)
    392			jsonw_end_object(json_wtr);
    393
    394		if (err)
    395			goto err_close;
    396
    397		lines++;
    398	}
    399
    400	if (errno && errno != ENOENT) {
    401		p_err("reading batch file failed: %s", strerror(errno));
    402		err = -1;
    403	} else {
    404		if (!json_output)
    405			printf("processed %d commands\n", lines);
    406	}
    407err_close:
    408	if (fp != stdin)
    409		fclose(fp);
    410
    411	if (json_output)
    412		jsonw_end_array(json_wtr);
    413
    414	return err;
    415}
    416
    417int main(int argc, char **argv)
    418{
    419	static const struct option options[] = {
    420		{ "json",	no_argument,	NULL,	'j' },
    421		{ "help",	no_argument,	NULL,	'h' },
    422		{ "pretty",	no_argument,	NULL,	'p' },
    423		{ "version",	no_argument,	NULL,	'V' },
    424		{ "bpffs",	no_argument,	NULL,	'f' },
    425		{ "mapcompat",	no_argument,	NULL,	'm' },
    426		{ "nomount",	no_argument,	NULL,	'n' },
    427		{ "debug",	no_argument,	NULL,	'd' },
    428		{ "use-loader",	no_argument,	NULL,	'L' },
    429		{ "base-btf",	required_argument, NULL, 'B' },
    430		{ "legacy",	no_argument,	NULL,	'l' },
    431		{ 0 }
    432	};
    433	bool version_requested = false;
    434	int opt, ret;
    435
    436	setlinebuf(stdout);
    437
    438	last_do_help = do_help;
    439	pretty_output = false;
    440	json_output = false;
    441	show_pinned = false;
    442	block_mount = false;
    443	bin_name = argv[0];
    444
    445	opterr = 0;
    446	while ((opt = getopt_long(argc, argv, "VhpjfLmndB:l",
    447				  options, NULL)) >= 0) {
    448		switch (opt) {
    449		case 'V':
    450			version_requested = true;
    451			break;
    452		case 'h':
    453			return do_help(argc, argv);
    454		case 'p':
    455			pretty_output = true;
    456			/* fall through */
    457		case 'j':
    458			if (!json_output) {
    459				json_wtr = jsonw_new(stdout);
    460				if (!json_wtr) {
    461					p_err("failed to create JSON writer");
    462					return -1;
    463				}
    464				json_output = true;
    465			}
    466			jsonw_pretty(json_wtr, pretty_output);
    467			break;
    468		case 'f':
    469			show_pinned = true;
    470			break;
    471		case 'm':
    472			relaxed_maps = true;
    473			break;
    474		case 'n':
    475			block_mount = true;
    476			break;
    477		case 'd':
    478			libbpf_set_print(print_all_levels);
    479			verifier_logs = true;
    480			break;
    481		case 'B':
    482			base_btf = btf__parse(optarg, NULL);
    483			if (libbpf_get_error(base_btf)) {
    484				p_err("failed to parse base BTF at '%s': %ld\n",
    485				      optarg, libbpf_get_error(base_btf));
    486				base_btf = NULL;
    487				return -1;
    488			}
    489			break;
    490		case 'L':
    491			use_loader = true;
    492			break;
    493		case 'l':
    494			legacy_libbpf = true;
    495			break;
    496		default:
    497			p_err("unrecognized option '%s'", argv[optind - 1]);
    498			if (json_output)
    499				clean_and_exit(-1);
    500			else
    501				usage();
    502		}
    503	}
    504
    505	if (!legacy_libbpf) {
    506		/* Allow legacy map definitions for skeleton generation.
    507		 * It will still be rejected if users use LIBBPF_STRICT_ALL
    508		 * mode for loading generated skeleton.
    509		 */
    510		libbpf_set_strict_mode(LIBBPF_STRICT_ALL & ~LIBBPF_STRICT_MAP_DEFINITIONS);
    511	} else {
    512		libbpf_set_strict_mode(LIBBPF_STRICT_AUTO_RLIMIT_MEMLOCK);
    513	}
    514
    515	argc -= optind;
    516	argv += optind;
    517	if (argc < 0)
    518		usage();
    519
    520	if (version_requested)
    521		return do_version(argc, argv);
    522
    523	ret = cmd_select(cmds, argc, argv, do_help);
    524
    525	if (json_output)
    526		jsonw_destroy(&json_wtr);
    527
    528	btf__free(base_btf);
    529
    530	return ret;
    531}