aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Borkmann <dborkman@redhat.com>2013-03-20 08:11:47 -0400
committerDavid S. Miller <davem@davemloft.net>2013-03-21 11:35:41 -0400
commite306e2c13b8c214618af0c61acf62a6e42d486de (patch)
tree69e093bc04315734365aa942f6e590039a9ac8b0
parentb34870fc9ff15fe46c4066faeeec437a4e63e2d8 (diff)
filter: add minimal BPF JIT image disassembler
This is a minimal stand-alone user space helper, that allows for debugging or verification of emitted BPF JIT images. This is in particular useful for emitted opcode debugging, since minor bugs in the JIT compiler can be fatal. The disassembler is architecture generic and uses libopcodes and libbfd. How to get to the disassembly, example: 1) `echo 2 > /proc/sys/net/core/bpf_jit_enable` 2) Load a BPF filter (e.g. `tcpdump -p -n -s 0 -i eth1 host 192.168.20.0/24`) 3) Run e.g. `bpf_jit_disasm -o` to disassemble the most recent JIT code output `bpf_jit_disasm -o` will display the related opcodes to a particular instruction as well. Example for x86_64: $ ./bpf_jit_disasm 94 bytes emitted from JIT compiler (pass:3, flen:9) ffffffffa0356000 + <x>: 0: push %rbp 1: mov %rsp,%rbp 4: sub $0x60,%rsp 8: mov %rbx,-0x8(%rbp) c: mov 0x68(%rdi),%r9d 10: sub 0x6c(%rdi),%r9d 14: mov 0xe0(%rdi),%r8 1b: mov $0xc,%esi 20: callq 0xffffffffe0d01b71 25: cmp $0x86dd,%eax 2a: jne 0x000000000000003d 2c: mov $0x14,%esi 31: callq 0xffffffffe0d01b8d 36: cmp $0x6,%eax [...] 5c: leaveq 5d: retq $ ./bpf_jit_disasm -o 94 bytes emitted from JIT compiler (pass:3, flen:9) ffffffffa0356000 + <x>: 0: push %rbp 55 1: mov %rsp,%rbp 48 89 e5 4: sub $0x60,%rsp 48 83 ec 60 8: mov %rbx,-0x8(%rbp) 48 89 5d f8 c: mov 0x68(%rdi),%r9d 44 8b 4f 68 10: sub 0x6c(%rdi),%r9d 44 2b 4f 6c [...] 5c: leaveq c9 5d: retq c3 Signed-off-by: Daniel Borkmann <dborkman@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--tools/Makefile11
-rw-r--r--tools/net/Makefile15
-rw-r--r--tools/net/bpf_jit_disasm.c199
3 files changed, 220 insertions, 5 deletions
diff --git a/tools/Makefile b/tools/Makefile
index fa36565b209d..c73c6357481c 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -12,6 +12,7 @@ help:
12 @echo ' turbostat - Intel CPU idle stats and freq reporting tool' 12 @echo ' turbostat - Intel CPU idle stats and freq reporting tool'
13 @echo ' usb - USB testing tools' 13 @echo ' usb - USB testing tools'
14 @echo ' virtio - vhost test module' 14 @echo ' virtio - vhost test module'
15 @echo ' net - misc networking tools'
15 @echo ' vm - misc vm tools' 16 @echo ' vm - misc vm tools'
16 @echo ' x86_energy_perf_policy - Intel energy policy tool' 17 @echo ' x86_energy_perf_policy - Intel energy policy tool'
17 @echo '' 18 @echo ''
@@ -34,7 +35,7 @@ help:
34cpupower: FORCE 35cpupower: FORCE
35 $(call descend,power/$@) 36 $(call descend,power/$@)
36 37
37cgroup firewire lguest perf usb virtio vm: FORCE 38cgroup firewire lguest perf usb virtio vm net: FORCE
38 $(call descend,$@) 39 $(call descend,$@)
39 40
40selftests: FORCE 41selftests: FORCE
@@ -46,7 +47,7 @@ turbostat x86_energy_perf_policy: FORCE
46cpupower_install: 47cpupower_install:
47 $(call descend,power/$(@:_install=),install) 48 $(call descend,power/$(@:_install=),install)
48 49
49cgroup_install firewire_install lguest_install perf_install usb_install virtio_install vm_install: 50cgroup_install firewire_install lguest_install perf_install usb_install virtio_install vm_install net_install:
50 $(call descend,$(@:_install=),install) 51 $(call descend,$(@:_install=),install)
51 52
52selftests_install: 53selftests_install:
@@ -57,12 +58,12 @@ turbostat_install x86_energy_perf_policy_install:
57 58
58install: cgroup_install cpupower_install firewire_install lguest_install \ 59install: cgroup_install cpupower_install firewire_install lguest_install \
59 perf_install selftests_install turbostat_install usb_install \ 60 perf_install selftests_install turbostat_install usb_install \
60 virtio_install vm_install x86_energy_perf_policy_install 61 virtio_install vm_install net_install x86_energy_perf_policy_install
61 62
62cpupower_clean: 63cpupower_clean:
63 $(call descend,power/cpupower,clean) 64 $(call descend,power/cpupower,clean)
64 65
65cgroup_clean firewire_clean lguest_clean perf_clean usb_clean virtio_clean vm_clean: 66cgroup_clean firewire_clean lguest_clean perf_clean usb_clean virtio_clean vm_clean net_clean:
66 $(call descend,$(@:_clean=),clean) 67 $(call descend,$(@:_clean=),clean)
67 68
68selftests_clean: 69selftests_clean:
@@ -73,6 +74,6 @@ turbostat_clean x86_energy_perf_policy_clean:
73 74
74clean: cgroup_clean cpupower_clean firewire_clean lguest_clean perf_clean \ 75clean: cgroup_clean cpupower_clean firewire_clean lguest_clean perf_clean \
75 selftests_clean turbostat_clean usb_clean virtio_clean \ 76 selftests_clean turbostat_clean usb_clean virtio_clean \
76 vm_clean x86_energy_perf_policy_clean 77 vm_clean net_clean x86_energy_perf_policy_clean
77 78
78.PHONY: FORCE 79.PHONY: FORCE
diff --git a/tools/net/Makefile b/tools/net/Makefile
new file mode 100644
index 000000000000..b4444d53b73f
--- /dev/null
+++ b/tools/net/Makefile
@@ -0,0 +1,15 @@
1prefix = /usr
2
3CC = gcc
4
5all : bpf_jit_disasm
6
7bpf_jit_disasm : CFLAGS = -Wall -O2
8bpf_jit_disasm : LDLIBS = -lopcodes -lbfd -ldl
9bpf_jit_disasm : bpf_jit_disasm.o
10
11clean :
12 rm -rf *.o bpf_jit_disasm
13
14install :
15 install bpf_jit_disasm $(prefix)/bin/bpf_jit_disasm
diff --git a/tools/net/bpf_jit_disasm.c b/tools/net/bpf_jit_disasm.c
new file mode 100644
index 000000000000..cfe0cdcda3de
--- /dev/null
+++ b/tools/net/bpf_jit_disasm.c
@@ -0,0 +1,199 @@
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
29static 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
46static 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
91static 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
104static void put_klog_buff(char *buff)
105{
106 free(buff);
107}
108
109static 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
171int 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}