简介
本文主要介绍如何在wsl上交叉编译运行在raspi上的ebpf程序,内核版本为6.6.42
wsl需要安装的工具
sudo apt install dwarves

如果不安装dwarves工具,打开CONFIG_DEBUG_INFO_BTF选项的话,内核会编译失败,会提示BTF: .tmp_vmlinux.btf: pahole (pahole) is not available

安装Clang
sudo apt install clang
clang -v


安装LLVM
sudo apt install llvm
llc --version


内核配置项
打开的内核选项
CONFIG_BPF=y
CONFIG_BPF_SYSCALL=y
# [optional, for tc filters]
CONFIG_NET_CLS_BPF=m
# [optional, for tc actions]
CONFIG_NET_ACT_BPF=m
CONFIG_BPF_JIT=y
# [for Linux kernel versions 4.1 through 4.6]
CONFIG_HAVE_BPF_JIT=y
# [for Linux kernel versions 4.7 and later]
CONFIG_HAVE_EBPF_JIT=y
# [optional, for kprobes]
CONFIG_BPF_EVENTS=y
# Need kernel headers through /sys/kernel/kheaders.tar.xz
CONFIG_IKHEADERS=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_BTF=y
CONFIG_XDP_SOCKETS=y
CONFIG_DEBUG_INFO_BTF=y
CONFIG_KPROBE_EVENTS=y
CONFIG_UPROBE_EVENTS=y
CONFIG_FTRACE_SYSCALLS=y
编译命令
编译之前的准备工作,注意,先保证内核已编译
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- headers_install
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- modules_prepare
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- scripts
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- -C tools/lib/bpf clean
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- -C tools/lib/bpf
编译内核带的samples命令如下
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- M=samples/bpf clean
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- M=samples/bpf
工具链路径

具体过程
这里主要描述编译过程的踩坑,反正总的编译方法都在编译命令章节了
headers
首先是安装头文件,会安装当前编译的内核目录下
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- headers_install

最终路径

modules_prepare
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- modules_prepare

scripts
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- scripts

tools
接着是编译tools,刺激的来了,接下来的过程会比较混乱,因为一开始编译这块的时候,工具链用的其实是wsl安装的aarch64-linux-gnu-,而后面才改用了aarch64-none-linux-gnu-
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -C tools/lib/bpf clean
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -C tools/lib/bpf

这个时候出现错误
fatal error: libelf.h: No such file or directory
搜了下,说需要安装libelf-dev
sudo apt-get install libelf-dev

装了后就能解决找不到libelf.h的问题,但是又有新的问题

出现了两个问题,找不到两个动态库,出现这个问题是说明我们使用的工具链下没有这两个库,需要我们交叉编译一下,且放到工具链的对应位置下
aarch64-linux-gnu/bin/ld: cannot find -lelf
aarch64-linux-gnu/bin/ld: cannot find -lz
首先解决找不到链接库libz的问题,最后按照参考做法,交叉编译了一把,解决了cannot find -lz的问题,不过还有cannot find -lelf的问题,解决思路跟解决libz的方法是一样的,不过这个时候要我来交叉编译elfutils,确实有点难度的

机智如我想到了,buildroot下也有交叉编译过的libelf库呀

于是把libelf的库拷贝到/usr/aarch64-linux-gnu/lib下,因为我用的工具链gcc为aarch64-linux-gnu-gcc,他的交叉编译库就放在这个目录下。其实讲道理libz的库也可以从buildroot下面拷贝,也省事的。

cp usr/lib/libelf* /usr/aarch64-linux-gnu/lib/

完美解决tools的编译问题

接着来编译samples最后一步了
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- M=samples/bpf
输入命令后出现了错误
undefined reference to `fstat64@GLIBC_2.33`

一眼看出来,大概猜测就是glibc库版本的问题,大概率编译buildroot的工具链aarch64-none-linux-gnu的C库版本比我们wsl安装的aarch64-linux-gnu版本大。
aarch64-linux-gnu用的是2.31版本

