filter: add minimal BPF JIT image disassembler
[~shefty/rdma-dev.git] / tools / net / bpf_jit_disasm.c
1 /*
2  * Minimal BPF JIT image disassembler
3  *
4  * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
5  * debugging or verification purposes.
6  *
7  * To get the disassembly of the JIT code, do the following:
8  *
9  *  1) `echo 2 > /proc/sys/net/core/bpf_jit_enable`
10  *  2) Load a BPF filter (e.g. `tcpdump -p -n -s 0 -i eth1 host 192.168.20.0/24`)
11  *  3) Run e.g. `bpf_jit_disasm -o` to read out the last JIT code
12  *
13  * Copyright 2013 Daniel Borkmann <borkmann@redhat.com>
14  * Licensed under the GNU General Public License, version 2.0 (GPLv2)
15  */
16
17 #include <stdint.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <assert.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <bfd.h>
24 #include <dis-asm.h>
25 #include <sys/klog.h>
26 #include <sys/types.h>
27 #include <regex.h>
28
29 static void get_exec_path(char *tpath, size_t size)
30 {
31         char *path;
32         ssize_t len;
33
34         snprintf(tpath, size, "/proc/%d/exe", (int) getpid());
35         tpath[size - 1] = 0;
36
37         path = strdup(tpath);
38         assert(path);
39
40         len = readlink(path, tpath, size);
41         tpath[len] = 0;
42
43         free(path);
44 }
45
46 static void get_asm_insns(uint8_t *image, size_t len, unsigned long base,
47                           int opcodes)
48 {
49         int count, i, pc = 0;
50         char tpath[256];
51         struct disassemble_info info;
52         disassembler_ftype disassemble;
53         bfd *bfdf;
54
55         memset(tpath, 0, sizeof(tpath));
56         get_exec_path(tpath, sizeof(tpath));
57
58         bfdf = bfd_openr(tpath, NULL);
59         assert(bfdf);
60         assert(bfd_check_format(bfdf, bfd_object));
61
62         init_disassemble_info(&info, stdout, (fprintf_ftype) fprintf);
63         info.arch = bfd_get_arch(bfdf);
64         info.mach = bfd_get_mach(bfdf);
65         info.buffer = image;
66         info.buffer_length = len;
67
68         disassemble_init_for_target(&info);
69
70         disassemble = disassembler(bfdf);
71         assert(disassemble);
72
73         do {
74                 printf("%4x:\t", pc);
75
76                 count = disassemble(pc, &info);
77
78                 if (opcodes) {
79                         printf("\n\t");
80                         for (i = 0; i < count; ++i)
81                                 printf("%02x ", (uint8_t) image[pc + i]);
82                 }
83                 printf("\n");
84
85                 pc += count;
86         } while(count > 0 && pc < len);
87
88         bfd_close(bfdf);
89 }
90
91 static char *get_klog_buff(int *klen)
92 {
93         int ret, len = klogctl(10, NULL, 0);
94         char *buff = malloc(len);
95
96         assert(buff && klen);
97         ret = klogctl(3, buff, len);
98         assert(ret >= 0);
99         *klen = ret;
100
101         return buff;
102 }
103
104 static void put_klog_buff(char *buff)
105 {
106         free(buff);
107 }
108
109 static int get_last_jit_image(char *haystack, size_t hlen,
110                               uint8_t *image, size_t ilen,
111                               unsigned long *base)
112 {
113         char *ptr, *pptr, *tmp;
114         off_t off = 0;
115         int ret, flen, proglen, pass, ulen = 0;
116         regmatch_t pmatch[1];
117         regex_t regex;
118
119         if (hlen == 0)
120                 return 0;
121
122         ret = regcomp(&regex, "flen=[[:alnum:]]+ proglen=[[:digit:]]+ "
123                       "pass=[[:digit:]]+ image=[[:xdigit:]]+", REG_EXTENDED);
124         assert(ret == 0);
125
126         ptr = haystack;
127         while (1) {
128                 ret = regexec(&regex, ptr, 1, pmatch, 0);
129                 if (ret == 0) {
130                         ptr += pmatch[0].rm_eo;
131                         off += pmatch[0].rm_eo;
132                         assert(off < hlen);
133                 } else
134                         break;
135         }
136
137         ptr = haystack + off - (pmatch[0].rm_eo - pmatch[0].rm_so);
138         ret = sscanf(ptr, "flen=%d proglen=%d pass=%d image=%lx",
139                      &flen, &proglen, &pass, base);
140         if (ret != 4)
141                 return 0;
142
143         tmp = ptr = haystack + off;
144         while ((ptr = strtok(tmp, "\n")) != NULL && ulen < ilen) {
145                 tmp = NULL;
146                 if (!strstr(ptr, "JIT code"))
147                         continue;
148                 pptr = ptr;
149                 while ((ptr = strstr(pptr, ":")))
150                         pptr = ptr + 1;
151                 ptr = pptr;
152                 do {
153                         image[ulen++] = (uint8_t) strtoul(pptr, &pptr, 16);
154                         if (ptr == pptr || ulen >= ilen) {
155                                 ulen--;
156                                 break;
157                         }
158                         ptr = pptr;
159                 } while (1);
160         }
161
162         assert(ulen == proglen);
163         printf("%d bytes emitted from JIT compiler (pass:%d, flen:%d)\n",
164                proglen, pass, flen);
165         printf("%lx + <x>:\n", *base);
166
167         regfree(&regex);
168         return ulen;
169 }
170
171 int main(int argc, char **argv)
172 {
173         int len, klen, opcodes = 0;
174         char *kbuff;
175         unsigned long base;
176         uint8_t image[4096];
177
178         if (argc > 1) {
179                 if (!strncmp("-o", argv[argc - 1], 2)) {
180                         opcodes = 1;
181                 } else {
182                         printf("usage: bpf_jit_disasm [-o: show opcodes]\n");
183                         exit(0);
184                 }
185         }
186
187         bfd_init();
188         memset(image, 0, sizeof(image));
189
190         kbuff = get_klog_buff(&klen);
191
192         len = get_last_jit_image(kbuff, klen, image, sizeof(image), &base);
193         if (len > 0 && base > 0)
194                 get_asm_insns(image, len, base, opcodes);
195
196         put_klog_buff(kbuff);
197
198         return 0;
199 }