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(®ex, "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(®ex, 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(®ex);
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(®ex);
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