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

llvm-utils.c (14838B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (C) 2015, Wang Nan <wangnan0@huawei.com>
      4 * Copyright (C) 2015, Huawei Inc.
      5 */
      6
      7#include <errno.h>
      8#include <limits.h>
      9#include <stdio.h>
     10#include <stdlib.h>
     11#include <unistd.h>
     12#include <linux/err.h>
     13#include <linux/string.h>
     14#include <linux/zalloc.h>
     15#include "debug.h"
     16#include "llvm-utils.h"
     17#include "config.h"
     18#include "util.h"
     19#include <sys/wait.h>
     20#include <subcmd/exec-cmd.h>
     21
     22#define CLANG_BPF_CMD_DEFAULT_TEMPLATE				\
     23		"$CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS "\
     24		"-DLINUX_VERSION_CODE=$LINUX_VERSION_CODE "	\
     25		"$CLANG_OPTIONS $PERF_BPF_INC_OPTIONS $KERNEL_INC_OPTIONS " \
     26		"-Wno-unused-value -Wno-pointer-sign "		\
     27		"-working-directory $WORKING_DIR "		\
     28		"-c \"$CLANG_SOURCE\" -target bpf $CLANG_EMIT_LLVM -O2 -o - $LLVM_OPTIONS_PIPE"
     29
     30struct llvm_param llvm_param = {
     31	.clang_path = "clang",
     32	.llc_path = "llc",
     33	.clang_bpf_cmd_template = CLANG_BPF_CMD_DEFAULT_TEMPLATE,
     34	.clang_opt = NULL,
     35	.opts = NULL,
     36	.kbuild_dir = NULL,
     37	.kbuild_opts = NULL,
     38	.user_set_param = false,
     39};
     40
     41static void version_notice(void);
     42
     43int perf_llvm_config(const char *var, const char *value)
     44{
     45	if (!strstarts(var, "llvm."))
     46		return 0;
     47	var += sizeof("llvm.") - 1;
     48
     49	if (!strcmp(var, "clang-path"))
     50		llvm_param.clang_path = strdup(value);
     51	else if (!strcmp(var, "clang-bpf-cmd-template"))
     52		llvm_param.clang_bpf_cmd_template = strdup(value);
     53	else if (!strcmp(var, "clang-opt"))
     54		llvm_param.clang_opt = strdup(value);
     55	else if (!strcmp(var, "kbuild-dir"))
     56		llvm_param.kbuild_dir = strdup(value);
     57	else if (!strcmp(var, "kbuild-opts"))
     58		llvm_param.kbuild_opts = strdup(value);
     59	else if (!strcmp(var, "dump-obj"))
     60		llvm_param.dump_obj = !!perf_config_bool(var, value);
     61	else if (!strcmp(var, "opts"))
     62		llvm_param.opts = strdup(value);
     63	else {
     64		pr_debug("Invalid LLVM config option: %s\n", value);
     65		return -1;
     66	}
     67	llvm_param.user_set_param = true;
     68	return 0;
     69}
     70
     71static int
     72search_program(const char *def, const char *name,
     73	       char *output)
     74{
     75	char *env, *path, *tmp = NULL;
     76	char buf[PATH_MAX];
     77	int ret;
     78
     79	output[0] = '\0';
     80	if (def && def[0] != '\0') {
     81		if (def[0] == '/') {
     82			if (access(def, F_OK) == 0) {
     83				strlcpy(output, def, PATH_MAX);
     84				return 0;
     85			}
     86		} else if (def[0] != '\0')
     87			name = def;
     88	}
     89
     90	env = getenv("PATH");
     91	if (!env)
     92		return -1;
     93	env = strdup(env);
     94	if (!env)
     95		return -1;
     96
     97	ret = -ENOENT;
     98	path = strtok_r(env, ":",  &tmp);
     99	while (path) {
    100		scnprintf(buf, sizeof(buf), "%s/%s", path, name);
    101		if (access(buf, F_OK) == 0) {
    102			strlcpy(output, buf, PATH_MAX);
    103			ret = 0;
    104			break;
    105		}
    106		path = strtok_r(NULL, ":", &tmp);
    107	}
    108
    109	free(env);
    110	return ret;
    111}
    112
    113static int search_program_and_warn(const char *def, const char *name,
    114				   char *output)
    115{
    116	int ret = search_program(def, name, output);
    117
    118	if (ret) {
    119		pr_err("ERROR:\tunable to find %s.\n"
    120		       "Hint:\tTry to install latest clang/llvm to support BPF. Check your $PATH\n"
    121		       "     \tand '%s-path' option in [llvm] section of ~/.perfconfig.\n",
    122		       name, name);
    123		version_notice();
    124	}
    125	return ret;
    126}
    127
    128#define READ_SIZE	4096
    129static int
    130read_from_pipe(const char *cmd, void **p_buf, size_t *p_read_sz)
    131{
    132	int err = 0;
    133	void *buf = NULL;
    134	FILE *file = NULL;
    135	size_t read_sz = 0, buf_sz = 0;
    136	char serr[STRERR_BUFSIZE];
    137
    138	file = popen(cmd, "r");
    139	if (!file) {
    140		pr_err("ERROR: unable to popen cmd: %s\n",
    141		       str_error_r(errno, serr, sizeof(serr)));
    142		return -EINVAL;
    143	}
    144
    145	while (!feof(file) && !ferror(file)) {
    146		/*
    147		 * Make buf_sz always have obe byte extra space so we
    148		 * can put '\0' there.
    149		 */
    150		if (buf_sz - read_sz < READ_SIZE + 1) {
    151			void *new_buf;
    152
    153			buf_sz = read_sz + READ_SIZE + 1;
    154			new_buf = realloc(buf, buf_sz);
    155
    156			if (!new_buf) {
    157				pr_err("ERROR: failed to realloc memory\n");
    158				err = -ENOMEM;
    159				goto errout;
    160			}
    161
    162			buf = new_buf;
    163		}
    164		read_sz += fread(buf + read_sz, 1, READ_SIZE, file);
    165	}
    166
    167	if (buf_sz - read_sz < 1) {
    168		pr_err("ERROR: internal error\n");
    169		err = -EINVAL;
    170		goto errout;
    171	}
    172
    173	if (ferror(file)) {
    174		pr_err("ERROR: error occurred when reading from pipe: %s\n",
    175		       str_error_r(errno, serr, sizeof(serr)));
    176		err = -EIO;
    177		goto errout;
    178	}
    179
    180	err = WEXITSTATUS(pclose(file));
    181	file = NULL;
    182	if (err) {
    183		err = -EINVAL;
    184		goto errout;
    185	}
    186
    187	/*
    188	 * If buf is string, give it terminal '\0' to make our life
    189	 * easier. If buf is not string, that '\0' is out of space
    190	 * indicated by read_sz so caller won't even notice it.
    191	 */
    192	((char *)buf)[read_sz] = '\0';
    193
    194	if (!p_buf)
    195		free(buf);
    196	else
    197		*p_buf = buf;
    198
    199	if (p_read_sz)
    200		*p_read_sz = read_sz;
    201	return 0;
    202
    203errout:
    204	if (file)
    205		pclose(file);
    206	free(buf);
    207	if (p_buf)
    208		*p_buf = NULL;
    209	if (p_read_sz)
    210		*p_read_sz = 0;
    211	return err;
    212}
    213
    214static inline void
    215force_set_env(const char *var, const char *value)
    216{
    217	if (value) {
    218		setenv(var, value, 1);
    219		pr_debug("set env: %s=%s\n", var, value);
    220	} else {
    221		unsetenv(var);
    222		pr_debug("unset env: %s\n", var);
    223	}
    224}
    225
    226static void
    227version_notice(void)
    228{
    229	pr_err(
    230"     \tLLVM 3.7 or newer is required. Which can be found from http://llvm.org\n"
    231"     \tYou may want to try git trunk:\n"
    232"     \t\tgit clone http://llvm.org/git/llvm.git\n"
    233"     \t\t     and\n"
    234"     \t\tgit clone http://llvm.org/git/clang.git\n\n"
    235"     \tOr fetch the latest clang/llvm 3.7 from pre-built llvm packages for\n"
    236"     \tdebian/ubuntu:\n"
    237"     \t\thttps://apt.llvm.org/\n\n"
    238"     \tIf you are using old version of clang, change 'clang-bpf-cmd-template'\n"
    239"     \toption in [llvm] section of ~/.perfconfig to:\n\n"
    240"     \t  \"$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS $PERF_BPF_INC_OPTIONS \\\n"
    241"     \t     -working-directory $WORKING_DIR -c $CLANG_SOURCE \\\n"
    242"     \t     -emit-llvm -o - | /path/to/llc -march=bpf -filetype=obj -o -\"\n"
    243"     \t(Replace /path/to/llc with path to your llc)\n\n"
    244);
    245}
    246
    247static int detect_kbuild_dir(char **kbuild_dir)
    248{
    249	const char *test_dir = llvm_param.kbuild_dir;
    250	const char *prefix_dir = "";
    251	const char *suffix_dir = "";
    252
    253	/* _UTSNAME_LENGTH is 65 */
    254	char release[128];
    255
    256	char *autoconf_path;
    257
    258	int err;
    259
    260	if (!test_dir) {
    261		err = fetch_kernel_version(NULL, release,
    262					   sizeof(release));
    263		if (err)
    264			return -EINVAL;
    265
    266		test_dir = release;
    267		prefix_dir = "/lib/modules/";
    268		suffix_dir = "/build";
    269	}
    270
    271	err = asprintf(&autoconf_path, "%s%s%s/include/generated/autoconf.h",
    272		       prefix_dir, test_dir, suffix_dir);
    273	if (err < 0)
    274		return -ENOMEM;
    275
    276	if (access(autoconf_path, R_OK) == 0) {
    277		free(autoconf_path);
    278
    279		err = asprintf(kbuild_dir, "%s%s%s", prefix_dir, test_dir,
    280			       suffix_dir);
    281		if (err < 0)
    282			return -ENOMEM;
    283		return 0;
    284	}
    285	pr_debug("%s: Couldn't find \"%s\", missing kernel-devel package?.\n",
    286		 __func__, autoconf_path);
    287	free(autoconf_path);
    288	return -ENOENT;
    289}
    290
    291static const char *kinc_fetch_script =
    292"#!/usr/bin/env sh\n"
    293"if ! test -d \"$KBUILD_DIR\"\n"
    294"then\n"
    295"	exit 1\n"
    296"fi\n"
    297"if ! test -f \"$KBUILD_DIR/include/generated/autoconf.h\"\n"
    298"then\n"
    299"	exit 1\n"
    300"fi\n"
    301"TMPDIR=`mktemp -d`\n"
    302"if test -z \"$TMPDIR\"\n"
    303"then\n"
    304"    exit 1\n"
    305"fi\n"
    306"cat << EOF > $TMPDIR/Makefile\n"
    307"obj-y := dummy.o\n"
    308"\\$(obj)/%.o: \\$(src)/%.c\n"
    309"\t@echo -n \"\\$(NOSTDINC_FLAGS) \\$(LINUXINCLUDE) \\$(EXTRA_CFLAGS)\"\n"
    310"\t\\$(CC) -c -o \\$@ \\$<\n"
    311"EOF\n"
    312"touch $TMPDIR/dummy.c\n"
    313"make -s -C $KBUILD_DIR M=$TMPDIR $KBUILD_OPTS dummy.o 2>/dev/null\n"
    314"RET=$?\n"
    315"rm -rf $TMPDIR\n"
    316"exit $RET\n";
    317
    318void llvm__get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts)
    319{
    320	static char *saved_kbuild_dir;
    321	static char *saved_kbuild_include_opts;
    322	int err;
    323
    324	if (!kbuild_dir || !kbuild_include_opts)
    325		return;
    326
    327	*kbuild_dir = NULL;
    328	*kbuild_include_opts = NULL;
    329
    330	if (saved_kbuild_dir && saved_kbuild_include_opts &&
    331	    !IS_ERR(saved_kbuild_dir) && !IS_ERR(saved_kbuild_include_opts)) {
    332		*kbuild_dir = strdup(saved_kbuild_dir);
    333		*kbuild_include_opts = strdup(saved_kbuild_include_opts);
    334
    335		if (*kbuild_dir && *kbuild_include_opts)
    336			return;
    337
    338		zfree(kbuild_dir);
    339		zfree(kbuild_include_opts);
    340		/*
    341		 * Don't fall through: it may breaks saved_kbuild_dir and
    342		 * saved_kbuild_include_opts if detect them again when
    343		 * memory is low.
    344		 */
    345		return;
    346	}
    347
    348	if (llvm_param.kbuild_dir && !llvm_param.kbuild_dir[0]) {
    349		pr_debug("[llvm.kbuild-dir] is set to \"\" deliberately.\n");
    350		pr_debug("Skip kbuild options detection.\n");
    351		goto errout;
    352	}
    353
    354	err = detect_kbuild_dir(kbuild_dir);
    355	if (err) {
    356		pr_warning(
    357"WARNING:\tunable to get correct kernel building directory.\n"
    358"Hint:\tSet correct kbuild directory using 'kbuild-dir' option in [llvm]\n"
    359"     \tsection of ~/.perfconfig or set it to \"\" to suppress kbuild\n"
    360"     \tdetection.\n\n");
    361		goto errout;
    362	}
    363
    364	pr_debug("Kernel build dir is set to %s\n", *kbuild_dir);
    365	force_set_env("KBUILD_DIR", *kbuild_dir);
    366	force_set_env("KBUILD_OPTS", llvm_param.kbuild_opts);
    367	err = read_from_pipe(kinc_fetch_script,
    368			     (void **)kbuild_include_opts,
    369			     NULL);
    370	if (err) {
    371		pr_warning(
    372"WARNING:\tunable to get kernel include directories from '%s'\n"
    373"Hint:\tTry set clang include options using 'clang-bpf-cmd-template'\n"
    374"     \toption in [llvm] section of ~/.perfconfig and set 'kbuild-dir'\n"
    375"     \toption in [llvm] to \"\" to suppress this detection.\n\n",
    376			*kbuild_dir);
    377
    378		zfree(kbuild_dir);
    379		goto errout;
    380	}
    381
    382	pr_debug("include option is set to %s\n", *kbuild_include_opts);
    383
    384	saved_kbuild_dir = strdup(*kbuild_dir);
    385	saved_kbuild_include_opts = strdup(*kbuild_include_opts);
    386
    387	if (!saved_kbuild_dir || !saved_kbuild_include_opts) {
    388		zfree(&saved_kbuild_dir);
    389		zfree(&saved_kbuild_include_opts);
    390	}
    391	return;
    392errout:
    393	saved_kbuild_dir = ERR_PTR(-EINVAL);
    394	saved_kbuild_include_opts = ERR_PTR(-EINVAL);
    395}
    396
    397int llvm__get_nr_cpus(void)
    398{
    399	static int nr_cpus_avail = 0;
    400	char serr[STRERR_BUFSIZE];
    401
    402	if (nr_cpus_avail > 0)
    403		return nr_cpus_avail;
    404
    405	nr_cpus_avail = sysconf(_SC_NPROCESSORS_CONF);
    406	if (nr_cpus_avail <= 0) {
    407		pr_err(
    408"WARNING:\tunable to get available CPUs in this system: %s\n"
    409"        \tUse 128 instead.\n", str_error_r(errno, serr, sizeof(serr)));
    410		nr_cpus_avail = 128;
    411	}
    412	return nr_cpus_avail;
    413}
    414
    415void llvm__dump_obj(const char *path, void *obj_buf, size_t size)
    416{
    417	char *obj_path = strdup(path);
    418	FILE *fp;
    419	char *p;
    420
    421	if (!obj_path) {
    422		pr_warning("WARNING: Not enough memory, skip object dumping\n");
    423		return;
    424	}
    425
    426	p = strrchr(obj_path, '.');
    427	if (!p || (strcmp(p, ".c") != 0)) {
    428		pr_warning("WARNING: invalid llvm source path: '%s', skip object dumping\n",
    429			   obj_path);
    430		goto out;
    431	}
    432
    433	p[1] = 'o';
    434	fp = fopen(obj_path, "wb");
    435	if (!fp) {
    436		pr_warning("WARNING: failed to open '%s': %s, skip object dumping\n",
    437			   obj_path, strerror(errno));
    438		goto out;
    439	}
    440
    441	pr_debug("LLVM: dumping %s\n", obj_path);
    442	if (fwrite(obj_buf, size, 1, fp) != 1)
    443		pr_debug("WARNING: failed to write to file '%s': %s, skip object dumping\n", obj_path, strerror(errno));
    444	fclose(fp);
    445out:
    446	free(obj_path);
    447}
    448
    449int llvm__compile_bpf(const char *path, void **p_obj_buf,
    450		      size_t *p_obj_buf_sz)
    451{
    452	size_t obj_buf_sz;
    453	void *obj_buf = NULL;
    454	int err, nr_cpus_avail;
    455	unsigned int kernel_version;
    456	char linux_version_code_str[64];
    457	const char *clang_opt = llvm_param.clang_opt;
    458	char clang_path[PATH_MAX], llc_path[PATH_MAX], abspath[PATH_MAX], nr_cpus_avail_str[64];
    459	char serr[STRERR_BUFSIZE];
    460	char *kbuild_dir = NULL, *kbuild_include_opts = NULL,
    461	     *perf_bpf_include_opts = NULL;
    462	const char *template = llvm_param.clang_bpf_cmd_template;
    463	char *pipe_template = NULL;
    464	const char *opts = llvm_param.opts;
    465	char *command_echo = NULL, *command_out;
    466	char *perf_include_dir = system_path(PERF_INCLUDE_DIR);
    467
    468	if (path[0] != '-' && realpath(path, abspath) == NULL) {
    469		err = errno;
    470		pr_err("ERROR: problems with path %s: %s\n",
    471		       path, str_error_r(err, serr, sizeof(serr)));
    472		return -err;
    473	}
    474
    475	if (!template)
    476		template = CLANG_BPF_CMD_DEFAULT_TEMPLATE;
    477
    478	err = search_program_and_warn(llvm_param.clang_path,
    479			     "clang", clang_path);
    480	if (err)
    481		return -ENOENT;
    482
    483	/*
    484	 * This is an optional work. Even it fail we can continue our
    485	 * work. Needn't check error return.
    486	 */
    487	llvm__get_kbuild_opts(&kbuild_dir, &kbuild_include_opts);
    488
    489	nr_cpus_avail = llvm__get_nr_cpus();
    490	snprintf(nr_cpus_avail_str, sizeof(nr_cpus_avail_str), "%d",
    491		 nr_cpus_avail);
    492
    493	if (fetch_kernel_version(&kernel_version, NULL, 0))
    494		kernel_version = 0;
    495
    496	snprintf(linux_version_code_str, sizeof(linux_version_code_str),
    497		 "0x%x", kernel_version);
    498	if (asprintf(&perf_bpf_include_opts, "-I%s/bpf", perf_include_dir) < 0)
    499		goto errout;
    500	force_set_env("NR_CPUS", nr_cpus_avail_str);
    501	force_set_env("LINUX_VERSION_CODE", linux_version_code_str);
    502	force_set_env("CLANG_EXEC", clang_path);
    503	force_set_env("CLANG_OPTIONS", clang_opt);
    504	force_set_env("KERNEL_INC_OPTIONS", kbuild_include_opts);
    505	force_set_env("PERF_BPF_INC_OPTIONS", perf_bpf_include_opts);
    506	force_set_env("WORKING_DIR", kbuild_dir ? : ".");
    507
    508	if (opts) {
    509		err = search_program_and_warn(llvm_param.llc_path, "llc", llc_path);
    510		if (err)
    511			goto errout;
    512
    513		err = -ENOMEM;
    514		if (asprintf(&pipe_template, "%s -emit-llvm | %s -march=bpf %s -filetype=obj -o -",
    515			      template, llc_path, opts) < 0) {
    516			pr_err("ERROR:\tnot enough memory to setup command line\n");
    517			goto errout;
    518		}
    519
    520		template = pipe_template;
    521
    522	}
    523
    524	/*
    525	 * Since we may reset clang's working dir, path of source file
    526	 * should be transferred into absolute path, except we want
    527	 * stdin to be source file (testing).
    528	 */
    529	force_set_env("CLANG_SOURCE",
    530		      (path[0] == '-') ? path : abspath);
    531
    532	pr_debug("llvm compiling command template: %s\n", template);
    533
    534	err = -ENOMEM;
    535	if (asprintf(&command_echo, "echo -n \"%s\"", template) < 0)
    536		goto errout;
    537
    538	err = read_from_pipe(command_echo, (void **) &command_out, NULL);
    539	if (err)
    540		goto errout;
    541
    542	pr_debug("llvm compiling command : %s\n", command_out);
    543
    544	err = read_from_pipe(template, &obj_buf, &obj_buf_sz);
    545	if (err) {
    546		pr_err("ERROR:\tunable to compile %s\n", path);
    547		pr_err("Hint:\tCheck error message shown above.\n");
    548		pr_err("Hint:\tYou can also pre-compile it into .o using:\n");
    549		pr_err("     \t\tclang -target bpf -O2 -c %s\n", path);
    550		pr_err("     \twith proper -I and -D options.\n");
    551		goto errout;
    552	}
    553
    554	free(command_echo);
    555	free(command_out);
    556	free(kbuild_dir);
    557	free(kbuild_include_opts);
    558	free(perf_bpf_include_opts);
    559	free(perf_include_dir);
    560
    561	if (!p_obj_buf)
    562		free(obj_buf);
    563	else
    564		*p_obj_buf = obj_buf;
    565
    566	if (p_obj_buf_sz)
    567		*p_obj_buf_sz = obj_buf_sz;
    568	return 0;
    569errout:
    570	free(command_echo);
    571	free(kbuild_dir);
    572	free(kbuild_include_opts);
    573	free(obj_buf);
    574	free(perf_bpf_include_opts);
    575	free(perf_include_dir);
    576	free(pipe_template);
    577	if (p_obj_buf)
    578		*p_obj_buf = NULL;
    579	if (p_obj_buf_sz)
    580		*p_obj_buf_sz = 0;
    581	return err;
    582}
    583
    584int llvm__search_clang(void)
    585{
    586	char clang_path[PATH_MAX];
    587
    588	return search_program_and_warn(llvm_param.clang_path, "clang", clang_path);
    589}