xref: /openbmc/linux/tools/bpf/bpf_jit_disasm.c (revision 96ed0660)
1a7d7a392SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2a92bb546SJakub Kicinski /*
3a92bb546SJakub Kicinski  * Minimal BPF JIT image disassembler
4a92bb546SJakub Kicinski  *
5a92bb546SJakub Kicinski  * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
6a92bb546SJakub Kicinski  * debugging or verification purposes.
7a92bb546SJakub Kicinski  *
8a92bb546SJakub Kicinski  * To get the disassembly of the JIT code, do the following:
9a92bb546SJakub Kicinski  *
10a92bb546SJakub Kicinski  *  1) `echo 2 > /proc/sys/net/core/bpf_jit_enable`
11a92bb546SJakub Kicinski  *  2) Load a BPF filter (e.g. `tcpdump -p -n -s 0 -i eth1 host 192.168.20.0/24`)
12a92bb546SJakub Kicinski  *  3) Run e.g. `bpf_jit_disasm -o` to read out the last JIT code
13a92bb546SJakub Kicinski  *
14a92bb546SJakub Kicinski  * Copyright 2013 Daniel Borkmann <borkmann@redhat.com>
15a92bb546SJakub Kicinski  */
16a92bb546SJakub Kicinski 
17a92bb546SJakub Kicinski #include <stdint.h>
18a92bb546SJakub Kicinski #include <stdio.h>
19a92bb546SJakub Kicinski #include <stdlib.h>
20a92bb546SJakub Kicinski #include <assert.h>
21a92bb546SJakub Kicinski #include <unistd.h>
22a92bb546SJakub Kicinski #include <string.h>
23a92bb546SJakub Kicinski #include <bfd.h>
24a92bb546SJakub Kicinski #include <dis-asm.h>
25a92bb546SJakub Kicinski #include <regex.h>
26a92bb546SJakub Kicinski #include <fcntl.h>
27a92bb546SJakub Kicinski #include <sys/klog.h>
28a92bb546SJakub Kicinski #include <sys/types.h>
29a92bb546SJakub Kicinski #include <sys/stat.h>
30cdc89c91SPrashant Bhole #include <limits.h>
31*96ed0660SAndres Freund #include <tools/dis-asm-compat.h>
32a92bb546SJakub Kicinski 
33a92bb546SJakub Kicinski #define CMD_ACTION_SIZE_BUFFER		10
34a92bb546SJakub Kicinski #define CMD_ACTION_READ_ALL		3
35a92bb546SJakub Kicinski 
get_exec_path(char * tpath,size_t size)36a92bb546SJakub Kicinski static void get_exec_path(char *tpath, size_t size)
37a92bb546SJakub Kicinski {
38a92bb546SJakub Kicinski 	char *path;
39a92bb546SJakub Kicinski 	ssize_t len;
40a92bb546SJakub Kicinski 
41a92bb546SJakub Kicinski 	snprintf(tpath, size, "/proc/%d/exe", (int) getpid());
42a92bb546SJakub Kicinski 	tpath[size - 1] = 0;
43a92bb546SJakub Kicinski 
44a92bb546SJakub Kicinski 	path = strdup(tpath);
45a92bb546SJakub Kicinski 	assert(path);
46a92bb546SJakub Kicinski 
47a92bb546SJakub Kicinski 	len = readlink(path, tpath, size);
48a92bb546SJakub Kicinski 	tpath[len] = 0;
49a92bb546SJakub Kicinski 
50a92bb546SJakub Kicinski 	free(path);
51a92bb546SJakub Kicinski }
52a92bb546SJakub Kicinski 
get_asm_insns(uint8_t * image,size_t len,int opcodes)53a92bb546SJakub Kicinski static void get_asm_insns(uint8_t *image, size_t len, int opcodes)
54a92bb546SJakub Kicinski {
55a92bb546SJakub Kicinski 	int count, i, pc = 0;
56cdc89c91SPrashant Bhole 	char tpath[PATH_MAX];
57a92bb546SJakub Kicinski 	struct disassemble_info info;
58a92bb546SJakub Kicinski 	disassembler_ftype disassemble;
59a92bb546SJakub Kicinski 	bfd *bfdf;
60a92bb546SJakub Kicinski 
61a92bb546SJakub Kicinski 	memset(tpath, 0, sizeof(tpath));
62a92bb546SJakub Kicinski 	get_exec_path(tpath, sizeof(tpath));
63a92bb546SJakub Kicinski 
64a92bb546SJakub Kicinski 	bfdf = bfd_openr(tpath, NULL);
65a92bb546SJakub Kicinski 	assert(bfdf);
66a92bb546SJakub Kicinski 	assert(bfd_check_format(bfdf, bfd_object));
67a92bb546SJakub Kicinski 
68*96ed0660SAndres Freund 	init_disassemble_info_compat(&info, stdout,
69*96ed0660SAndres Freund 				     (fprintf_ftype) fprintf,
70*96ed0660SAndres Freund 				     fprintf_styled);
71a92bb546SJakub Kicinski 	info.arch = bfd_get_arch(bfdf);
72a92bb546SJakub Kicinski 	info.mach = bfd_get_mach(bfdf);
73a92bb546SJakub Kicinski 	info.buffer = image;
74a92bb546SJakub Kicinski 	info.buffer_length = len;
75a92bb546SJakub Kicinski 
76a92bb546SJakub Kicinski 	disassemble_init_for_target(&info);
77a92bb546SJakub Kicinski 
78fb982666SRoman Gushchin #ifdef DISASM_FOUR_ARGS_SIGNATURE
79fb982666SRoman Gushchin 	disassemble = disassembler(info.arch,
80fb982666SRoman Gushchin 				   bfd_big_endian(bfdf),
81fb982666SRoman Gushchin 				   info.mach,
82fb982666SRoman Gushchin 				   bfdf);
83fb982666SRoman Gushchin #else
84a92bb546SJakub Kicinski 	disassemble = disassembler(bfdf);
85fb982666SRoman Gushchin #endif
86a92bb546SJakub Kicinski 	assert(disassemble);
87a92bb546SJakub Kicinski 
88a92bb546SJakub Kicinski 	do {
89a92bb546SJakub Kicinski 		printf("%4x:\t", pc);
90a92bb546SJakub Kicinski 
91a92bb546SJakub Kicinski 		count = disassemble(pc, &info);
92a92bb546SJakub Kicinski 
93a92bb546SJakub Kicinski 		if (opcodes) {
94a92bb546SJakub Kicinski 			printf("\n\t");
95a92bb546SJakub Kicinski 			for (i = 0; i < count; ++i)
96a92bb546SJakub Kicinski 				printf("%02x ", (uint8_t) image[pc + i]);
97a92bb546SJakub Kicinski 		}
98a92bb546SJakub Kicinski 		printf("\n");
99a92bb546SJakub Kicinski 
100a92bb546SJakub Kicinski 		pc += count;
101a92bb546SJakub Kicinski 	} while(count > 0 && pc < len);
102a92bb546SJakub Kicinski 
103a92bb546SJakub Kicinski 	bfd_close(bfdf);
104a92bb546SJakub Kicinski }
105a92bb546SJakub Kicinski 
get_klog_buff(unsigned int * klen)106a92bb546SJakub Kicinski static char *get_klog_buff(unsigned int *klen)
107a92bb546SJakub Kicinski {
108a92bb546SJakub Kicinski 	int ret, len;
109a92bb546SJakub Kicinski 	char *buff;
110a92bb546SJakub Kicinski 
111a92bb546SJakub Kicinski 	len = klogctl(CMD_ACTION_SIZE_BUFFER, NULL, 0);
112a92bb546SJakub Kicinski 	if (len < 0)
113a92bb546SJakub Kicinski 		return NULL;
114a92bb546SJakub Kicinski 
115a92bb546SJakub Kicinski 	buff = malloc(len);
116a92bb546SJakub Kicinski 	if (!buff)
117a92bb546SJakub Kicinski 		return NULL;
118a92bb546SJakub Kicinski 
119a92bb546SJakub Kicinski 	ret = klogctl(CMD_ACTION_READ_ALL, buff, len);
120a92bb546SJakub Kicinski 	if (ret < 0) {
121a92bb546SJakub Kicinski 		free(buff);
122a92bb546SJakub Kicinski 		return NULL;
123a92bb546SJakub Kicinski 	}
124a92bb546SJakub Kicinski 
125a92bb546SJakub Kicinski 	*klen = ret;
126a92bb546SJakub Kicinski 	return buff;
127a92bb546SJakub Kicinski }
128a92bb546SJakub Kicinski 
get_flog_buff(const char * file,unsigned int * klen)129a92bb546SJakub Kicinski static char *get_flog_buff(const char *file, unsigned int *klen)
130a92bb546SJakub Kicinski {
131a92bb546SJakub Kicinski 	int fd, ret, len;
132a92bb546SJakub Kicinski 	struct stat fi;
133a92bb546SJakub Kicinski 	char *buff;
134a92bb546SJakub Kicinski 
135a92bb546SJakub Kicinski 	fd = open(file, O_RDONLY);
136a92bb546SJakub Kicinski 	if (fd < 0)
137a92bb546SJakub Kicinski 		return NULL;
138a92bb546SJakub Kicinski 
139a92bb546SJakub Kicinski 	ret = fstat(fd, &fi);
140a92bb546SJakub Kicinski 	if (ret < 0 || !S_ISREG(fi.st_mode))
141a92bb546SJakub Kicinski 		goto out;
142a92bb546SJakub Kicinski 
143a92bb546SJakub Kicinski 	len = fi.st_size + 1;
144a92bb546SJakub Kicinski 	buff = malloc(len);
145a92bb546SJakub Kicinski 	if (!buff)
146a92bb546SJakub Kicinski 		goto out;
147a92bb546SJakub Kicinski 
148a92bb546SJakub Kicinski 	memset(buff, 0, len);
149a92bb546SJakub Kicinski 	ret = read(fd, buff, len - 1);
150a92bb546SJakub Kicinski 	if (ret <= 0)
151a92bb546SJakub Kicinski 		goto out_free;
152a92bb546SJakub Kicinski 
153a92bb546SJakub Kicinski 	close(fd);
154a92bb546SJakub Kicinski 	*klen = ret;
155a92bb546SJakub Kicinski 	return buff;
156a92bb546SJakub Kicinski out_free:
157a92bb546SJakub Kicinski 	free(buff);
158a92bb546SJakub Kicinski out:
159a92bb546SJakub Kicinski 	close(fd);
160a92bb546SJakub Kicinski 	return NULL;
161a92bb546SJakub Kicinski }
162a92bb546SJakub Kicinski 
get_log_buff(const char * file,unsigned int * klen)163a92bb546SJakub Kicinski static char *get_log_buff(const char *file, unsigned int *klen)
164a92bb546SJakub Kicinski {
165a92bb546SJakub Kicinski 	return file ? get_flog_buff(file, klen) : get_klog_buff(klen);
166a92bb546SJakub Kicinski }
167a92bb546SJakub Kicinski 
put_log_buff(char * buff)168a92bb546SJakub Kicinski static void put_log_buff(char *buff)
169a92bb546SJakub Kicinski {
170a92bb546SJakub Kicinski 	free(buff);
171a92bb546SJakub Kicinski }
172a92bb546SJakub Kicinski 
get_last_jit_image(char * haystack,size_t hlen,unsigned int * ilen)173a92bb546SJakub Kicinski static uint8_t *get_last_jit_image(char *haystack, size_t hlen,
174a92bb546SJakub Kicinski 				   unsigned int *ilen)
175a92bb546SJakub Kicinski {
176a92bb546SJakub Kicinski 	char *ptr, *pptr, *tmp;
177a92bb546SJakub Kicinski 	off_t off = 0;
178b223e3b4SDan Carpenter 	unsigned int proglen;
179b223e3b4SDan Carpenter 	int ret, flen, pass, ulen = 0;
180a92bb546SJakub Kicinski 	regmatch_t pmatch[1];
181a92bb546SJakub Kicinski 	unsigned long base;
182a92bb546SJakub Kicinski 	regex_t regex;
183a92bb546SJakub Kicinski 	uint8_t *image;
184a92bb546SJakub Kicinski 
185a92bb546SJakub Kicinski 	if (hlen == 0)
186a92bb546SJakub Kicinski 		return NULL;
187a92bb546SJakub Kicinski 
188a92bb546SJakub Kicinski 	ret = regcomp(&regex, "flen=[[:alnum:]]+ proglen=[[:digit:]]+ "
189a92bb546SJakub Kicinski 		      "pass=[[:digit:]]+ image=[[:xdigit:]]+", REG_EXTENDED);
190a92bb546SJakub Kicinski 	assert(ret == 0);
191a92bb546SJakub Kicinski 
192a92bb546SJakub Kicinski 	ptr = haystack;
193a92bb546SJakub Kicinski 	memset(pmatch, 0, sizeof(pmatch));
194a92bb546SJakub Kicinski 
195a92bb546SJakub Kicinski 	while (1) {
196a92bb546SJakub Kicinski 		ret = regexec(&regex, ptr, 1, pmatch, 0);
197a92bb546SJakub Kicinski 		if (ret == 0) {
198a92bb546SJakub Kicinski 			ptr += pmatch[0].rm_eo;
199a92bb546SJakub Kicinski 			off += pmatch[0].rm_eo;
200a92bb546SJakub Kicinski 			assert(off < hlen);
201a92bb546SJakub Kicinski 		} else
202a92bb546SJakub Kicinski 			break;
203a92bb546SJakub Kicinski 	}
204a92bb546SJakub Kicinski 
205a92bb546SJakub Kicinski 	ptr = haystack + off - (pmatch[0].rm_eo - pmatch[0].rm_so);
206b223e3b4SDan Carpenter 	ret = sscanf(ptr, "flen=%d proglen=%u pass=%d image=%lx",
207a92bb546SJakub Kicinski 		     &flen, &proglen, &pass, &base);
208a92bb546SJakub Kicinski 	if (ret != 4) {
209a92bb546SJakub Kicinski 		regfree(&regex);
210a92bb546SJakub Kicinski 		return NULL;
211a92bb546SJakub Kicinski 	}
212a92bb546SJakub Kicinski 	if (proglen > 1000000) {
213a92bb546SJakub Kicinski 		printf("proglen of %d too big, stopping\n", proglen);
214a92bb546SJakub Kicinski 		return NULL;
215a92bb546SJakub Kicinski 	}
216a92bb546SJakub Kicinski 
217a92bb546SJakub Kicinski 	image = malloc(proglen);
218a92bb546SJakub Kicinski 	if (!image) {
219a92bb546SJakub Kicinski 		printf("Out of memory\n");
220a92bb546SJakub Kicinski 		return NULL;
221a92bb546SJakub Kicinski 	}
222a92bb546SJakub Kicinski 	memset(image, 0, proglen);
223a92bb546SJakub Kicinski 
224a92bb546SJakub Kicinski 	tmp = ptr = haystack + off;
225a92bb546SJakub Kicinski 	while ((ptr = strtok(tmp, "\n")) != NULL && ulen < proglen) {
226a92bb546SJakub Kicinski 		tmp = NULL;
227a92bb546SJakub Kicinski 		if (!strstr(ptr, "JIT code"))
228a92bb546SJakub Kicinski 			continue;
229a92bb546SJakub Kicinski 		pptr = ptr;
230a92bb546SJakub Kicinski 		while ((ptr = strstr(pptr, ":")))
231a92bb546SJakub Kicinski 			pptr = ptr + 1;
232a92bb546SJakub Kicinski 		ptr = pptr;
233a92bb546SJakub Kicinski 		do {
234a92bb546SJakub Kicinski 			image[ulen++] = (uint8_t) strtoul(pptr, &pptr, 16);
235a92bb546SJakub Kicinski 			if (ptr == pptr) {
236a92bb546SJakub Kicinski 				ulen--;
237a92bb546SJakub Kicinski 				break;
238a92bb546SJakub Kicinski 			}
239a92bb546SJakub Kicinski 			if (ulen >= proglen)
240a92bb546SJakub Kicinski 				break;
241a92bb546SJakub Kicinski 			ptr = pptr;
242a92bb546SJakub Kicinski 		} while (1);
243a92bb546SJakub Kicinski 	}
244a92bb546SJakub Kicinski 
245a92bb546SJakub Kicinski 	assert(ulen == proglen);
246b223e3b4SDan Carpenter 	printf("%u bytes emitted from JIT compiler (pass:%d, flen:%d)\n",
247a92bb546SJakub Kicinski 	       proglen, pass, flen);
248a92bb546SJakub Kicinski 	printf("%lx + <x>:\n", base);
249a92bb546SJakub Kicinski 
250a92bb546SJakub Kicinski 	regfree(&regex);
251a92bb546SJakub Kicinski 	*ilen = ulen;
252a92bb546SJakub Kicinski 	return image;
253a92bb546SJakub Kicinski }
254a92bb546SJakub Kicinski 
usage(void)255a92bb546SJakub Kicinski static void usage(void)
256a92bb546SJakub Kicinski {
257a92bb546SJakub Kicinski 	printf("Usage: bpf_jit_disasm [...]\n");
258a92bb546SJakub Kicinski 	printf("       -o          Also display related opcodes (default: off).\n");
259a92bb546SJakub Kicinski 	printf("       -O <file>   Write binary image of code to file, don't disassemble to stdout.\n");
260a92bb546SJakub Kicinski 	printf("       -f <file>   Read last image dump from file or stdin (default: klog).\n");
261a92bb546SJakub Kicinski 	printf("       -h          Display this help.\n");
262a92bb546SJakub Kicinski }
263a92bb546SJakub Kicinski 
main(int argc,char ** argv)264a92bb546SJakub Kicinski int main(int argc, char **argv)
265a92bb546SJakub Kicinski {
266a92bb546SJakub Kicinski 	unsigned int len, klen, opt, opcodes = 0;
267a92bb546SJakub Kicinski 	char *kbuff, *file = NULL;
268a92bb546SJakub Kicinski 	char *ofile = NULL;
269a92bb546SJakub Kicinski 	int ofd;
270a92bb546SJakub Kicinski 	ssize_t nr;
271a92bb546SJakub Kicinski 	uint8_t *pos;
272a92bb546SJakub Kicinski 	uint8_t *image = NULL;
273a92bb546SJakub Kicinski 
274a92bb546SJakub Kicinski 	while ((opt = getopt(argc, argv, "of:O:")) != -1) {
275a92bb546SJakub Kicinski 		switch (opt) {
276a92bb546SJakub Kicinski 		case 'o':
277a92bb546SJakub Kicinski 			opcodes = 1;
278a92bb546SJakub Kicinski 			break;
279a92bb546SJakub Kicinski 		case 'O':
280a92bb546SJakub Kicinski 			ofile = optarg;
281a92bb546SJakub Kicinski 			break;
282a92bb546SJakub Kicinski 		case 'f':
283a92bb546SJakub Kicinski 			file = optarg;
284a92bb546SJakub Kicinski 			break;
285a92bb546SJakub Kicinski 		default:
286a92bb546SJakub Kicinski 			usage();
287a92bb546SJakub Kicinski 			return -1;
288a92bb546SJakub Kicinski 		}
289a92bb546SJakub Kicinski 	}
290a92bb546SJakub Kicinski 
291a92bb546SJakub Kicinski 	bfd_init();
292a92bb546SJakub Kicinski 
293a92bb546SJakub Kicinski 	kbuff = get_log_buff(file, &klen);
294a92bb546SJakub Kicinski 	if (!kbuff) {
295a92bb546SJakub Kicinski 		fprintf(stderr, "Could not retrieve log buffer!\n");
296a92bb546SJakub Kicinski 		return -1;
297a92bb546SJakub Kicinski 	}
298a92bb546SJakub Kicinski 
299a92bb546SJakub Kicinski 	image = get_last_jit_image(kbuff, klen, &len);
300a92bb546SJakub Kicinski 	if (!image) {
301a92bb546SJakub Kicinski 		fprintf(stderr, "No JIT image found!\n");
302a92bb546SJakub Kicinski 		goto done;
303a92bb546SJakub Kicinski 	}
304a92bb546SJakub Kicinski 	if (!ofile) {
305a92bb546SJakub Kicinski 		get_asm_insns(image, len, opcodes);
306a92bb546SJakub Kicinski 		goto done;
307a92bb546SJakub Kicinski 	}
308a92bb546SJakub Kicinski 
309a92bb546SJakub Kicinski 	ofd = open(ofile, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
310a92bb546SJakub Kicinski 	if (ofd < 0) {
311a92bb546SJakub Kicinski 		fprintf(stderr, "Could not open file %s for writing: ", ofile);
312a92bb546SJakub Kicinski 		perror(NULL);
313a92bb546SJakub Kicinski 		goto done;
314a92bb546SJakub Kicinski 	}
315a92bb546SJakub Kicinski 	pos = image;
316a92bb546SJakub Kicinski 	do {
317a92bb546SJakub Kicinski 		nr = write(ofd, pos, len);
318a92bb546SJakub Kicinski 		if (nr < 0) {
319a92bb546SJakub Kicinski 			fprintf(stderr, "Could not write data to %s: ", ofile);
320a92bb546SJakub Kicinski 			perror(NULL);
321a92bb546SJakub Kicinski 			goto done;
322a92bb546SJakub Kicinski 		}
323a92bb546SJakub Kicinski 		len -= nr;
324a92bb546SJakub Kicinski 		pos += nr;
325a92bb546SJakub Kicinski 	} while (len);
326a92bb546SJakub Kicinski 	close(ofd);
327a92bb546SJakub Kicinski 
328a92bb546SJakub Kicinski done:
329a92bb546SJakub Kicinski 	put_log_buff(kbuff);
330a92bb546SJakub Kicinski 	free(image);
331a92bb546SJakub Kicinski 	return 0;
332a92bb546SJakub Kicinski }
333