using ebpf in embeded env

嵌入式环境使用ebpf

Posted by icecube on September 20, 2024

不借助其它bpf调试工具,开发人员需要自行编写bpf 内核态类C代码和用户态C代码,
内核态代码用于生成字节码,用户态代码负责load并attach字节码到系统

1 借助开源库 libbpf-bootstrap 生成示例demo
先生成libbpf.so,再编译demo程序,最后把应用程序 (比如kprobe)打包进rootfs中

git clone --recursive https://github.com/libbpf/libbpf-bootstrap
cd libbpf/src && make install
cd samples/c/ && make

libbpf-bootstrap/examples/c/ 目录下的文件命名规则

基于 libbpf-bootstrap 的 BPF 程序对于源文件有一定的命名规则,
用于生成内核态字节码的 bpf 文件以 .bpf.c 结尾,用户态加载字节码的文件以 .c 结尾,且这两个文件的 前缀必须相同。

基于 libbpf-bootstrap 的 BPF 程序在编译时会先将 .bpf.c 文件编译为 对应的 .o 文件,
然后根据此文件生成 skeleton 文件,即 
.skel.h,这个文件会包含内核态中定义的一些 数据结构,以及用于装载内核态代码的关键函数。
在用户态代码 include 此文件之后调用对应的装载函数即可将字节码装载到内核中。

2 使用pahole工具基于vmlinux文件可以生成单独的btf文件,打包到rootfs中
pahole --btf_encode_detached external.btf vmlinux

3 系统起来之后拷贝external.btf 到 libbpf 指定搜索的某一个位置
比如 cp external.btf /boot/vmlinux-5.10.0

libbpf-bootstrap/libbpf/src/btf.c

