You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

仅用系统调用加载并附着eBPF至sys_enter_execve失败原因排查

问题

我正尝试对eBPF字节码引入变异,因此希望从简单目标文件而非ELF加载eBPF字节码(据我所知libbpf要求使用ELF)。

我编写了一个向跟踪管道输出'hello world'的简单eBPF程序:

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>

int hello_world(void *ctx) {

    char msg[12] = "Hello World\n";
    bpf_trace_printk(msg, sizeof(msg));
    return 0;
}

我按以下步骤加载并将该程序附着到sys_enter_execve:

  • 将程序从目标文件加载到缓冲区
  • 调用bpf(BPF_PROG_LOAD)
  • 调用perf_event_open(PERF_TYPE_TRACEPOINT, sys_enter_execve_id)
  • 调用bpf(BPF_LINK_CREATE, prog_fd, perf_fd)
  • 调用ioctl(perf_fd, PERF_EVENT_IOC_ENABLE, 0)

这与libbpf等效程序执行的系统调用序列一致,且所有系统调用均返回成功。但使用bpftool prog show查看eBPF程序时,显示程序未附着:

46: tracepoint  tag 57b707c2f01b5190  gpl
    loaded_at 2023-09-05T16:55:39+0100  uid 0
    xlated 88B  jited 61B  memlock 4096B

而libbpf等效程序的bpftool输出显示程序已附着:

51: tracepoint  name tracepoint__syscalls__sys_enter_execve  tag 327022fb55d6e9d0  gpl
    loaded_at 2023-09-05T16:59:45+0100  uid 0
    xlated 48B  jited 35B  memlock 4096B  map_ids 27
    btf_id 69

显然我的系统调用序列无法将eBPF程序附着到sys_enter_execve,而libbpf的序列可以。这是为什么?

以下是我传递给系统调用的具体参数:

//open performance event
int perf_fd;
struct perf_event_attr pe = {
    .type = PERF_TYPE_TRACEPOINT,
    .size = sizeof(struct perf_event_attr),
    .config = EXECVE_ID

    .sample_period = 1,
    .sample_type = PERF_SAMPLE_RAW,
    .wakeup_events = 1,

    .disabled = 0,
    .bpf_event = 1
};
perf_fd = syscall(__NR_perf_event_open, &pe, -1, 0, -1, PERF_FLAG_FD_CLOEXEC);


//load the bpf program
int prog_fd;
union bpf_attr load_attr;
memset(&load_attr, 0, sizeof(load_attr));
load_attr.prog_type = BPF_PROG_TYPE_TRACEPOINT;
load_attr.insn_cnt = INSTRUCTION_COUNT;
load_attr.insns = (__aligned_u64) insns_buf;
load_attr.license = "GPL";

prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &load_attr, sizeof(load_attr));


//create bpf link(?)
int link_fd;
union bpf_attr link_attr;
link_attr.link_create.prog_fd = prog_fd;
link_attr.link_create.target_fd = perf_fd;
link_attr.link_create.attach_type = BPF_PERF_EVENT;
link_attr.link_create.flags = 0;
int link_fd = syscall(__NR_bpf, BPF_LINK_CREATE, &link_attr, sizeof(link_attr));

ret = ioctl(perf_fd, PERF_EVENT_IOC_ENABLE, 0);

libbpf版本的strace片段:

openat(AT_FDCWD, "/sys/kernel/debug/tracing/events/syscalls/sys_enter_execve/id", O_RDONLY) = 6
newfstatat(6, "", {st_mode=S_IFREG|0440, st_size=0, ...}, AT_EMPTY_PATH) = 0
read(6, "807\n", 4096)                  = 4
read(6, "", 4096)                       = 0
close(6)                                = 0
perf_event_open({type=PERF_TYPE_TRACEPOINT, size=0x88 /* PERF_ATTR_SIZE_??? */, config=807, sample_period=0, sample_type=0, read_format=0, precise_ip=0 /* arbitrary skid */, ...}, -1, 0, -1, PERF_FLAG_FD_CLOEXEC) = 6
bpf(BPF_LINK_CREATE, {link_create={prog_fd=5, target_fd=6, attach_type=BPF_PERF_EVENT, flags=0, perf_event={bpf_cookie=0}}}, 48) = 7
ioctl(6, PERF_EVENT_IOC_ENABLE, 0)      = 0

