18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Minimal BPF JIT image disassembler
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
68c2ecf20Sopenharmony_ci * debugging or verification purposes.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * To get the disassembly of the JIT code, do the following:
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *  1) `echo 2 > /proc/sys/net/core/bpf_jit_enable`
118c2ecf20Sopenharmony_ci *  2) Load a BPF filter (e.g. `tcpdump -p -n -s 0 -i eth1 host 192.168.20.0/24`)
128c2ecf20Sopenharmony_ci *  3) Run e.g. `bpf_jit_disasm -o` to read out the last JIT code
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * Copyright 2013 Daniel Borkmann <borkmann@redhat.com>
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <stdint.h>
188c2ecf20Sopenharmony_ci#include <stdio.h>
198c2ecf20Sopenharmony_ci#include <stdlib.h>
208c2ecf20Sopenharmony_ci#include <assert.h>
218c2ecf20Sopenharmony_ci#include <unistd.h>
228c2ecf20Sopenharmony_ci#include <string.h>
238c2ecf20Sopenharmony_ci#include <bfd.h>
248c2ecf20Sopenharmony_ci#include <dis-asm.h>
258c2ecf20Sopenharmony_ci#include <regex.h>
268c2ecf20Sopenharmony_ci#include <fcntl.h>
278c2ecf20Sopenharmony_ci#include <sys/klog.h>
288c2ecf20Sopenharmony_ci#include <sys/types.h>
298c2ecf20Sopenharmony_ci#include <sys/stat.h>
308c2ecf20Sopenharmony_ci#include <limits.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define CMD_ACTION_SIZE_BUFFER		10
338c2ecf20Sopenharmony_ci#define CMD_ACTION_READ_ALL		3
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic void get_exec_path(char *tpath, size_t size)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	char *path;
388c2ecf20Sopenharmony_ci	ssize_t len;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	snprintf(tpath, size, "/proc/%d/exe", (int) getpid());
418c2ecf20Sopenharmony_ci	tpath[size - 1] = 0;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	path = strdup(tpath);
448c2ecf20Sopenharmony_ci	assert(path);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	len = readlink(path, tpath, size);
478c2ecf20Sopenharmony_ci	tpath[len] = 0;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	free(path);
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic void get_asm_insns(uint8_t *image, size_t len, int opcodes)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	int count, i, pc = 0;
558c2ecf20Sopenharmony_ci	char tpath[PATH_MAX];
568c2ecf20Sopenharmony_ci	struct disassemble_info info;
578c2ecf20Sopenharmony_ci	disassembler_ftype disassemble;
588c2ecf20Sopenharmony_ci	bfd *bfdf;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	memset(tpath, 0, sizeof(tpath));
618c2ecf20Sopenharmony_ci	get_exec_path(tpath, sizeof(tpath));
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	bfdf = bfd_openr(tpath, NULL);
648c2ecf20Sopenharmony_ci	assert(bfdf);
658c2ecf20Sopenharmony_ci	assert(bfd_check_format(bfdf, bfd_object));
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	init_disassemble_info(&info, stdout, (fprintf_ftype) fprintf);
688c2ecf20Sopenharmony_ci	info.arch = bfd_get_arch(bfdf);
698c2ecf20Sopenharmony_ci	info.mach = bfd_get_mach(bfdf);
708c2ecf20Sopenharmony_ci	info.buffer = image;
718c2ecf20Sopenharmony_ci	info.buffer_length = len;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	disassemble_init_for_target(&info);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#ifdef DISASM_FOUR_ARGS_SIGNATURE
768c2ecf20Sopenharmony_ci	disassemble = disassembler(info.arch,
778c2ecf20Sopenharmony_ci				   bfd_big_endian(bfdf),
788c2ecf20Sopenharmony_ci				   info.mach,
798c2ecf20Sopenharmony_ci				   bfdf);
808c2ecf20Sopenharmony_ci#else
818c2ecf20Sopenharmony_ci	disassemble = disassembler(bfdf);
828c2ecf20Sopenharmony_ci#endif
838c2ecf20Sopenharmony_ci	assert(disassemble);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	do {
868c2ecf20Sopenharmony_ci		printf("%4x:\t", pc);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci		count = disassemble(pc, &info);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci		if (opcodes) {
918c2ecf20Sopenharmony_ci			printf("\n\t");
928c2ecf20Sopenharmony_ci			for (i = 0; i < count; ++i)
938c2ecf20Sopenharmony_ci				printf("%02x ", (uint8_t) image[pc + i]);
948c2ecf20Sopenharmony_ci		}
958c2ecf20Sopenharmony_ci		printf("\n");
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci		pc += count;
988c2ecf20Sopenharmony_ci	} while(count > 0 && pc < len);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	bfd_close(bfdf);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic char *get_klog_buff(unsigned int *klen)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	int ret, len;
1068c2ecf20Sopenharmony_ci	char *buff;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	len = klogctl(CMD_ACTION_SIZE_BUFFER, NULL, 0);
1098c2ecf20Sopenharmony_ci	if (len < 0)
1108c2ecf20Sopenharmony_ci		return NULL;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	buff = malloc(len);
1138c2ecf20Sopenharmony_ci	if (!buff)
1148c2ecf20Sopenharmony_ci		return NULL;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	ret = klogctl(CMD_ACTION_READ_ALL, buff, len);
1178c2ecf20Sopenharmony_ci	if (ret < 0) {
1188c2ecf20Sopenharmony_ci		free(buff);
1198c2ecf20Sopenharmony_ci		return NULL;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	*klen = ret;
1238c2ecf20Sopenharmony_ci	return buff;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic char *get_flog_buff(const char *file, unsigned int *klen)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	int fd, ret, len;
1298c2ecf20Sopenharmony_ci	struct stat fi;
1308c2ecf20Sopenharmony_ci	char *buff;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	fd = open(file, O_RDONLY);
1338c2ecf20Sopenharmony_ci	if (fd < 0)
1348c2ecf20Sopenharmony_ci		return NULL;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	ret = fstat(fd, &fi);
1378c2ecf20Sopenharmony_ci	if (ret < 0 || !S_ISREG(fi.st_mode))
1388c2ecf20Sopenharmony_ci		goto out;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	len = fi.st_size + 1;
1418c2ecf20Sopenharmony_ci	buff = malloc(len);
1428c2ecf20Sopenharmony_ci	if (!buff)
1438c2ecf20Sopenharmony_ci		goto out;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	memset(buff, 0, len);
1468c2ecf20Sopenharmony_ci	ret = read(fd, buff, len - 1);
1478c2ecf20Sopenharmony_ci	if (ret <= 0)
1488c2ecf20Sopenharmony_ci		goto out_free;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	close(fd);
1518c2ecf20Sopenharmony_ci	*klen = ret;
1528c2ecf20Sopenharmony_ci	return buff;
1538c2ecf20Sopenharmony_ciout_free:
1548c2ecf20Sopenharmony_ci	free(buff);
1558c2ecf20Sopenharmony_ciout:
1568c2ecf20Sopenharmony_ci	close(fd);
1578c2ecf20Sopenharmony_ci	return NULL;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic char *get_log_buff(const char *file, unsigned int *klen)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	return file ? get_flog_buff(file, klen) : get_klog_buff(klen);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic void put_log_buff(char *buff)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	free(buff);
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic uint8_t *get_last_jit_image(char *haystack, size_t hlen,
1718c2ecf20Sopenharmony_ci				   unsigned int *ilen)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	char *ptr, *pptr, *tmp;
1748c2ecf20Sopenharmony_ci	off_t off = 0;
1758c2ecf20Sopenharmony_ci	unsigned int proglen;
1768c2ecf20Sopenharmony_ci	int ret, flen, pass, ulen = 0;
1778c2ecf20Sopenharmony_ci	regmatch_t pmatch[1];
1788c2ecf20Sopenharmony_ci	unsigned long base;
1798c2ecf20Sopenharmony_ci	regex_t regex;
1808c2ecf20Sopenharmony_ci	uint8_t *image;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (hlen == 0)
1838c2ecf20Sopenharmony_ci		return NULL;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	ret = regcomp(&regex, "flen=[[:alnum:]]+ proglen=[[:digit:]]+ "
1868c2ecf20Sopenharmony_ci		      "pass=[[:digit:]]+ image=[[:xdigit:]]+", REG_EXTENDED);
1878c2ecf20Sopenharmony_ci	assert(ret == 0);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	ptr = haystack;
1908c2ecf20Sopenharmony_ci	memset(pmatch, 0, sizeof(pmatch));
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	while (1) {
1938c2ecf20Sopenharmony_ci		ret = regexec(&regex, ptr, 1, pmatch, 0);
1948c2ecf20Sopenharmony_ci		if (ret == 0) {
1958c2ecf20Sopenharmony_ci			ptr += pmatch[0].rm_eo;
1968c2ecf20Sopenharmony_ci			off += pmatch[0].rm_eo;
1978c2ecf20Sopenharmony_ci			assert(off < hlen);
1988c2ecf20Sopenharmony_ci		} else
1998c2ecf20Sopenharmony_ci			break;
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	ptr = haystack + off - (pmatch[0].rm_eo - pmatch[0].rm_so);
2038c2ecf20Sopenharmony_ci	ret = sscanf(ptr, "flen=%d proglen=%u pass=%d image=%lx",
2048c2ecf20Sopenharmony_ci		     &flen, &proglen, &pass, &base);
2058c2ecf20Sopenharmony_ci	if (ret != 4) {
2068c2ecf20Sopenharmony_ci		regfree(&regex);
2078c2ecf20Sopenharmony_ci		return NULL;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci	if (proglen > 1000000) {
2108c2ecf20Sopenharmony_ci		printf("proglen of %d too big, stopping\n", proglen);
2118c2ecf20Sopenharmony_ci		return NULL;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	image = malloc(proglen);
2158c2ecf20Sopenharmony_ci	if (!image) {
2168c2ecf20Sopenharmony_ci		printf("Out of memory\n");
2178c2ecf20Sopenharmony_ci		return NULL;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci	memset(image, 0, proglen);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	tmp = ptr = haystack + off;
2228c2ecf20Sopenharmony_ci	while ((ptr = strtok(tmp, "\n")) != NULL && ulen < proglen) {
2238c2ecf20Sopenharmony_ci		tmp = NULL;
2248c2ecf20Sopenharmony_ci		if (!strstr(ptr, "JIT code"))
2258c2ecf20Sopenharmony_ci			continue;
2268c2ecf20Sopenharmony_ci		pptr = ptr;
2278c2ecf20Sopenharmony_ci		while ((ptr = strstr(pptr, ":")))
2288c2ecf20Sopenharmony_ci			pptr = ptr + 1;
2298c2ecf20Sopenharmony_ci		ptr = pptr;
2308c2ecf20Sopenharmony_ci		do {
2318c2ecf20Sopenharmony_ci			image[ulen++] = (uint8_t) strtoul(pptr, &pptr, 16);
2328c2ecf20Sopenharmony_ci			if (ptr == pptr) {
2338c2ecf20Sopenharmony_ci				ulen--;
2348c2ecf20Sopenharmony_ci				break;
2358c2ecf20Sopenharmony_ci			}
2368c2ecf20Sopenharmony_ci			if (ulen >= proglen)
2378c2ecf20Sopenharmony_ci				break;
2388c2ecf20Sopenharmony_ci			ptr = pptr;
2398c2ecf20Sopenharmony_ci		} while (1);
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	assert(ulen == proglen);
2438c2ecf20Sopenharmony_ci	printf("%u bytes emitted from JIT compiler (pass:%d, flen:%d)\n",
2448c2ecf20Sopenharmony_ci	       proglen, pass, flen);
2458c2ecf20Sopenharmony_ci	printf("%lx + <x>:\n", base);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	regfree(&regex);
2488c2ecf20Sopenharmony_ci	*ilen = ulen;
2498c2ecf20Sopenharmony_ci	return image;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic void usage(void)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	printf("Usage: bpf_jit_disasm [...]\n");
2558c2ecf20Sopenharmony_ci	printf("       -o          Also display related opcodes (default: off).\n");
2568c2ecf20Sopenharmony_ci	printf("       -O <file>   Write binary image of code to file, don't disassemble to stdout.\n");
2578c2ecf20Sopenharmony_ci	printf("       -f <file>   Read last image dump from file or stdin (default: klog).\n");
2588c2ecf20Sopenharmony_ci	printf("       -h          Display this help.\n");
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ciint main(int argc, char **argv)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	unsigned int len, klen, opt, opcodes = 0;
2648c2ecf20Sopenharmony_ci	char *kbuff, *file = NULL;
2658c2ecf20Sopenharmony_ci	char *ofile = NULL;
2668c2ecf20Sopenharmony_ci	int ofd;
2678c2ecf20Sopenharmony_ci	ssize_t nr;
2688c2ecf20Sopenharmony_ci	uint8_t *pos;
2698c2ecf20Sopenharmony_ci	uint8_t *image = NULL;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	while ((opt = getopt(argc, argv, "of:O:")) != -1) {
2728c2ecf20Sopenharmony_ci		switch (opt) {
2738c2ecf20Sopenharmony_ci		case 'o':
2748c2ecf20Sopenharmony_ci			opcodes = 1;
2758c2ecf20Sopenharmony_ci			break;
2768c2ecf20Sopenharmony_ci		case 'O':
2778c2ecf20Sopenharmony_ci			ofile = optarg;
2788c2ecf20Sopenharmony_ci			break;
2798c2ecf20Sopenharmony_ci		case 'f':
2808c2ecf20Sopenharmony_ci			file = optarg;
2818c2ecf20Sopenharmony_ci			break;
2828c2ecf20Sopenharmony_ci		default:
2838c2ecf20Sopenharmony_ci			usage();
2848c2ecf20Sopenharmony_ci			return -1;
2858c2ecf20Sopenharmony_ci		}
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	bfd_init();
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	kbuff = get_log_buff(file, &klen);
2918c2ecf20Sopenharmony_ci	if (!kbuff) {
2928c2ecf20Sopenharmony_ci		fprintf(stderr, "Could not retrieve log buffer!\n");
2938c2ecf20Sopenharmony_ci		return -1;
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	image = get_last_jit_image(kbuff, klen, &len);
2978c2ecf20Sopenharmony_ci	if (!image) {
2988c2ecf20Sopenharmony_ci		fprintf(stderr, "No JIT image found!\n");
2998c2ecf20Sopenharmony_ci		goto done;
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci	if (!ofile) {
3028c2ecf20Sopenharmony_ci		get_asm_insns(image, len, opcodes);
3038c2ecf20Sopenharmony_ci		goto done;
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	ofd = open(ofile, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
3078c2ecf20Sopenharmony_ci	if (ofd < 0) {
3088c2ecf20Sopenharmony_ci		fprintf(stderr, "Could not open file %s for writing: ", ofile);
3098c2ecf20Sopenharmony_ci		perror(NULL);
3108c2ecf20Sopenharmony_ci		goto done;
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci	pos = image;
3138c2ecf20Sopenharmony_ci	do {
3148c2ecf20Sopenharmony_ci		nr = write(ofd, pos, len);
3158c2ecf20Sopenharmony_ci		if (nr < 0) {
3168c2ecf20Sopenharmony_ci			fprintf(stderr, "Could not write data to %s: ", ofile);
3178c2ecf20Sopenharmony_ci			perror(NULL);
3188c2ecf20Sopenharmony_ci			goto done;
3198c2ecf20Sopenharmony_ci		}
3208c2ecf20Sopenharmony_ci		len -= nr;
3218c2ecf20Sopenharmony_ci		pos += nr;
3228c2ecf20Sopenharmony_ci	} while (len);
3238c2ecf20Sopenharmony_ci	close(ofd);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cidone:
3268c2ecf20Sopenharmony_ci	put_log_buff(kbuff);
3278c2ecf20Sopenharmony_ci	free(image);
3288c2ecf20Sopenharmony_ci	return 0;
3298c2ecf20Sopenharmony_ci}
330