4942 struct btf *btf__load_vmlinux_btf(void)
4943 {
4944         const char *sysfs_btf_path = "/sys/kernel/btf/vmlinux";
4945         /* fall back locations, trying to find vmlinux on disk */
4946         const char *locations[] = {
4947                 "/boot/vmlinux-%1$s",
4948                 "/lib/modules/%1$s/vmlinux-%1$s",
4949                 "/lib/modules/%1$s/build/vmlinux",
4950                 "/usr/lib/modules/%1$s/kernel/vmlinux",
4951                 "/usr/lib/debug/boot/vmlinux-%1$s",
4952                 "/usr/lib/debug/boot/vmlinux-%1$s.debug",
4953                 "/usr/lib/debug/lib/modules/%1$s/vmlinux",
4954         };

4 最后在设备上执行 demo中的应用
如下是运行 libbpf-bootstrap 中的 kprobe demo 时的输出

# kprobe                                                                                                                    
libbpf: loading object 'kprobe_bpf' from buffer                                                                                     
libbpf: elf: section(2) .symtab, size 240, link 1, flags 0, type=2                                                                  
libbpf: elf: section(3) kprobe/do_unlinkat, size 152, link 0, flags 6, type=1                                                       
libbpf: sec 'kprobe/do_unlinkat': found program 'do_unlinkat' at insn offset 0 (0 bytes), code size 19 insns (152 bytes)            
libbpf: elf: section(4) kretprobe/do_unlinkat, size 88, link 0, flags 6, type=1                                                     
libbpf: sec 'kretprobe/do_unlinkat': found program 'do_unlinkat_exit' at insn offset 0 (0 bytes), code size 11 insns (88 bytes)     
libbpf: elf: section(5) license, size 13, link 0, flags 3, type=1                                                                   
libbpf: license of kprobe_bpf is Dual BSD/GPL                                                                                       
libbpf: elf: section(6) .rodata, size 72, link 0, flags 2, type=1                                                                   
libbpf: elf: section(7) .relkprobe/do_unlinkat, size 16, link 2, flags 0, type=9                                                    
libbpf: elf: section(8) .relkretprobe/do_unlinkat, size 16, link 2, flags 0, type=9                                                 
libbpf: elf: section(9) .BTF, size 1449, link 0, flags 0, type=1                                                                    
libbpf: elf: section(10) .BTF.ext, size 364, link 0, flags 0, type=1                                                                
libbpf: looking for externs among 10 symbols...                                                                                     
libbpf: collected 0 externs total                                                                                                   
libbpf: map 'kprobe_b.rodata' (global data): at sec_idx 6, offset 0, flags 80.                                                      
libbpf: map 0 is "kprobe_b.rodata"                                                                                                  
libbpf: sec '.relkprobe/do_unlinkat': collecting relocation for section(3) 'kprobe/do_unlinkat'                                     
libbpf: sec '.relkprobe/do_unlinkat': relo #0: insn #12 against '.rodata'                                                           
libbpf: prog 'do_unlinkat': found data map 0 (kprobe_b.rodata, sec 6, off 0) for insn 12                                            
libbpf: sec '.relkretprobe/do_unlinkat': collecting relocation for section(4) 'kretprobe/do_unlinkat'                               
libbpf: sec '.relkretprobe/do_unlinkat': relo #0: insn #3 against '.rodata'                                                         
libbpf: prog 'do_unlinkat_exit': found data map 0 (kprobe_b.rodata, sec 6, off 0) for insn 3                                        
libbpf: object 'kprobe_bpf': failed (-22) to create BPF token from '/sys/fs/bpf', skipping optional step...                         
libbpf: loaded kernel BTF from ''                                                                                                   
libbpf: sec 'kprobe/do_unlinkat': found 2 CO-RE relocations                                                                         
libbpf: CO-RE relocating [2] struct pt_regs: found target candidate [209] struct pt_regs in [vmlinux]                               
libbpf: prog 'do_unlinkat': relo #0: <byte_off> [2] struct pt_regs.si (0:13 @ offset 104)                                           
libbpf: prog 'do_unlinkat': relo #0: matching candidate #0 <byte_off> [209] struct pt_regs.si (0:13 @ offset 104)                   
libbpf: prog 'do_unlinkat': relo #0: patched insn #0 (LDX/ST/STX) off 104 -> 104                                                    
libbpf: CO-RE relocating [7] struct filename: found target candidate [1731] struct filename in [vmlinux]                            
libbpf: prog 'do_unlinkat': relo #1: <byte_off> [7] struct filename.name (0:0 @ offset 0)                                           
libbpf: prog 'do_unlinkat': relo #1: matching candidate #0 <byte_off> [1731] struct filename.name (0:0 @ offset 0)                  
libbpf: prog 'do_unlinkat': relo #1: patched insn #3 (ALU/ALU64) imm 0 -> 0                                                         
libbpf: sec 'kretprobe/do_unlinkat': found 1 CO-RE relocations                                                                      
libbpf: prog 'do_unlinkat_exit': relo #0: <byte_off> [2] struct pt_regs.ax (0:10 @ offset 80)                                       
libbpf: prog 'do_unlinkat_exit': relo #0: matching candidate #0 <byte_off> [209] struct pt_regs.ax (0:10 @ offset 80)               
libbpf: prog 'do_unlinkat_exit': relo #0: patched insn #0 (LDX/ST/STX) off 80 -> 80                                                 
libbpf: map 'kprobe_b.rodata': created successfully, fd=3                                                                           
libbpf: failed to open '/sys/bus/event_source/devices/kprobe/type': No such file or directory                                       
libbpf: failed to open '/sys/bus/event_source/devices/kprobe/type': No such file or directory                                       
Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` to see output of the BPF programs.                 
.........

测试创建删除一个文件,然后从缓冲区读取信息

# touch hello
# rm hello
# mount -t debugfs nodev /sys/kernel/debug/
# cat /sys/kernel/debug/tracing/trace
# tracer: nop
#
# entries-in-buffer/entries-written: 2/2   #P:1
#
#                                _-----=> irqs-off
#                               / _----=> need-resched
#                              | / _---=> hardirq/softirq
#                              || / _--=> preempt-depth
#                              ||| /     delay
#           TASK-PID     CPU#  ||||   TIMESTAMP  FUNCTION
#              | |         |   ||||      |         |
              rm-1532    [000] d...    68.247724: bpf_trace_printk: KPROBE ENTRY pid = 1532, filename = hello

              rm-1532    [000] d...    68.247751: bpf_trace_printk: KPROBE EXIT: pid = 1532, ret = 0

参考

https://github.com/libbpf/libbpf-bootstrap
https://eunomia.dev/zh/tutorials/0-introduce/#ebpf-go-library
https://github.com/aquasecurity/btfhub/blob/main/docs/how-to-use-pahole.md