使用bpf_program__attach_uprobe_multi附加多Uprobe遇无效参数错误求助
我需要对指定可执行文件中匹配op_*模式的函数做调用计数,尝试使用新推出的bpf_program__attach_uprobe_multi API,但找不到相关示例。现有代码执行时触发「无效参数」错误,在libbpf中添加打印后确认路径、模式、变量均正常读取到(libbpf日志显示:函数模式op_*,路径/data/expr/operations,匹配到2个函数,偏移量4521、4538),但仍报错:
libbpf: prog 'test_uprobe': failed to attach multi-uprobe: Invalid argument
当前使用的内核BPF代码如下:
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause int main(int argc, char **argv) { size_t offset1; size_t offset2; int err, i; char filename[256]; pid_t pid; struct bpf_object *obj = NULL; struct bpf_program *prog[2]; static struct bpf_link *bpflinks[2]; static struct bpf_link *ubpflinks[2]; if (argc < 2) { printf("Error return \n"); return 0; } pid = strtoul(argv[1], NULL, 10); snprintf(filename, 256, "/proc/%d/exe", pid); offset1 = get_elf_func_offset("/data/expr/operations", "op_add"); offset2 = get_elf_func_offset("/data/expr/operations", "op_sub"); snprintf(filename, sizeof(filename), "%s.bpf.o", argv[0]); obj = bpf_object__open_file(filename, NULL); if (libbpf_get_error(obj)) { fprintf(stderr, "ERROR: opening BPF object file failed\n"); obj = NULL; goto cleanup; } /* load BPF program */ if (bpf_object__load(obj)) { fprintf(stderr, "ERROR: loading BPF object file failed\n"); goto cleanup; } prog[0] = bpf_object__find_program_by_name(obj, "handle_uprobe_ref_ctr"); // prog[1] = bpf_object__find_program_by_name(obj, "handle_uretprobe_ref_ctr"); ubpflinks[0] = bpf_program__attach_uprobe(prog[0], false /* not uretprobe */, pid, "/data/expr/operations" , offset1); if (!ubpflinks[0]) printf("failed at link0\n"); ubpflinks[1] = bpf_program__attach_uprobe(prog[0], false /* not uretprobe */, pid, "/data/expr/operations" , offset2); if (!ubpflinks[1]) printf("failed at link1\n"); // bpflinks[0] = bpf_program__attach(prog[0]); // do we need to attach already probes attached getchar(); cleanup: bpf_link__destroy(ubpflinks[0]); bpf_link__destroy(ubpflinks[1]); return -err; } char LICENSE[] SEC("license") = "Dual BSD/GPL"; SEC("uprobe") int handle_uprobe_ref_ctr(struct pt_regs *ctx) { const char fmt_str[] = "function fp %lld ip %lld\n"; bpf_trace_printk(fmt_str, sizeof(fmt_str), (void *)PT_REGS_FP(ctx)); return 0; } SEC("uretprobe") int handle_uretprobe_ref_ctr(struct pt_regs *ctx) { bpf_printk("handle_uretprobe_ref_ctr \n"); return 0; }
排查方向与修复方案
1. 内核版本兼容性检查
bpf_program__attach_uprobe_multi是内核5.19才引入的API,如果你的内核版本低于该版本,调用必然返回「无效参数」错误。执行uname -r查看内核版本,若版本不足需升级内核。
2. 正确使用bpf_program__attach_uprobe_multi API
你当前代码使用的是单个探针的bpf_program__attach_uprobe,切换到多探针API时需按以下方式调用,必须初始化opts.sz字段(libbpf用于识别结构体版本,遗漏会导致参数错误):
struct bpf_uprobe_multi_opts opts = { .sz = sizeof(opts), .binary_path = "/data/expr/operations", .func_pattern = "op_*", // 直接用通配符,无需手动获取偏移 .pid = pid, // 传0监控所有进程,指定pid则只监控目标进程 .is_retprobe = false, // false对应uprobe,true对应uretprobe }; ubpflinks[0] = bpf_program__attach_uprobe_multi(prog[0], &opts); if (libbpf_get_error(ubpflinks[0])) { fprintf(stderr, "Failed to attach multi-uprobe: %s\n", strerror(errno)); ubpflinks[0] = NULL; goto cleanup; }
3. 权限验证
挂载uprobe需要足够权限,需以root用户运行程序,或给程序添加必要capability:
sudo setcap cap_bpf,cap_perfmon+ep ./your_executable
4. 二进制文件符号检查
确保/data/expr/operations包含完整的符号表,libbpf需要通过符号解析匹配op_*函数。执行nm /data/expr/operations | grep op_,如果无法看到op_add和op_sub符号,说明二进制被剥离了符号信息,需要重新编译保留符号。
5. 程序类型匹配
确认BPF程序的SEC段与挂载类型一致:
- 挂载普通uprobe时,BPF程序需标记为
SEC("uprobe") - 挂载uretprobe时,BPF程序需标记为
SEC("uretprobe"),同时API调用中is_retprobe设为true
内容的提问来源于stack exchange,提问作者nullptr

