目前,我知道的查看 bpf prog 反汇编的办法有:
使用 bpftool prog dump jited
;使用 gdb -q -c /proc/kcore -ex 'disas/r 0xffffffffc00a6188,+0x143' -ex 'quit'
;使用 drgn contrib/bpf_inspec.py
。
这些办法各有优劣。
1. bpftool prog dump jited
bpftool
查看反汇编的办法如下:
# bpftool p d j i 23 linum opcodes
int fentry_xdp(unsigned long long * ctx):
bpf_prog_52d82017b7a0a424_fentry_xdp:
; int BPF_PROG(fentry_xdp, struct xdp_buff *xdp) [file:./xdp.c line_num:30 line_col:0]
0: nopl (%rax,%rax)
0f 1f 44 00 00
5: nop
66 90
7: pushq %rbp
55
8: movq %rsp, %rbp
48 89 e5
b: subq $24, %rsp
48 81 ec 18 00 00 00
12: pushq %rbx
53
13: pushq %r13
41 55
15: pushq %r14
41 56
17: pushq %r15
41 57
19: movq %rdi, %rbx
48 89 fb
1c: xorl %edi, %edi
31 ff
; int BPF_PROG(fentry_xdp, struct xdp_buff *xdp) [file:./xdp.c line_num:30 line_col:5]
1e: movq (%rbx), %r14
4c 8b 73 00
22: movq %r14, %rdx
4c 89 f2
25: addq %rdi, %rdx
48 01 fa
28: movq %rbp, %rdi
48 89 ef
; [file:./xdp.c line_num:0 line_col:0]
2b: addq $-16, %rdi
48 83 c7 f0
; struct ethhdr *eth = (void *)(long)BPF_CORE_READ(xdp, data); [file:./xdp.c line_num:15 line_col:40]
2f: movl $8, %esi
be 08 00 00 00
34: callq 0xffffffffec9de688
...
; bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &ev, sizeof(ev)); [file:./../headers/lib_xdp_tc.h line_num:41 line_col:5]
118: movq %rbx, %rdi
48 89 df
11b: movabsq $-105200468817920, %rsi
48 be 00 08 6d 1b 52 a0 ff ff
125: movl $4294967295, %edx
ba ff ff ff ff
12a: movq %r14, %rcx
4c 89 f1
12d: movl $12, %r8d
41 b8 0c 00 00 00
133: callq 0xffffffffec9dff08
e8 d0 fd 9d ec
; int BPF_PROG(fentry_xdp, struct xdp_buff *xdp) [file:./xdp.c line_num:30 line_col:5]
138: xorl %eax, %eax
31 c0
13a: popq %r15
41 5f
13c: popq %r14
41 5e
13e: popq %r13
41 5d
140: popq %rbx
5b
141: leave
c9
142: retq
c3
143: int3
cc
通过指定 linum
和 opcodes
,可以看到行号信息、源代码和原始的机器码。
P.S. bpftool
里计算的 callq
地址是错误的:Wrong callq address displayed[1]。
2. gdb -q -c /proc/kcore -ex 'disas/r 0xffffffffc00a6188,+0x143' -ex 'quit'
gdb
查看反汇编的办法如下:
# grep fentry_xdp /proc/kallsyms
ffffffffc00a6188 t bpf_prog_52d82017b7a0a424_fentry_xdp [bpf]
# gdb -q -c /proc/kcore -ex 'disas/r 0xffffffffc00a6188,+0x143' -ex 'quit'
Dump of assembler code from 0xffffffffc00a6188 to 0xffffffffc00a62cb:
0xffffffffc01062d8: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
0xffffffffc01062dd: 66 90 xchg %ax,%ax
0xffffffffc01062df: 55 push %rbp
0xffffffffc01062e0: 48 89 e5 mov %rsp,%rbp
0xffffffffc01062e3: 48 81 ec 18 00 00 00 sub $0x18,%rsp
0xffffffffc01062ea: 53 push %rbx
0xffffffffc01062eb: 41 55 push %r13
0xffffffffc01062ed: 41 56 push %r14
0xffffffffc01062ef: 41 57 push %r15
0xffffffffc01062f1: 48 89 fb mov %rdi,%rbx
0xffffffffc01062f4: 31 ff xor %edi,%edi
0xffffffffc01062f6: 4c 8b 73 00 mov 0x0(%rbx),%r14
0xffffffffc01062fa: 4c 89 f2 mov %r14,%rdx
0xffffffffc01062fd: 48 01 fa add %rdi,%rdx
0xffffffffc0106300: 48 89 ef mov %rbp,%rdi
0xffffffffc0106303: 48 83 c7 f0 add $0xfffffffffffffff0,%rdi
0xffffffffc0106307: be 08 00 00 00 mov $0x8,%esi
0xffffffffc010630c: e8 4f e6 9d ec call 0xffffffffacae4960
...
0xffffffffc01063f0: 48 89 df mov %rbx,%rdi
0xffffffffc01063f3: 48 be 00 08 6d 1b 52 a0 ff ff movabs $0xffffa0521b6d0800,%rsi
0xffffffffc01063fd: ba ff ff ff ff mov $0xffffffff,%edx
0xffffffffc0106402: 4c 89 f1 mov %r14,%rcx
0xffffffffc0106405: 41 b8 0c 00 00 00 mov $0xc,%r8d
0xffffffffc010640b: e8 d0 fd 9d ec call 0xffffffffacae61e0
0xffffffffc0106410: 31 c0 xor %eax,%eax
0xffffffffc0106412: 41 5f pop %r15
0xffffffffc0106414: 41 5e pop %r14
0xffffffffc0106416: 41 5d pop %r13
0xffffffffc0106418: 5b pop %rbx
0xffffffffc0106419: c9 leave
0xffffffffc010641a: c3 ret
3. drgn contrib/bpf_inspec.py
在 drgn
的 PR Proposal: contrib/bpf_inspect.py: disas bpf prog with capstone[2] 里,可以查看 bpf prog 的反汇编:
# drgn ./contrib/bpf_inspect.py i
For help, type help(drgn).
>>> import drgn
>>> from drgn import NULL, Object, cast, container_of, execscript, offsetof, reinterpret, sizeof, stack_trace
>>> from drgn.helpers.common import *
>>> from drgn.helpers.linux import *
>>> p = get_bpf_prog_by_id(25)
>>> print("\n".join(p.disas()))
0xffffffffc01062d8: 0f 1f 44 00 00 nop dword ptr [rax + rax]
0xffffffffc01062dd: 66 90 nop
0xffffffffc01062df: 55 push rbp
0xffffffffc01062e0: 48 89 e5 mov rbp, rsp
0xffffffffc01062e3: 48 81 ec 18 00 00 00 sub rsp, 0x18
0xffffffffc01062ea: 53 push rbx
0xffffffffc01062eb: 41 55 push r13
0xffffffffc01062ed: 41 56 push r14
0xffffffffc01062ef: 41 57 push r15
0xffffffffc01062f1: 48 89 fb mov rbx, rdi
0xffffffffc01062f4: 31 ff xor edi, edi
0xffffffffc01062f6: 4c 8b 73 00 mov r14, qword ptr [rbx]
0xffffffffc01062fa: 4c 89 f2 mov rdx, r14
0xffffffffc01062fd: 48 01 fa add rdx, rdi
0xffffffffc0106300: 48 89 ef mov rdi, rbp
0xffffffffc0106303: 48 83 c7 f0 add rdi, -0x10
0xffffffffc0106307: be 08 00 00 00 mov esi, 8
0xffffffffc010630c: e8 4f e6 9d ec call 0xffffffffacae4960
...
0xffffffffc01063f0: 48 89 df mov rdi, rbx
0xffffffffc01063f3: 48 be 00 08 6d 1b 52 a0 ff ff movabs rsi, 0xffffa0521b6d0800
0xffffffffc01063fd: ba ff ff ff ff mov edx, 0xffffffff
0xffffffffc0106402: 4c 89 f1 mov rcx, r14
0xffffffffc0106405: 41 b8 0c 00 00 00 mov r8d, 0xc
0xffffffffc010640b: e8 d0 fd 9d ec call 0xffffffffacae61e0
0xffffffffc0106410: 31 c0 xor eax, eax
0xffffffffc0106412: 41 5f pop r15
0xffffffffc0106414: 41 5e pop r14
0xffffffffc0106416: 41 5d pop r13
0xffffffffc0106418: 5b pop rbx
0xffffffffc0106419: c9 leave
0xffffffffc010641a: c3 ret
0xffffffffc010641b: cc int3
好吧,drgn
的效果跟 gdb
的效果差不多;不过,却要依赖内核 dbgsym 文件。
自制查看 bpf prog 反汇编的工具
在开发使用 LBR 跟踪 bpf prog 内部一些细节的工具 bpflbr[3] 的过程中,顺手将查看 bpf prog 反汇编的功能先开发出来了:
# ./bpflbr -p 25 --dump-jited
; ./xdp.c:30:0 int BPF_PROG(fentry_xdp, struct xdp_buff *xdp)
0xffffffffc01062d8: 0f 1f 44 00 00 nopl (%rax, %rax)
0xffffffffc01062dd: 66 90 nop
0xffffffffc01062df: 55 pushq %rbp
0xffffffffc01062e0: 48 89 e5 movq %rsp, %rbp
0xffffffffc01062e3: 48 81 ec 18 00 00 00 subq $0x18, %rsp
0xffffffffc01062ea: 53 pushq %rbx
0xffffffffc01062eb: 41 55 pushq %r13
0xffffffffc01062ed: 41 56 pushq %r14
0xffffffffc01062ef: 41 57 pushq %r15
0xffffffffc01062f1: 48 89 fb movq %rdi, %rbx
0xffffffffc01062f4: 31 ff xorl %edi, %edi
; ./xdp.c:30:5 int BPF_PROG(fentry_xdp, struct xdp_buff *xdp)
0xffffffffc01062f6: 4c 8b 73 00 movq (%rbx), %r14
0xffffffffc01062fa: 4c 89 f2 movq %r14, %rdx
0xffffffffc01062fd: 48 01 fa addq %rdi, %rdx
0xffffffffc0106300: 48 89 ef movq %rbp, %rdi
; ./xdp.c:0:0
0xffffffffc0106303: 48 83 c7 f0 addq $-0x10, %rdi
; ./xdp.c:15:40 struct ethhdr *eth = (void *)(long)BPF_CORE_READ(xdp, data);
0xffffffffc0106307: be 08 00 00 00 movl $8, %esi
0xffffffffc010630c: e8 4f e6 9d ec callq 0xffffffffacae4960
...
; ./../headers/lib_xdp_tc.h:41:5 bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &ev, sizeof(ev));
0xffffffffc01063f0: 48 89 df movq %rbx, %rdi
0xffffffffc01063f3: 48 be 00 08 6d 1b 52 a0 ff ff movabsq $0xffffa0521b6d0800, %rsi
0xffffffffc01063fd: ba ff ff ff ff movl $0xffffffff, %edx
0xffffffffc0106402: 4c 89 f1 movq %r14, %rcx
0xffffffffc0106405: 41 b8 0c 00 00 00 movl $0xc, %r8d
0xffffffffc010640b: e8 d0 fd 9d ec callq 0xffffffffacae61e0
; ./xdp.c:30:5 int BPF_PROG(fentry_xdp, struct xdp_buff *xdp)
0xffffffffc0106410: 31 c0 xorl %eax, %eax
0xffffffffc0106412: 41 5f popq %r15
0xffffffffc0106414: 41 5e popq %r14
0xffffffffc0106416: 41 5d popq %r13
0xffffffffc0106418: 5b popq %rbx
0xffffffffc0106419: c9 leave
0xffffffffc010641a: c3 retq
0xffffffffc010641b: cc int3
保持跟 gdb
/drgn
同样效果的同时,也跟 bpftool
一样提供了行号信息和源代码;而且,并不需要内核 dbgsym 文件。
总结
bpftool
是最方便的,但计算 callq
地址有问题;gdb
用来查看反汇编略繁琐;drgn
用来查看反汇编需要内核 dbgsym 文件。
不需要内核 dbgsym 文件,又想要行号信息和源代码,那就用 bpflbr
吧。
Wrong callq address displayed: https://github.com/libbpf/bpftool/issues/109
[2]Proposal: contrib/bpf_inspect.py: disas bpf prog with capstone: https://github.com/osandov/drgn/pull/409
[3]bpflbr: https://github.com/Asphaltt/bpflbr