ebpf入门程序

Posted by 婷 on September 24, 2024 本文总阅读量

简介

本文主要介绍如何在wsl上交叉编译运行在raspi上的ebpf程序,内核版本为6.6.42

wsl需要安装的工具

sudo apt install dwarves

image-20240921175445892

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

image-20240921175353544

安装Clang

sudo apt install clang
clang -v

image-20240921000614120

image-20240921000749106

安装LLVM

sudo apt install llvm
llc --version

image-20240921000812631

image-20240921000900162

内核配置项

打开的内核选项

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

工具链路径

image-20240921202517299

具体过程

这里主要描述编译过程的踩坑,反正总的编译方法都在编译命令章节了

headers

首先是安装头文件,会安装当前编译的内核目录下

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

image-20240921204005343

最终路径

image-20240921142001075

modules_prepare

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

image-20240921204023015

scripts

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

image-20240921204036137

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

image-20240921001848972

这个时候出现错误

fatal error: libelf.h: No such file or directory

搜了下,说需要安装libelf-dev

sudo apt-get install libelf-dev

image-20240921145049270

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

image-20240921145308760

出现了两个问题,找不到两个动态库,出现这个问题是说明我们使用的工具链下没有这两个库,需要我们交叉编译一下,且放到工具链的对应位置下

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,确实有点难度的

image-20240921162540338

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

image-20240921163036019

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

image-20240921163107814

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

image-20240921163159979

完美解决tools的编译问题

image-20240921163232905

接着来编译samples最后一步了

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-  M=samples/bpf

输入命令后出现了错误

undefined reference to `fstat64@GLIBC_2.33`

image-20240921163433064

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

aarch64-linux-gnu用的是2.31版本

image-20240921163613821

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

image-20240921163734527

思来想去,决定用aarch64-none-linux-gnu这个编译工具链,那大不了重头再来

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

image-20240921170223769

拷贝到/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 .

image-20240921170447000

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

image-20240921170614257

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

image-20240921170703679

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

image-20240921170827969

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/

image-20240921171212019

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

image-20240921215158391

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)

image-20240921180357210

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

image-20240921234202929

image-20240921234251214

image-20240921234308211

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

image-20240921182546379

image-20240921234415126

sock_example demo

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

image-20240921200523460

添加自己的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

image-20240922001328526

内核态程序

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增加修改

image-20240921235340821

image-20240921235419725

注意这里增加了$(TRACE_HELPERS)

image-20240921235913446

编译

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

image-20240924084502690

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

image-20240924005505714

参考做法

交叉编译工具链支持zlib

首先来到zlib官网,下载代码

https://www.zlib.net/

image-20240921145736641

配置

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

image-20240921150314480

安装

make -j32 && make install

image-20240921150346489

安装到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/

image-20240921150631321

image-20240921150802482

安装到aarch64-linux-gnu工具链对应的位置

tar -xf zlib.tar -C /usr/aarch64-linux-gnu/

image-20240921162351148

image-20240921162448344

像这种类似的问题,比如这个链接提的没有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程序一开始跑不起来解决过程

一开始代码执行,并没有任何反应

image-20240922114124628

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

image-20240922114138960

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

image-20240924090257104

image-20240922113907740

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

image-20240922114201428

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

cat available_events  | grep exe

image-20240922123444626

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

image-20240924090953933

跟代码对应不起来

image-20240924090920102

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

image-20240922122657107

于是升级内核后,就有啦

image-20240924091101925

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

image-20240924091116286

输入查看,已经有了对应的events了

cat /sys/kernel/debug/tracing/available_events | grep execve

image-20240924091239079

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
    

跟踪器被暂停或禁用

系统可能在某些情况下会禁用或暂停跟踪功能,检查是否存在与系统状态相关的问题(如内存不足等)。

检查步骤

  1. 启用跟踪

    echo 1 > /sys/kernel/debug/tracing/tracing_on
    
  2. 启用事件: 启用一个常用事件,比如系统调用:

    echo 1 > /sys/kernel/debug/tracing/events/syscalls/enable
    
  3. 查看输出: 使用 cattail 查看 trace_pipe 文件:

    sudo cat /sys/kernel/debug/tracing/trace_pipe
    

参考链接