简介
本文主要介绍如何在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_pipe
sudo 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