将 eBPF 附加到 KPROBE?

Attaching eBPF to KPROBE?

我写了一个简单的程序来附加到带有 kprobe 的 execve 系统调用,但是我看不到相关的输出。

这是我的 one.c(BPF 程序):

#include <errno.h>
#include <bpf/bpf.h>
#include <stdio.h>
#include <string.h>

#include "bpf_load.h"
#include "bpf_util.h"
#include "libbpf.h"

#define SEC(NAME) __attribute__((section(NAME), used))

SEC("kprobe/execve")
int bpf_prog1(struct pt_regs *ctx)
{
        char m[]="hello world";
        bpf_trace_printk(m,sizeof(m));
        
        return 0;
}

char _license[] SEC("license") = "GPL";

bpf_load.c(用户 space 加载程序):

#include "bpf_load.h"
#include <stdio.h>

#include <linux/bpf.h>
#include <sys/resource.h>

int main(int argc, char **argv) {
    if (load_bpf_file("one.o")) {
        printf("%s", bpf_log_buf);
        return 1;
    }
    return 0;
}

和生成文件:

CLANG = clang

EXECABLE = monitor-exec

BPFCODE = one

BPFTOOLS = /kernel-src/samples/bpf
BPFLOADER = $(BPFTOOLS)/bpf_load.c

INCLUDE += -I/kernel-src/samples/bpf
INCLUDE += -I/kernel-src/tools/lib

CCINCLUDE += -I/kernel-src/tools/testing/selftests/bpf
CCINCLUDE += -I/kernel-src/tools/lib/bpf
CCINCLUDE += ${INCLUDE}

LOADINCLUDE += -I/kernel-src/tools/include
LOADINCLUDE += -I/kernel-src/tools/perf
LOADINCLUDE += ${INCLUDE}
LIBRARY_PATH = -L/usr/local/lib64
BPFSO = -lbpf

.PHONY: clean bpfload build

clean:
    rm -f *.o *.so $(EXECABLE)

build: ${BPFCODE.c} ${BPFLOADER}
    $(CLANG) -O2 -DHAVE_ATTR_TEST=0 -target bpf -c $(BPFCODE:=.c) $(CCINCLUDE) -o ${BPFCODE:=.o}

bpfload: build
    clang -o $(EXECABLE) -DHAVE_ATTR_TEST=0  -lelf $(LOADINCLUDE) $(LIBRARY_PATH) $(BPFSO) \
        $(BPFLOADER) loader.c

$(EXECABLE): bpfload

.DEFAULT_GOAL := $(EXECABLE)

到目前为止,我没有从 Makefile 中收到任何错误。

我在执行 ./monitor-exec

时得到以下输出
invalid relo for insn[6].code 0x85
bpf_load_program() err=22
last insn is not an exit or jmp
processed 0 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
last insn is not an exit or jmp
processed 0 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0

我无法理解我做错了什么。我只是附加了一个 kprobe,它应该在系统调用发生时打印 hello world

在您的 eBPF 程序中:

#include <errno.h>
#include <bpf/bpf.h>
#include <stdio.h>
#include <string.h>

#include "bpf_load.h"
#include "bpf_util.h"
#include "libbpf.h"

#define SEC(NAME) __attribute__((section(NAME), used))

SEC("kprobe/execve")
int bpf_prog1(struct pt_regs *ctx)
{
        char m[]="hello world";
        bpf_trace_printk(m,sizeof(m));
        
        return 0;
}

你正确地使用了bpf_trace_printk()(虽然你可能想在你的消息末尾添加一个\n,否则你的输出会很乱),但结果是none您包含的文件包含此助手的定义。

bpf_trace_printk() 被编译为内核的一部分,永远不会被编译到你的 BPF object 文件中。当尝试加载您的程序时,函数 load_bpf_file() 执行重定位步骤,它将 数字 关联到 bpf_trace_printk()(在用户 API 中) eBPF字节码的相关指令。

但它需要在某处找到这个号码。它在 header linux/bpf.h(从你的几个包含中拉出)中定义为 FN(trace_printk)(一些宏魔术正在进行),导致 de facto一个#define BPF_FUNC_trace_prink 6。但是你需要告诉你的加载函数它对应于你正在调用的bpf_trace_prink()

两种解决方案:

  • 手动声明:
    static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) =
            (void *) BPF_FUNC_trace_printk;
    
  • 或添加包含它的 header,例如tools/lib/bpf/bpf_helpers.h 在内核仓库中。在你的情况下:
    #include <bpf/bpf_helpers.h>
    
    (请注意,此 header 在编译 libbpf 时为 generated,默认情况下它不存在于存储库中。)