分析与解决

对比你的代码和libbpf的系统调用参数,核心问题出在**perf_event_attr配置、BPF链接初始化细节**上:

1. perf_event_attr的错误配置

你的perf_event_attr存在两个关键问题:

  • 语法错误:.config = EXECVE_ID后缺少逗号,会导致编译异常(即使系统调用返回成功,实际运行代码可能存在隐式问题)
  • 冗余采样参数:libbpf的配置中sample_period和sample_type均为0,而你设置了sample_period=1、sample_type=PERF_SAMPLE_RAW。仅用于附着BPF程序的tracepoint事件不需要这些采样参数,冗余配置可能干扰内核的附着逻辑
  • disabled字段:你设置为0,而libbpf默认设为1,再通过ioctl启用。虽然不是核心问题,但保持一致更稳妥

2. BPF链接创建时的未初始化问题

你没有对union bpf_attr link_attr做清零初始化(比如memset),可能导致无关字段残留垃圾值,干扰内核的链接创建流程。libbpf会确保所有无关字段被清零。

3. 程序元数据缺失(非功能影响)

libbpf会自动为BPF程序设置名称并关联BTF信息,这是bpftool能显示完整附着状态的原因。你的手动加载方式未设置这些元数据,但这不是程序未附着的直接原因,仅会导致bpftool显示信息不全。

修正后的代码示例

//open performance event
int perf_fd;
struct perf_event_attr pe = {
    .type = PERF_TYPE_TRACEPOINT,
    .size = sizeof(struct perf_event_attr),
    .config = EXECVE_ID, // 补上缺失的逗号
    .disabled = 1, // 先禁用,后续通过ioctl启用
    .bpf_event = 1
};
// 采样相关字段保持默认0即可,无需额外配置
perf_fd = syscall(__NR_perf_event_open, &pe, -1, 0, -1, PERF_FLAG_FD_CLOEXEC);


//load the bpf program
int prog_fd;
union bpf_attr load_attr;
memset(&load_attr, 0, sizeof(load_attr));
load_attr.prog_type = BPF_PROG_TYPE_TRACEPOINT;
load_attr.insn_cnt = INSTRUCTION_COUNT;
load_attr.insns = (__aligned_u64) insns_buf;
load_attr.license = "GPL";
load_attr.prog_name = (__aligned_u64)"hello_world"; // 可选:设置程序名称,方便bpftool识别

prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &load_attr, sizeof(load_attr));


//create bpf link
int link_fd;
union bpf_attr link_attr;
memset(&link_attr, 0, sizeof(link_attr)); // 关键:初始化整个union,避免垃圾值干扰
link_attr.link_create.prog_fd = prog_fd;
link_attr.link_create.target_fd = perf_fd;
link_attr.link_create.attach_type = BPF_PERF_EVENT;
link_attr.link_create.flags = 0;
link_fd = syscall(__NR_bpf, BPF_LINK_CREATE, &link_attr, sizeof(link_attr));

ret = ioctl(perf_fd, PERF_EVENT_IOC_ENABLE, 0);

额外注意事项

  • 确保EXECVE_ID是从/sys/kernel/debug/tracing/events/syscalls/sys_enter_execve/id文件中正确读取的值
  • 不要提前关闭link_fd:内核通过该文件描述符维护BPF链接的生命周期,关闭后链接会被销毁,程序将不再附着
  • 如果需要bpftool显示完整的附着信息,可手动关联BTF,但这对程序功能无影响,仅用于调试

内容的提问来源于stack exchange,提问作者vykt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 11:28:11