Merge branch 'drm-next-3.16' of git://people.freedesktop.org/~agd5f/linux into drm...
[sfrench/cifs-2.6.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, int opcodes)
47 {
48         int count, i, pc = 0;
49         char tpath[256];
50         struct disassemble_info info;
51         disassembler_ftype disassemble;
52         bfd *bfdf;
53
54         memset(tpath, 0, sizeof(tpath));
55         get_exec_path(tpath, sizeof(tpath));
56
57         bfdf = bfd_openr(tpath, NULL);
58         assert(bfdf);
59         assert(bfd_check_format(bfdf, bfd_object));
60
61         init_disassemble_info(&info, stdout, (fprintf_ftype) fprintf);
62         info.arch = bfd_get_arch(bfdf);
63         info.mach = bfd_get_mach(bfdf);
64         info.buffer = image;
65         info.buffer_length = len;
66
67         disassemble_init_for_target(&info);
68
69         disassemble = disassembler(bfdf);
70         assert(disassemble);
71
72         do {
73                 printf("%4x:\t", pc);
74
75                 count = disassemble(pc, &info);
76
77                 if (opcodes) {
78                         printf("\n\t");
79                         for (i = 0; i < count; ++i)
80                                 printf("%02x ", (uint8_t) image[pc + i]);
81                 }
82                 printf("\n");
83
84                 pc += count;
85         } while(count > 0 && pc < len);
86
87         bfd_close(bfdf);
88 }
89
90 static char *get_klog_buff(int *klen)
91 {
92         int ret, len = klogctl(10, NULL, 0);
93         char *buff = malloc(len);
94
95         assert(buff && klen);
96         ret = klogctl(3, buff, len);
97         assert(ret >= 0);
98         *klen = ret;
99
100         return buff;
101 }
102
103 static void put_klog_buff(char *buff)
104 {
105         free(buff);
106 }
107
108 static int get_last_jit_image(char *haystack, size_t hlen,
109                               uint8_t *image, size_t ilen)
110 {
111         char *ptr, *pptr, *tmp;
112         off_t off = 0;
113         int ret, flen, proglen, pass, ulen = 0;
114         regmatch_t pmatch[1];
115         unsigned long base;
116         regex_t regex;
117
118         if (hlen == 0)
119                 return 0;
120
121         ret = regcomp(&regex, "flen=[[:alnum:]]+ proglen=[[:digit:]]+ "
122                       "pass=[[:digit:]]+ image=[[:xdigit:]]+", REG_EXTENDED);
123         assert(ret == 0);
124
125         ptr = haystack;
126         while (1) {
127                 ret = regexec(&regex, ptr, 1, pmatch, 0);
128                 if (ret == 0) {
129                         ptr += pmatch[0].rm_eo;
130                         off += pmatch[0].rm_eo;
131                         assert(off < hlen);
132                 } else
133                         break;
134         }
135
136         ptr = haystack + off - (pmatch[0].rm_eo - pmatch[0].rm_so);
137         ret = sscanf(ptr, "flen=%d proglen=%d pass=%d image=%lx",
138                      &flen, &proglen, &pass, &base);
139         if (ret != 4)
140                 return 0;
141
142         tmp = ptr = haystack + off;
143         while ((ptr = strtok(tmp, "\n")) != NULL && ulen < ilen) {
144                 tmp = NULL;
145                 if (!strstr(ptr, "JIT code"))
146                         continue;
147                 pptr = ptr;
148                 while ((ptr = strstr(pptr, ":")))
149                         pptr = ptr + 1;
150                 ptr = pptr;
151                 do {
152                         image[ulen++] = (uint8_t) strtoul(pptr, &pptr, 16);
153                         if (ptr == pptr || ulen >= ilen) {
154                                 ulen--;
155                                 break;
156                         }
157                         ptr = pptr;
158                 } while (1);
159         }
160
161         assert(ulen == proglen);
162         printf("%d bytes emitted from JIT compiler (pass:%d, flen:%d)\n",
163                proglen, pass, flen);
164         printf("%lx + <x>:\n", base);
165
166         regfree(&regex);
167         return ulen;
168 }
169
170 int main(int argc, char **argv)
171 {
172         int len, klen, opcodes = 0;
173         char *kbuff;
174         static uint8_t image[32768];
175
176         if (argc > 1) {
177                 if (!strncmp("-o", argv[argc - 1], 2)) {
178                         opcodes = 1;
179                 } else {
180                         printf("usage: bpf_jit_disasm [-o: show opcodes]\n");
181                         exit(0);
182                 }
183         }
184
185         bfd_init();
186         memset(image, 0, sizeof(image));
187
188         kbuff = get_klog_buff(&klen);
189
190         len = get_last_jit_image(kbuff, klen, image, sizeof(image));
191         if (len > 0)
192                 get_asm_insns(image, len, opcodes);
193
194         put_klog_buff(kbuff);
195
196         return 0;
197 }