eBPF CO:RE:vmlinux.h 不完整?
eBPF CO:RE: vmlinux.h incomplete?
我正在尝试使用 libbpf 进入 eBPF CO:RE。我的程序使用跟踪点 SEC("tracepoint/syscalls/sys_enter_kill")),我想知道如何获取参数以及为什么它们不包含在 vmlinux.h 中。
此外,似乎缺少 define BPF_F_CURRENT_CPU 。或者可以组合 vmlinux.h 和 uapi/linux/bpf.h?
我的设置:
- Ubuntu 20.04
- 内核 5.4.0-90-generic
- bpftool --version: ./bpftool v5.16.0-rc2 - features: libbfd, skeletons (来自 Kernel github, 应该是最新的)
- vmlinux.h 用 bpftool 生成 btf 转储文件 /sys/kernel/btf/vmlinux 格式 c > vmlinux.h
我知道已经有关于如何获取参数的问题 ()。但是,这并没有回答我的问题(为什么它不在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;
};
这看起来很奇怪。有人知道哪里出了问题吗?
提前致谢! :)
唯一的原因是它不起作用,因为在这种情况下变量和大小的格式不匹配。
检查此文件的格式 sys_kill_enter
/sys/kernel/debug/tracing/events/syscalls/sys_enter_kill/format
尽管 pid 字段是 pid_t 并且 sig 是 int.
但是如果你检查 pid 和 sig 的大小都是 8。
所以 long
在 pid_t
没有的地方工作。
我正在尝试使用 libbpf 进入 eBPF CO:RE。我的程序使用跟踪点 SEC("tracepoint/syscalls/sys_enter_kill")),我想知道如何获取参数以及为什么它们不包含在 vmlinux.h 中。 此外,似乎缺少 define BPF_F_CURRENT_CPU 。或者可以组合 vmlinux.h 和 uapi/linux/bpf.h?
我的设置:
- Ubuntu 20.04
- 内核 5.4.0-90-generic
- bpftool --version: ./bpftool v5.16.0-rc2 - features: libbfd, skeletons (来自 Kernel github, 应该是最新的)
- vmlinux.h 用 bpftool 生成 btf 转储文件 /sys/kernel/btf/vmlinux 格式 c > vmlinux.h
我知道已经有关于如何获取参数的问题 (
我的代码:
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;
};
这看起来很奇怪。有人知道哪里出了问题吗? 提前致谢! :)
唯一的原因是它不起作用,因为在这种情况下变量和大小的格式不匹配。 检查此文件的格式 sys_kill_enter /sys/kernel/debug/tracing/events/syscalls/sys_enter_kill/format
尽管 pid 字段是 pid_t 并且 sig 是 int.
但是如果你检查 pid 和 sig 的大小都是 8。
所以 long
在 pid_t
没有的地方工作。