aarch64-none-linux-gnu用的是2.33版本,跟前面的报错提示对应上了

思来想去,决定用aarch64-none-linux-gnu这个编译工具链,那大不了重头再来
编译tools,显示碰到一个找不到libelf.h的问题,这个这里截图没放,这种一般就是说aarch64-none-linux-gnu的工具链下的头文件目录有这个文件,果然从buildroot里面去找就真的有

拷贝到/opt/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/aarch64-none-linux-gnu/libc/usr/include目录下
cp ~/raspi4b-project/buildroot/output/target/usr/include/libelf.h .

然后再次编译,出现找不到gelf.h的问题,没事继续去buildroot找

cp ~/raspi4b-project/buildroot/output/target/usr/include/gelf.h .

头文件的问题解决了,接着又是出现没有找到libelf库的问题,那这个解决思路跟前面是一样的

cp ~/raspi4b-project/buildroot/output/target/usr/lib/libelf* /opt/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/aarch64-none-linux-gnu/libc/usr/lib/

解决完这些错误后,完美编译

samples
执行命令
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- M=samples/bpf clean
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- M=samples/bpf
最后碰到这个跟XDP相关的BTF报错的问题
libbpf: global 'rx_cnt': incompatible BTF kinds struct and void
Error: failed to link 'samples/bpf/xdp_sample.bpf.o': Invalid argument (22)

大不了就先注释掉Makefile中的相关内容喽,注释内容如下



注释了之后,感觉有些XDP相关的都会报错,但是不影响其他的程序编译,报错以后再来看看怎么解决吧


sock_example demo
这里拿sock_example文件放到树莓派中运行,证实可行

添加自己的demo
用户态程序
hello_user.c
#include <stdio.h>
#include "trace_helpers.h"
#include <linux/bpf.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <unistd.h>
int main(int ac, char **argv)
{
struct bpf_object *obj;
char filename[256];
int err;
snprintf(filename, sizeof(filename), "hello_kern.o");
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
printf("bpf_open_file error!!!\n");
return 1;
}
err = bpf_object__load(obj);
if (err) {
printf("bpf_object_load error!!!\n");
return 1;
}
printf("read start\n");
read_trace_pipe();
printf("read end\n");
return 0;
}
其中的read_trace_pipe函数定义在trace_helpers.h里

内核态程序
hello_kern.c
#include <linux/bpf.h>
#include <uapi/linux/bpf.h>
#include <bpf/bpf_helpers.h>
SEC("tracepoint/syscalls/sys_enter_execve")
int bpf_prog(void *ctx)
{
char msg[] = "Hello BPF test!\n";
bpf_trace_printk(msg, sizeof(msg));
return 0;
}
char _license[] SEC("license") = "GPL";
Makefile
Makefile增加修改


注意这里增加了$(TRACE_HELPERS)

编译
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- M=samples/bpf
运行
先使能sys_enter_execve
echo 1 > /sys/kernel/debug/tracing/events/syscalls/sys_enter_execve

拷贝hello跟hello_kern.o两个文件,注意要在同个目录下,执行结果如下

参考做法
交叉编译工具链支持zlib
首先来到zlib官网,下载代码
https://www.zlib.net/

配置
export PATH=$PATH:/opt/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/
export CC=aarch64-none-linux-gnu-gcc
export AR=aarch64-none-linux-gnu-ar
export RANLIB=aarch64-none-linux-gnu-ranlib
./configure --prefix=$HOME/tools/install/zlib-1.2.11/aarch64 --shared

安装
make -j32 && make install

