eBPF CO:RE: vmlinux.h 不完整?

Posted

技术标签:

【中文标题】eBPF CO:RE: vmlinux.h 不完整?【英文标题】:eBPF CO:RE: vmlinux.h incomplete? 【发布时间】:2022-01-07 03:02:33 【问题描述】:

我正在尝试使用 libbpf 进入 eBPF CO:RE。我的程序使用跟踪点 SEC("tracepoint/syscalls/sys_enter_kill")) 我想知道如何获取参数以及为什么它们不包含在 vmlinux.h 中。 此外,似乎缺少定义 BPF_F_CURRENT_CPU 。或者是否可以将 vmlinux.h 和 uapi/linux/bpf.h 结合起来?

我的设置:

Ubuntu 20.04 内核 5.4.0-90-通用 bpftool --version: ./bpftool v5.16.0-rc2 - 特性:libbfd、skeletons(来自 Kernel github,应该是最新的) vmlinux.h 使用 bpftool btf 转储文件 /sys/kernel/btf/vmlinux 格式 c > vmlinux.h 生成

我知道已经有关于如何获取论点的问题 (Read eBPF tracepoint argument)。但是,这并不能回答我的问题(为什么它不在 vmlinux.h 中?vmlinux.h 还有什么意义?)而且我还有另一个问题,内核显示的布局似乎不正确(见下文)。此外,似乎缺少定义 BPF_F_CURRENT_CPU。

我的代码:

common.h:只有一个小结构将数据传输到用户空间

struct dataStruct 
    int pid;
;

main.bpf.c

// Compiling with: clang -target bpf -S -D __BPF_TRACING__ -Wall -Werror -O2 -emit-llvm -c -g main.bpf.c
// and then: llc -march=bpf -filetype=obj -o main.bpf.o main.bpf.ll

#include "vmlinux.h"
#include "../90_lib/libbpf/build/root/usr/include/bpf/bpf_helpers.h"
#include "../90_lib/libbpf/build/root/usr/include/bpf/bpf_tracing.h"
#include "../90_lib/libbpf/build/root/usr/include/bpf/bpf_core_read.h"

#include "common.h"


struct 
    __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
    __uint(key_size, sizeof(int));
    __uint(value_size, sizeof(int));
 pb SEC(".maps");
struct 
    __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
    __uint(max_entries, 1);
    __type(key, int);;
    __type(value, struct dataStruct);
 heap SEC(".maps");


// sudo cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_kill/format
// name: sys_enter_kill
// ID: 184
// format:
//  field:unsigned short common_type;   offset:0;   size:2; signed:0;
//  field:unsigned char common_flags;   offset:2;   size:1; signed:0;
//  field:unsigned char common_preempt_count;   offset:3;   size:1;signed:0;
//  field:int common_pid;   offset:4;   size:4; signed:1;

//  field:int __syscall_nr; offset:8;   size:4; signed:1;
//  field:pid_t pid;    offset:16;  size:8; signed:0;
//  field:int sig;  offset:24;  size:8; signed:0;

// print fmt: "pid: 0x%08lx, sig: 0x%08lx", ((unsigned long)(REC->pid)), ((unsigned long)(REC->sig))


// >> How to obtain this structurecorrectly?
struct syscalls_enter_kill_args

    unsigned short common_type;
    unsigned char common_flags;
    unsigned char common_preempt_count;
    int common_pid;

    // int syscall_nr;      // From sudo cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_kill/format
    // pid_t pid;
    // int sig;

    long syscall_nr;    // From https://hed.am/papers/2021-EBPF.pdf
    long pid;
    long sig;
;



enum       // from #include "../90_lib/libbpf/include/uapi/linux/bpf.h"
    BPF_F_INDEX_MASK        = 0xffffffffULL,
    BPF_F_CURRENT_CPU       = BPF_F_INDEX_MASK,
/* BPF_FUNC_perf_event_output for sk_buff input context. */
    BPF_F_CTXLEN_MASK       = (0xfffffULL << 32),
;



SEC("tracepoint/syscalls/sys_enter_kill")
int kill_example(struct syscalls_enter_kill_args *ctx)

    if(ctx->sig != 9)
       return 0;

    // int s = BPF_CORE_READ(ctx, sig); // Does not work
    // if(s != 9)
    //  return 0;

    char fmt[] = "BPF handle\n";
    bpf_trace_printk(fmt, sizeof(fmt));


    struct dataStruct *e;
    int zero = 0;
    
    e = bpf_map_lookup_elem(&heap, &zero);
    if (!e) /* can't happen */
        return 0;

    e->pid = bpf_get_current_pid_tgid() >> 32;

    bpf_perf_event_output(ctx, &pb, BPF_F_CURRENT_CPU, e, sizeof(*e));

    return 0;


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

main.cc

// Compile loader
// >> g++ -I../90_lib/libbpf/src/root/usr/include/ -L../90_lib/libbpf/src/ -o ebpf main.cc -lbpf -lelf -Wl,-R../90_lib/libbpf/src/


# include <stdio.h>
#include <iostream>
# include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/resource.h>

