Android libbpf Demo 书写

——From ebpf.io
基础概念
- vmlinux 文件是什么
vmlinux 文件是一个 ELF 文件,可以理解就是 linux 内核
在linux系统中,vmlinux(vmlinuz)是一个包含linux kernel的静态链接的可执行文件,文件类型可能是linux接受的可执行文件格式之一(ELF、COFF或a.out),vmlinux若要用于调试时则必须要在开机前增加symbol table。
——From Wikipedia
- vmlinux.h 是什么
vmlinux.h 是使用工具为 vmlinux 内核文件生成的的所有类型定义文件。
vmlinux.h 部分内容输出

大体的逻辑如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
#ifndef __VMLINUX_H__ #define __VMLINUX_H__
struct ... {} struct ... {} struct ... {} struct ... {} ......
#endif
|
refs:https://www.ebpf.top/post/intro_vmlinux_h/
Demo开发
refs:https://blog.csdn.net/weixin_45092290/article/details/138767704
前置准备
1 2 3 4 5 6
| # 安装常规编译工具链 apt install clang clangd lldb cmake # 安装libbpf-dev(安装 bpf/bpf_helpers.h 等头文件) apt install libbpf-dev # 提取vmlinux.h头文件 bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
|
内核Demo程序编写
clang -target bpf -I. -c ebpf-demo.bpf.c -o ebpf-demo.bpf.o
// ebpf-demo.bpf.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include "vmlinux.h" #include <bpf/bpf_helpers.h>
SEC("kprobe/__arm64_sys_openat") int bpf_prog(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid() >> 32; u64 *count, init_val = 1;
bpf_printk("opennat calced");
return 0; }
char LICENSE[] SEC("license") = "GPL";
|
Skel生成
bpftool gen skeleton ebpf-demo.bpf.o > ebpf-demo.skel.h
用户态Demo程序
clang -Wall -I. -c ebpf-demo.c -o ebpf-demo.o
// ebpf-demo.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/resource.h> #include <bpf/libbpf.h> #include "ebpf-demo.skel.h"
static int libbpf_print_fn(enum libbpf_print_level level, const char * format, va_list args){ return vfprintf(stderr, format, args); }
static void bump_memlock_rlimit() {
struct rlimit rlimt_new = { .rlim_cur = RLIM_INFINITY, .rlim_max = RLIM_INFINITY, };
if (setrlimit(RLIMIT_MEMLOCK, &rlimt_new)) { fprintf(stderr, "Fail to increate the limit!"); exit(1); }
}
int main() {
struct ebpf_demo_bpf* skel; int err; libbpf_set_print(libbpf_print_fn);
bump_memlock_rlimit();
skel = ebpf_demo_bpf__open_and_load();
if (!skel) { fprintf(stderr,"Fail to open bpf"); }
err = ebpf_demo_bpf__attach(skel);
if (err) { fprintf(stderr, "Fail to load %d", err); goto cleanup; }
fprintf(stderr, "Successfully started! see output by cat /sys/kernel/tracing/trace_pipe\n");
for(;;) { fprintf(stderr, "."); sleep(1); }
cleanup: ebpf_demo_bpf__destroy(skel); return -err;
}
|
编译总程序
compile.sh
1 2 3 4 5 6
| #!/bin/zsh rm ebpf-demo ebpf-demo.bpf.o ebpf-demo.skel.h clang -O2 -target bpf -I. -c ebpf-demo.bpf.c -o ebpf-demo.bpf.o bpftool gen skeleton ebpf-demo.bpf.o > ebpf-demo.skel.h clang -Wall -I. -c ebpf-demo.c -o ebpf-demo.o clang -Wall ebpf-demo.o -L/usr/lib64 -lbpf -lelf -lz -o ebpf-demo
|
运行程序
1 2 3 4 5 6 7 8 9 10 11 12
| ➜ code ./ebpf-demo libbpf: loading object 'ebpf_demo_bpf' from buffer libbpf: elf: section(3) kprobe/__arm64_sys_openat, size 72, link 0, flags 6, type=1 libbpf: sec 'kprobe/__arm64_sys_openat': found program 'bpf_prog' at insn offset 0 (0 bytes), code size 9 insns (72 bytes) libbpf: elf: section(4) license, size 4, link 0, flags 3, type=1 libbpf: license of ebpf_demo_bpf is GPL libbpf: elf: section(6) .symtab, size 96, link 1, flags 0, type=2 libbpf: looking for externs among 4 symbols... libbpf: collected 0 externs total libbpf: object 'ebpf_demo_bpf': failed (-22) to create BPF token from '/sys/fs/bpf', skipping optional step... Successfully started! see output by cat /sys/kernel/tracing/trace_pipe .......
|
你可能会疑惑,输出呢?
由于我们内核程序使用的bpf_printk。
所以输出会被重定向到/sys/kernel/debug/tracing/trace_pipe文件
直接cat /sys/kernel/tracing/trace_pip
即可。
你可能还有疑惑。
因为你cat /sys/kernel/tracing/trace_pipe 的输出结果很可能是空的。
那是因为你没有开启 trace
执行如下 shell 脚本
echo 1 > /sys/kernel/tracing/tracing_on
总结
1.eBPF 的程序其实包含两个部分
a. 内核态程序(这个程序会在内核虚拟机内执行)
b. 用户态程序——前端(这个程序会用于加载并与内核态程序通讯,虽然 Demo 中没有体现通讯这一过程,但是他是可以实现的)
2.libbpf 程序
a. 最终产物只有一个 elf 文件,这个 elf 内包含用户态程序 & 内核态程序的二进制数据。
b. libbpf 编译过程中会生成 skel 文件,这个文件内包含一些 eBPF 生命周期模板方法。
c. libbpf 以及其他 eBPF 脚手架一样,都只是 eBPF 前端(后端在内核态上运行)
eBPF 的脚手架特别多,libbpf 只是其中一种。
常见的有
所以没必要纠结需要使用哪种,哪种顺手就用哪种~