Skip to content

Commit e306e2c

Browse files
Daniel Borkmanndavem330
authored andcommitted
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>
1 parent b34870f commit e306e2c

File tree

3 files changed

+220
-5
lines changed

3 files changed

+220
-5
lines changed

tools/Makefile

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ help:
1212
@echo ' turbostat - Intel CPU idle stats and freq reporting tool'
1313
@echo ' usb - USB testing tools'
1414
@echo ' virtio - vhost test module'
15+
@echo ' net - misc networking tools'
1516
@echo ' vm - misc vm tools'
1617
@echo ' x86_energy_perf_policy - Intel energy policy tool'
1718
@echo ''
@@ -34,7 +35,7 @@ help:
3435
cpupower: FORCE
3536
$(call descend,power/$@)
3637

37-
cgroup firewire lguest perf usb virtio vm: FORCE
38+
cgroup firewire lguest perf usb virtio vm net: FORCE
3839
$(call descend,$@)
3940

4041
selftests: FORCE
@@ -46,7 +47,7 @@ turbostat x86_energy_perf_policy: FORCE
4647
cpupower_install:
4748
$(call descend,power/$(@:_install=),install)
4849

49-
cgroup_install firewire_install lguest_install perf_install usb_install virtio_install vm_install:
50+
cgroup_install firewire_install lguest_install perf_install usb_install virtio_install vm_install net_install:
5051
$(call descend,$(@:_install=),install)
5152

5253
selftests_install:
@@ -57,12 +58,12 @@ turbostat_install x86_energy_perf_policy_install:
5758

5859
install: cgroup_install cpupower_install firewire_install lguest_install \
5960
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
6162

6263
cpupower_clean:
6364
$(call descend,power/cpupower,clean)
6465

65-
cgroup_clean firewire_clean lguest_clean perf_clean usb_clean virtio_clean vm_clean:
66+
cgroup_clean firewire_clean lguest_clean perf_clean usb_clean virtio_clean vm_clean net_clean:
6667
$(call descend,$(@:_clean=),clean)
6768

6869
selftests_clean:
@@ -73,6 +74,6 @@ turbostat_clean x86_energy_perf_policy_clean:
7374

7475
clean: cgroup_clean cpupower_clean firewire_clean lguest_clean perf_clean \
7576
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
7778

7879
.PHONY: FORCE

tools/net/Makefile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
prefix = /usr
2+
3+
CC = gcc
4+
5+
all : bpf_jit_disasm
6+
7+
bpf_jit_disasm : CFLAGS = -Wall -O2
8+
bpf_jit_disasm : LDLIBS = -lopcodes -lbfd -ldl
9+
bpf_jit_disasm : bpf_jit_disasm.o
10+
11+
clean :
12+
rm -rf *.o bpf_jit_disasm
13+
14+
install :
15+
install bpf_jit_disasm $(prefix)/bin/bpf_jit_disasm

tools/net/bpf_jit_disasm.c

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
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+
}

0 commit comments

Comments
 (0)