#include <poll.h>
#include <sys/select.h>

#include </usr/include/asm-generic/errno-base.h>


// Should be defined in up-to-date linux/bpf.h, but is not (and other linux/bpf.h conflicts with a lot of definitions)
/* type for BPF_ENABLE_STATS */
enum bpf_stats_type 
    /* enabled run_time_ns and run_cnt */
    BPF_STATS_RUN_TIME = 0,
;

#include "../90_lib/libbpf/build/root/usr/include/bpf/bpf.h"
#include "../90_lib/libbpf/build/root/usr/include/bpf/libbpf.h"

#include "common.h"


void bump_memlock_rlimit(void)

    struct rlimit rlim_new = 
        .rlim_cur   = RLIM_INFINITY,
        .rlim_max   = RLIM_INFINITY,
    ;

    if (setrlimit(RLIMIT_MEMLOCK, &rlim_new)) 
        fprintf(stderr, "Failed to increase RLIMIT_MEMLOCK limit!\n");
        exit(1);
    




using namespace std;

void handle_event(void *ctx, int cpu, void *data, unsigned int data_sz)

    cout << "perfBuffer - Event!; got " << data_sz << " Bytes? data." << endl;
    struct dataStruct* d = static_cast<struct dataStruct*>(data);
    cout << "    PID: " << d->pid << endl;




int main (int argc , char ** argv )

    int prog_fd ;
    struct bpf_object *obj;

    bump_memlock_rlimit();

    if(bpf_prog_load("main.bpf.o", BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd) != 0)
    
        printf (" eBPF program not loaded \n");
        return -1;
    
    // Check that we got a file descriptor for the loaded object file.
    if(prog_fd < 1)
    
        printf (" Error creating prog_fd \n");
        return -2;
    
    // Attach the eBPF program by it 's function name
    struct bpf_program * prog = bpf_object__find_program_by_name(obj, "kill_example");
    bpf_program__attach(prog);

    int numMaps = 0;
    struct bpf_map * map;
    struct bpf_map* map_pb;

    struct bpf_map* maps[10];

    for (map = bpf_map__next(NULL, (obj));  \
         map != NULL;               \
         map = bpf_map__next(map, (obj)))
    
        ++numMaps;
        cout << "Found map, name: '" << bpf_map__name(map) << "'" << endl;
        if(strcmp(bpf_map__name(map), "pb") == 0)
        
            cout << "  Found map 'pb'; fileDescriptor: " << bpf_map__fd(map) << endl;
            map_pb = map;
        
    

    cout << "Found " << numMaps << " Maps." << endl;

    int map_pb_fd = bpf_map__fd(map_pb);
    cout << "Got Map File Descriptor: " << map_pb_fd << endl;

    uint32_t map_key_cnt = 0;
    uint64_t val = 0;
    uint64_t val_last = val;

    struct perf_buffer *pb = NULL;
    struct perf_buffer_opts pb_opts = ;
    pb_opts.sample_cb = handle_event;

    pb = perf_buffer__new(map_pb_fd, 8 /* 32KB per CPU */, &pb_opts);
    if (libbpf_get_error(pb)) 
        fprintf(stderr, "Failed to create perf buffer\n");
    

    int res = 0;
    cout << "Entering main loop." << endl;
    while(1)
    
        res = perf_buffer__poll(pb, -1);
        if(res == -EINTR)
        
            cout << "-EINTR" << endl;
            return 0;
        
    
    return 0;


这适用于结构

struct syscalls_enter_kill_args

    unsigned short common_type;
    unsigned char common_flags;
    unsigned char common_preempt_count;
    int common_pid;

    long syscall_nr;    // From https://hed.am/papers/2021-EBPF.pdf
    long pid;
    long sig;
;

但不是当我将结构更改为

struct syscalls_enter_kill_args

    unsigned short common_type;
    unsigned char common_flags;
    unsigned char common_preempt_count;
    int common_pid;

    int syscall_nr;     // From sudo cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_kill/format
    pid_t pid;
    int sig;
;

这似乎很奇怪。有人知道出了什么问题吗? 提前致谢! :)

【问题讨论】:

【参考方案1】:

唯一的原因是它不起作用,因为在这种情况下变量和大小的格式不匹配。 检查此文件以获取 sys_kill_enter 的格式 /sys/kernel/debug/tracing/events/syscalls/sys_enter_kill/format

虽然 pid 的字段是 pid_t 而对于 sig 它的 int。

但如果你检查 pid 和 sig 的大小,两者都是 8。

所以long 正在工作,而pid_t 没有。

【讨论】:

谢谢!所以很遗憾,我无法将类型复制到 C 代码中,但需要检查大小字段。将它包含在 vmlinux.h 中不是很好吗?

以上是关于eBPF CO:RE: vmlinux.h 不完整?的主要内容,如果未能解决你的问题,请参考以下文章

我对eBPF的偏见

微软启动新的开源项目 将Linux工具eBPF引入Windows

XDP/eBPF — eBPF

XDP/eBPF — eBPF

XDP/eBPF — BPF

XDP/eBPF — BPF