一、背景
需求在嵌入式环境下进行交叉编译,学习ebpf相关技术,所以想搭建一个不依赖硬件环境的学习环境。
本文使用的环境版本:
宿主机: Ubuntu24.02
libbpf-bootstrap源码:
https://github.com/libbpf/libbpf-bootstrap
Linux内核源码:Linux 5.4.123
下载地址:
Index of /kernel/linux/kernel/v5.x/https://mirror.bjtu.edu.cn/kernel/linux/kernel/v5.x/
交叉编译链:gcc-linaro-7.5.0-2019.12-x86_64_aarch64
Linaro Releases
Buildroot版本:buildroot-2024.02.9
下载地址:
Buildroot - Making Embedded Linux Easy
Qemu安装:
qemu-system-aarch64启动Linux内核 - PolarisZg - 博客园
二、调试环境搭建
2.1 内核支持ebpf配置
ws@ws-pc:~/qemu/aarch64/linux-5.4.123$ make ARCH=arm64 menuconfig
除了上述配置,还需要开启kprobe、uprobe、tracing、debugfs、bfp相关配置。最终好用的config如上传的附件。
如果内核配置模块未启用,会导致类似如下报错。
上述报错在源码中debug定位,最后会发现失败在相关 syscall中,补全就好。
2.2 buildroot编译rootfs文件系统
制作rootfs根文件系统的方式目前流行的有三种,一种是直接使用busybox直接制作,这种系统占用内存小,适合嵌入式系统上运行,但是缺少很多工具,不便于学习;第二种是使用buildroot直接源码编译制作,简单可定制化;第三种是基于ubuntu等开源系统的最小包,在此基础上追加制作。本文选择使用buildroot的方式,编译出来的roofs提供给qemu使用,由于是虚拟板子没有串口,所以bulidroot一定要选择支持ssh 或者 telent的方式登陆,这样后面才可以有多个终端登陆。
制作教程资料很多,本文在制作时,没有制作内核,交叉编译链使用的系统自带的:
基于Linux的Buildroot 制作根文件系统(rootfs)_buildroot制作根文件系统-CSDN博客
基本配置:
设置root权限的登陆密码:
取消内核编译:
open SSH相关需要打开,打开server,这样可以ssh登陆:
文件系统选择的ext4:
还需要执行,在busybox添加一下telnet:
make busybox-menuconfig
2.3 qemu安装启动内核
命令行直接安装qemu: 教程也很多,就直接安装就好了
sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virt-manager -y
注意网络net使用 tap的方式,可以让qemu出现网卡:
宿主机上需要进行如下网络配置,开发板可以直接访问公网,但是DHCP没有自动获取到IP,所以板子启动之后还需要手动设置以下IP:
qemu aarch64虚拟机创建好后,使用NAT连接网络 - wswang - 博客园
Documentation/Networking/NAT - QEMU
#!/bin/sh
#-initrd /home/ws/qemu/aarch64/rootfs.cpio.gz \
# -kernel /home/ws/qemu/aarch64/rootfs/kernel_5.4_rootfs/Image \
qemu-system-aarch64 \
-machine virt,virtualization=true,gic-version=3 \
-nographic \
-m size=4096M \
-cpu cortex-a76 \
-net tap -net nic \
-smp 4 \
-kernel /home/ws/qemu/aarch64/linux-5.4.123/arch/arm64/boot/Image \
-drive file=/home/ws/qemu/aarch64/rootfs/kernel_5.4_rootfs/rootfs.ext4,if=none,format=raw,id=hd0 \
-device virtio-blk-device,drive=hd0 \
-append "rootwait root=/dev/vda rw console=ttyAMA0 rdinit=/linuxrc"
#! /bin/sh
ifconfig eth0 192.168.53.10 netmask 255.255.255.0 up
mount -t nfs -o nolock,vers=3 192.168.53.1:/home/ws/qemu /mnt
有了网络之后,就可以设置虚拟开发板和宿主机之间的nfs,教程如下:
Qemu - 百问网嵌入式Linux wiki
2.4 gdbserver远程debug
由于环境搭建过程中遇到很多奇奇怪怪的错误,但是提示报错的原因又很少不准确,所以需求搭建一个vscode+gdb的远程调试工具,来debug libbpf-bootstrap工程。
一开始是使用交叉编译环境,使用源码编译的方式来搭建,但是发现高版本的又需要交叉编译库,低版本的编译出来gdbserver在开发版上执行报错,所以还了一种简单的方式来搭建。
宿主机安装 gdb-multiarch:
sudo apt-get update
sudo apt-get install gdb-multiarch
which gdb-multiarch
目标机直接下载其他人编译好的版本:(下载可能需要梯子)
https://github.com/skyedai910/gdbserver-all-in-one/releases
设置vscode相关设置:
指定要debug的进程minimal_legacy,在指定使用的gdb工具,使用witch可以看到安装路径。在指定远程调试的gdb地址和端口号。
将gdbserver拷贝到目标开发板的nfs路径下,远程执行。
打开内核的编译优化,两处地方加上-O0,否则单步调试的时候会乱跳:
2.5 libbpf-bootstrap编译
bilibili有位up主讲的非常好,这里引用一下:
libbpf-bootstrap交叉编译_libbpf-bootstrap arm-CSDN博客
这里同时分享以下我的编译脚本:
gcc_7.5.sh
#! /bin/sh
export PATH=/home/ws/chain_tools/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin:$PATH
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
2.5.1 zlib-1.3.1 zlib编译
make_zlib.sh
cd zlib-1.3.1
source ~/env/gcc_7.5.sh
#export PATH=$PATH:/usr/bin/
export CC=aarch64-linux-gnu-gcc
./configure --prefix=$PWD/_install
make -j16
make install
2.5.2 elfutils-0.192 编译
make_elfutils.sh
cd elfutils-0.192
# 参考当前目录下的 INSTALL 文档 和 网上资料
source ~/env/gcc_7.5.sh
export CFLAGS="-fPIC -I /home/ws/qemu/ebpf_arm64/zlib-1.3.1/_install/include"
export LDFLAGS="-L /home/ws/qemu/ebpf_arm64/zlib-1.3.1/_install/lib -Wl,-rpath-link,/home/ws/qemu/ebpf_arm64/zlib-1.3.1/_install/lib"
./configure --prefix=$PWD/_install --build=x86_64-linux-gnu \
--host=aarch64-linux-gnu \
CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ \
--disable-nls --disable-rpath --disable-libdebuginfod --disable-debuginfod \
--with-zlib=/home/ws/qemu/ebpf_arm64/zlib-1.3.1/_install/lib
make -j16
make install
2.5.3 libbpf的example编译
在这个example的编译Makefile和cMake最好需要了解以下原理,这里推进一个up主的视频:
03-09-libbpf样例程序Makefile脚本解读_哔哩哔哩_bilibili
make_libbpf.sh
#!/bin/sh
cd ./libbpf-bootstrap/examples/c
source ~/env/gcc_7.5.sh
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
export EXTRA_CFLAGS="-fPIC -I /home/ws/qemu/ebpf_arm64/elfutils-0.192/_install/include -I /home/ws/qemu/ebpf_arm64/zlib-1.3.1/_install/include"
export EXTRA_LDFLAGS="-L /home/ws/qemu/ebpf_arm64/elfutils-0.192/_install/lib -Wl,-rpath-link,/home/ws/qemu/ebpf_arm64/elfutils-0.192/_install/lib -L /home/ws/qemu/ebpf_arm64/zlib-1.3.1/_install/lib -Wl,-rpath-link,/home/ws/qemu/ebpf_arm64/zlib-1.3.1/_install/lib"
#make -j16 V=1
make minimal_legacy V=1
make_libbpf_clean.sh
#!/bin/sh
cd /home/ws/qemu/ebpf_arm64/libbpf-bootstrap/examples/c
make clean
cd /home/ws/qemu/ebpf_arm64/libbpf-bootstrap/libbpf/src
make clean
cd /home/ws/qemu/ebpf_arm64/libbpf-bootstrap/bpftool/src
make clean
三、遇到的问题
3.1 高版本内核编译时生成BTF失败
Linux 5.19在编译的时候,生成BTF文件失败,但是Linux 5.4.123内核的时候能够成功。
BTF: .tmp_vmlinux.btf: pahole (pahole) is not available Failed to generate BTF for vmlinux Try to disable CONFIG_DEBUG_INFO_BTF make: *** [Makefile:1100:vmlinux] 错误 1
原因:Ubuntu 24.04 apt-get install默认安装的pohole 版本过高,需要降低到v1.22版本
源码编译安装:下载对应版本的源码编译安装,注意tag版本
https://github.com/acmel/dwarves/tree/v1.22
git clone --recurse-submodules https://github.com/acmel/dwarves.git
git checkout tags/v1.22
git switch -c v1.22
cd dwarves
mkdir build
cd build
cmake -D__LIB=lib -DBUILD_SHARED_LIBS=OFF ..
sudo make install
安装完成之后需要更新一下动态库,如果安装错了,需要卸载:
#!/bin/bash
# 定义要删除的文件和目录列表
files=(
"/usr/local/bin/codiff"
"/usr/local/bin/ctracer"
"/usr/local/bin/dtagnames"
"/usr/local/bin/pahole"
"/usr/local/bin/pdwtags"
"/usr/local/bin/pfunct"
"/usr/local/bin/pglobal"
"/usr/local/bin/prefcnt"
"/usr/local/bin/scncopy"
"/usr/local/bin/syscse"
"/usr/local/lib/libdwarves.so.1.0.0"
"/usr/local/lib/libdwarves.so.1"
"/usr/local/lib/libdwarves.so"
"/usr/local/lib/libdwarves_emit.so.1.0.0"
"/usr/local/lib/libdwarves_emit.so.1"
"/usr/local/lib/libdwarves_emit.so"
"/usr/local/lib/libdwarves_reorganize.so.1.0.0"
"/usr/local/lib/libdwarves_reorganize.so.1"
"/usr/local/lib/libdwarves_reorganize.so"
"/usr/local/include/dwarves/dwarves.h"
"/usr/local/include/dwarves/dwarves_emit.h"
"/usr/local/include/dwarves/dwarves_reorganize.h"
"/usr/local/include/dwarves/dutil.h"
"/usr/local/include/dwarves/gobuffer.h"
"/usr/local/include/dwarves/list.h"
"/usr/local/include/dwarves/rbtree.h"
"/usr/local/include/dwarves/btf_encoder.h"
"/usr/local/include/dwarves/config.h"
"/usr/local/include/dwarves/ctf.h"
"/usr/local/include/dwarves/elfcreator.h"
"/usr/local/include/dwarves/elf_symtab.h"
"/usr/local/include/dwarves/hash.h"
"/usr/local/include/dwarves/libctf.h"
"/usr/local/share/man/man1/pahole.1"
"/usr/local/bin/ostra-cg"
"/usr/local/share/dwarves/runtime/python/ostra.py"
"/usr/local/bin/btfdiff"
"/usr/local/bin/fullcircle"
"/usr/local/share/dwarves/runtime/Makefile"
"/usr/local/share/dwarves/runtime/ctracer_relay.c"
"/usr/local/share/dwarves/runtime/ctracer_relay.h"
"/usr/local/share/dwarves/runtime/linux.blacklist.cu"
)
# 循环删除文件和目录
for file in "${files[@]}"; do
if [ -f "$file" ]; then
echo "Deleting file: $file"
rm -f "$file"
elif [ -d "$file" ]; then
echo "Deleting directory: $file"
rm -rf "$file"
fi
done
# 清理动态库缓存
echo "Updating dynamic library cache..."
sudo /sbin/ldconfig
echo "Uninstallation complete."
3.2 执行时报错 object 'minimal_legacy_': failed (-22) to create BPF token from '/sys/fs/bpf', skipping optional step
一开始不确定是否是该原因导致无法加载,后面是将libbpf和bpftool仓库都降低了版本之后编译解决。
Tags · libbpf/bpftool · GitHub
Tags · libbpf/libbpf · GitHub
切换到指定版本的方式:
git checkout tags/v7.3.0
git switch -c v7.3.0
# bpftool下面有子仓库,使用下面命令能够自动更新子仓库
git submodule update --init
3.3 编译libbpf example程序的时候,报错编译链存在问题
需要将两处cc改为gcc,否则使用的编译链是cc结尾,本人安装的交叉编译链下没有这个。
3.4 报错/sys/fs/bpf failed (-22) to create BPF token from '/sys/fs/bpf', skipping optional step...
内核配置里面有相关的模块没有使能