安装到aarch64-none-linux-gnu工具链对应的位置
cd $HOME/tools/install/zlib-1.2.11/aarch64
tar -cf zlib.tar ./*
tar -xf zlib.tar -C /opt/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/aarch64-none-linux-gnu/libc/usr/


安装到aarch64-linux-gnu工具链对应的位置
tar -xf zlib.tar -C /usr/aarch64-linux-gnu/


像这种类似的问题,比如这个链接提的没有ssl的也是可以同样交叉编译把这个问题解决掉
/usr/lib/gcc-cross/aarch64-linux-gnu/5/…/…/…/…/aarch64-linux-gnu/bin/ld: cannot find -lssl
/usr/lib/gcc-cross/aarch64-linux-gnu/5/…/…/…/…/aarch64-linux-gnu/bin/ld: cannot find -lcrypto
ebpf程序一开始跑不起来解决过程
一开始代码执行,并没有任何反应

用strace跟踪了一下,过程没啥毛病

然后看到代码,因为对tracepoint相关概念不熟悉,以为是系统调用的函数搞错了,认为不应该是sys_enter_execve,而是应该是__arm64_sys_execve


但还是没反应,从strace看结果,一直在读/sys/kernel/debug/tracing/trace_pipe(忽略红框),说明没有东西出来

然后问了GPT,启发了我,GPT的回答见后面的参考做法。首先查看tracepoint可跟踪事件,发现并没有我们hello_kern.c中的sys_enter_execve
cat available_events | grep exe

然后发现这个目录下tracepoint的events没有syscall相关的内容

跟代码对应不起来

然后发现CONFIG_FTRACE_SYSCALLS内核配置项没有打开

于是升级内核后,就有啦

目录下有很多可以跟踪的事件

输入查看,已经有了对应的events了
cat /sys/kernel/debug/tracing/available_events | grep execve

gpt的回答
在 Linux 中,如果 /sys/kernel/debug/tracing/trace_pipe 没有打印出任何内容,可能是由于以下几种原因:
跟踪未启用
检查是否启用了跟踪。可以通过以下方式启用跟踪:
-
查看当前的跟踪状态:
cat /sys/kernel/debug/tracing/tracing_on输出为
0表示跟踪已禁用。 -
启用跟踪:
echo 1 > /sys/kernel/debug/tracing/tracing_on
未设置事件
如果没有为跟踪设置事件,可能会导致没有任何输出。你可以通过以下命令设置要跟踪的事件,例如系统调用:
-
跟踪所有系统调用:
echo 1 > /sys/kernel/debug/tracing/events/syscalls/enable -
或者跟踪特定的事件(例如:
sched_switch):echo 1 > /sys/kernel/debug/tracing/events/sched/sched_switch/enable
没有足够的权限
确保你有足够的权限访问 trace_pipe 文件。使用 sudo 来查看输出,或者确保你当前的用户有适当的权限。
-
查看
trace_pipesudo cat /sys/kernel/debug/tracing/trace_pipe
没有触发可跟踪的事件
如果系统中没有触发与已启用事件相关的活动,trace_pipe 中不会有任何输出。例如,如果你只启用了某些系统调用的跟踪,而这些系统调用没有被触发,则不会有输出。
- 你可以尝试触发一些已启用的事件,或者通过运行特定的命令来生成活动。
缓冲区满
如果跟踪缓冲区很小或者之前的跟踪内容过多,可能会导致缓冲区被覆盖,从而无法看到新数据。你可以通过以下方式清除缓冲区:
-
清除跟踪缓冲区:
echo > /sys/kernel/debug/tracing/trace
跟踪器被暂停或禁用
系统可能在某些情况下会禁用或暂停跟踪功能,检查是否存在与系统状态相关的问题(如内存不足等)。
检查步骤
-
启用跟踪:
echo 1 > /sys/kernel/debug/tracing/tracing_on -
启用事件: 启用一个常用事件,比如系统调用:
echo 1 > /sys/kernel/debug/tracing/events/syscalls/enable -
查看输出: 使用
cat或tail查看trace_pipe文件:sudo cat /sys/kernel/debug/tracing/trace_pipe