eBPF摸着石头过河之一行代码8个报错

解决特定内核版本加载ebpf失败的问题 1.分析特定内核版本加载ebpf失败的原因 2.演示使用 vscode 和 gdb 调试的方法 阅读详情
AI摘要

本文记录了在学习eBPF过程中遇到的八个错误,包括callunknown#179、R0invalidmemaccess、infiniteloopdetected、invalidindirectaccesstostack和函数返回类型错误等问题。错误涉及内核函数查找、内存访问、循环限制等,每个错误都提供了原因分析和解决策略,如升级内核、添加空指针检查、避免循环等。

eBPF摸着石头过河之一行代码8个报错

最近在进行eBPF的学习,学习过程中难免一步一个深渊,为了给大家一些排错参考,留下点笔记,权当抛砖引玉。

本文可能根据情况适时更新。

如果你也遇到一些棘手问题欢迎一块交流。

个人开发系统为Ubuntu22.04,内核版本Linux 5.15。

文章中不只是给出解决办法(没有解决办法的会给出原因)包含报错信息、报错现场代码以及分析和解决过程。

因为大家代码不同,给出这些信息时希望朋友们在遇到相似报错的情况下,能有自己解决问题的能力或者提供一个解决思路。网上很多文章在解决代码报错时都是只给出解决方法,但是没有分析方法,这样不利于成长。授人以鱼不如授人以渔。

0x01.call unknown#179/invalid func unknown#179/invalid func unknown#179

背景介绍

在使用libpf进行eBPF开发过程中,程序编译通过,但是加载却出错。

出错代码如下:

u64 address = 0;
bpf_kallsyms_lookup_name("selinux_xfrm_state_free", strlen("selinux_xfrm_state_free") + 1, 0, &address);

本意是想通过该函数获取内核函数selinux_xfrm_state_free的地址,结果eBPF程序却报错了。

报错信息

19: (85) call unknown#179
invalid func unknown#179
processed 19 insns (limit 1000000) max_states_per_insn 0 total_states 1 peak_states 1 mark_read 1
-- END PROG LOAD LOG --
libbpf: prog 'do_unlinkat': failed to load: -22
libbpf: failed to load object 'kprobe_bpf'
libbpf: failed to load BPF skeleton 'kprobe_bpf': -22

原因分析

其中,179是libbpf中help function bpf_kallsyms_lookup_name的序号。
可以看一下其声明

/*
 * bpf_kallsyms_lookup_name
 *
 *      Get the address of a kernel symbol, returned in *res*. *res* is
 *      set to 0 if the symbol is not found.
 *
 * Returns
 *      On success, zero. On error, a negative value.
 *
 *      **-EINVAL** if *flags* is not zero.
 *
 *      **-EINVAL** if string *name* is not the same size as *name_sz*.
 *
 *      **-ENOENT** if symbol is not found.
 *
 *      **-EPERM** if caller does not have permission to obtain kernel address.
 */
static long (*bpf_kallsyms_lookup_name)(const char *name, int name_sz, int flags, __u64 
*res) = (void *) 179;

根据报错提示信息,是eBPF在加载过程中不认识这个函数,于是到/proc/kallsyms文件里看一眼。

# cat /proc/kallsyms |grep .*kallsyms_lookup_name
ffffffffb3f8b5e0 T module_kallsyms_lookup_name
ffffffffb3f8bfd0 T kallsyms_lookup_name

果然没有!!!于是我就慌了~~
如果我的开发环境没有该函数,意味着如果要使用该函数,就只能替换新内核了。

然后查了查Linux各个版本内核源码(该函数被定义在kernel/bpf/syscall.c文件里)。
结果这个函数是在Linux 5.16的版本定义的,而我的是Linux 5.15,我勒个大去~~

以下为该函数在Linux5.16中的实现

BPF_CALL_4(bpf_kallsyms_lookup_name, const char *, name, int, name_sz, int, flags, u64 *, res){
        if (flags)
                return -EINVAL;

        if (name_sz <= 1 || name[name_sz - 1])
                return -EINVAL;

        if (!bpf_dump_raw_ok(current_cred()))
                return -EPERM;

        *res = kallsyms_lookup_name(name);
        return *res ? 0 : -ENOENT;
}

MMP!这么方便且有用又nb的函数为啥实现得这么晚!!!

解决办法

  1. 升级内核到Linux5.16或以上;
  2. 通过应用层读取/proc/kallsyms文件来获取符号地址给到内核eBPF;
  3. 编写LKM,然后导出同名函数;

0x02 R0 invalid mem access ‘map_value_or_null’/libbpf: prog ‘handle_tp_enter_kill’: failed to load: -13

报错信息

; xx = bpf_map_lookup_elem(&aa, &i);
64: (18) r1 = 0xffff8dc8e5366400
66: (85) call bpf_map_lookup_elem#1
; xx = bpf_map_lookup_elem(&aa, &i);
67: (18) r1 = 0xffff9addc0568000
69: (7b) *(u64 *)(r1 +0) = r0
 R0=map_value_or_null(id=5,off=0,ks=8,vs=72,imm=0) R1_w=map_value(id=0,off=0,ks=4,vs=8,imm=0) R6=inv1111 R7=invP0 R8=inv1 R9=inv(id=0,smax_value=9223372032559808512,umax_value=18446744069414584320,var_off=(0x0; 0xffffffff00000000),s32_min_value=0,s32_max_value=0,u32_max_value=0) R10=fp0 fp-8=mmmmmmmm fp-16=mmmm?mmm fp-24=mmmmmmmm fp-32=mmmmmmmm fp-40=mmmmmmmm
70: (b7) r1 = 686134
; bpf_printk("%-18s:addr:0x%-16x\n", xx->name, xx->addr);
71: (63) *(u32 *)(r10 -24) = r1
72: (18) r1 = 0x312d2578303a7264
74: (7b) *(u64 *)(r10 -32) = r1
75: (18) r1 = 0x64613a7338312d25
77: (7b) *(u64 *)(r10 -40) = r1
78: (79) r4 = *(u64 *)(r0 +64)
R0 invalid mem access 'map_value_or_null'
processed 69 insns (limit 1000000) max_states_per_insn 0 total_states 4 peak_states 4 mark_read 2
-- END PROG LOAD LOG --
libbpf: prog 'handle_tp_enter_kill': failed to load: -13
libbpf: failed to load object 'inline_check_bpf'
libbpf: failed to load BPF skeleton 'inline_check_bpf': -13

报错代码行

xx = bpf_map_lookup_elem(&aa, &i);
bpf_printk("%-18s:addr:0x%-16x\n", xx->name, xx->addr);

原因分析

根据提示,在源码bpf_printk("%-18s:addr:0x%-16x\n", xx->name, xx->addr);这一行,可能会出现’map_value_or_null’。这里面要打印的也就是xx指针指向的内容。
所以推测这里是校验器发现我们的代码里没有进行空指针检查。

代码修复

加个判断就OK了。

xx = bpf_map_lookup_elem(&aa, &i);
if (xx)
    bpf_printk("%-18s:addr:0x%-16x\n", xx->name, xx->addr);

0x03 infinite loop detected at insn 85/libbpf: prog ‘handle_tp_enter_kill’: failed to load: -22

报错信息

82: (b7) r2 = 20
83: (bf) r3 = r0
84: (85) call bpf_trace_printk#6
last_idx 84 first_idx 73
regs=4 stack=0 before 83: (bf) r3 = r0
regs=4 stack=0 before 82: (b7) r2 = 20
; for (; j < 3; ++j)
infinite loop detected at insn 85
processed 99 insns (limit 1000000) max_states_per_insn 1 total_states 6 peak_states 6 mark_read 2
-- END PROG LOAD LOG --
libbpf: prog 'handle_tp_enter_kill': failed to load: -22
libbpf: failed to load object 'inline_check_bpf'
libbpf: failed to load BPF skeleton 'inline_check_bpf': -22

报错代码

int j = 0;
for (; j < 3; ++j)  // 报错指向这里
{
	xx = bpf_map_lookup_elem(&aa, &j);
	if (xx)
		bpf_printk("%-18s:addr:0x%-16x\n", xx->name, xx->addr);
	else
		bpf_printk("xx = NULL\n");
}

分析

意思是检测了无限循环(infinite loop detected),但我这里明明只循环3次。

通过查阅资料发现eBPF不支持循环,如果一定要是要使用参考这篇文章:《[eBPF的使用限制 | REXROCK](https://rexrock.github.io/post/ebpf1/#:~:text=eBPF程序中不能使用循环语句,如果非要使用循环,则必须通过编译选项" %23pragma clang,loop unroll (full) "让编译器在编译过程中将循环展开。)》

我摘抄一些

eBPF程序中不能使用循环语句,如果非要使用循环,则必须通过编译选项“#pragma clang loop unroll(full)”让编译器在编译过程中将循环展开。

此外必须要注意的一点是,循环中的语句必须是单一且独立的块,如下:
    
static __always_inline int search_service_ip(int i, __u32 ip) {
	...
	return 0;
}

static __always_inline int is_service_ip(__u32 ip) {
#pragma clang loop unroll(full)
	for(int i = 0; i < 32; i++) {
		switch(search_service_ip(i, ip)) {
			case 0:
				continue;
			case 1:
				return 1;
			default:
				break;
		}
	}
	return 0;
}

解决方法

没有什么好的解决办法。

循环次数不多的情况下,就手动展开。我尝试过用goto语句代替循环,行不通(没试过用递归代替循环)。

或者你的代码满足“单一且独立的块”,则可以通过内联的方式让编译器展开。

如果你有什么好的方法,还请留言~~~

0x04 invalid indirect access to stack R2 off=-4 size=8/libbpf: prog ‘handle_tp_enter_kill’: failed to load: -13

报错信息

; xx = bpf_map_lookup_elem(&aa, &i);
63: (18) r1 = 0xffff8dc8cbbc9400
65: (85) call bpf_map_lookup_elem#1
invalid indirect access to stack R2 off=-4 size=8
processed 59 insns (limit 1000000) max_states_per_insn 0 total_states 3 peak_states 3 mark_read 2
-- END PROG LOAD LOG --
libbpf: prog 'handle_tp_enter_kill': failed to load: -13
libbpf: failed to load object 'inline_check_bpf'
libbpf: failed to load BPF skeleton 'inline_check_bpf': -13
Failed to open and load BPF skeleton

### 报错原因以及解决

对map变量的访问错误。

可能的原因:

  1. map变量定义的max_entries过小;
  2. map变量的索引值类型不对,比如:
struct {
	__uint(type, BPF_MAP_TYPE_ARRAY);
	,,,,
	__type(key, unsigned long);
	__type(value, xx);
} my_pid_map SEC(".maps");


int key = 0;			// 此处变量key类型错误   正确为 unsigned long key = 0;
bpf_map_lookup_elem(&aa, &key);

0x05 Validating my_function() func#1…/Global function my_function() doesn’t return scalar. Only those are supported.

报错信息

Validating my_function() func#1...
Global function my_function() doesn't return scalar. Only those are supported.
processed 0 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
-- END PROG LOAD LOG --
libbpf: prog 'handle_tp_enter_kill': failed to load: -22

分析与解决

emmmm… 貌似是自定义的全局函数(我这里为my_function)没有相应的return语句,或者return类型不对。

libbpf 开发指南:设置libbpf的函数回调打印 做最好的BPF教程 阅读详情

相关推荐

libbpf 开发指南:加载BPF 程序”

做最好的libbpf 教程

阿呆的隐秘角落 1191

#pragma unroll的作用

比如每次i的初始化,每次做一个判断,还有i++的计算,一旦展开后全部没有了,第二就是当循环里有load数据和计算数据的话,展开后可以让这两个pipe并行执行,隐藏延迟。需要N是常量,不然编译器不认识,没法展开,这个在CPU和GPU上优化都是通用的,再说一次他是。

feng__shuai的博客 815

eBPF:arm平台上的uprobe

讲解 arm 32 平台,libbpf-bootstrap 框架上的 uprobe 使用方法;

qq_38232169的博客 1717

#pragma-学习笔记

因此,在使用 #pragma clang loop unroll(full) 指令时,应该根据具体情况仔细评估其对性能的影响,并确保不会导致不必要的代码膨胀和性能下降。循环展开是一种优化技术,它通过将循环体中的多次迭代展开成单独的语句,以减少循环的迭代次数和减少循环控制开销。完全展开循环意味着将循环中的所有迭代都展开成单独的语句,从而完全消除了循环的迭代次数和循环控制开销。这可以在一定程度上提高代码的性能,特别是对于迭代次数较少且迭代次数在编译时已知的循环来说,完全展开循环可能会更有效地利用计算资源。

mrtyxr的博客 679

#pragma clang的使用

文章目录-Wdeprecated-declarations 去除方法弃用告警-Wincompatible-pointer-types 不兼容指针类型-Warc-retain-cycles 循环引用-Wunused-variable 未使用变量-Wcovered-switch-default 未使用default -Wdeprecated-declarations 去除方法弃用告警 #pragma clang diagnostic push #pragma clang diagnostic ignore

y2015的博客 1127

聚类分析(超全超详细版)

聚类分析是一种典型的无监督学习, 用于对未知类别的样本进行划分,将它们按照一定的规则划分成若干个类族,把相似(距高相近)的样本聚在同一个类簇中, 把不相似的样本分为不同类簇,从而揭示样本之间内在的性质以及相互之间的联系规律 聚类算法在银行、零售、保险、医学、军事等诸多领域有着广泛的应用 聚类分析基础 聚类分析的概念 聚类分析是根据在数据中发现的描述对象及其关系的信息,将数据对象分组。目的是...

Gfqwy2021 22万+

BPF示例在64位ARM上的调试

eBPF调试工具 eBPF作为Linux内核中调试功能强大的子系统,相应的应用层调试工具有很多,例如bpftool/bpftrace,以及提供了Lua和Python调试接口的bcc;内核信息收集、性能分析的开源工具SystemTap也用到了内核的eBPF功能。不过笔者希望在嵌入式设备上使用eBPF提供的调试功能,一种方法是使用开源的SDK(例如yocto,笔者未尝试过)自动化构建这些调试工具(否则就需要手动交叉编译);另一种方法是将某个支持arm64架构的Linux发行版(例如运行debian系统的树莓派)

yeholmes的专栏 5110

28、解码清华SSVEP基准数据集:从EEG信号到深度学习模型的实战指南

本文详细解析了清华大学SSVEP基准数据集的处理与深度学习模型构建全流程。从EEG信号预处理、数据维度变换到SSVEP分类模型设计,提供了实战代码示例和关键技巧,帮助读者掌握脑机接口领域的数据处理方法与模型优化策略。

weixin_29029995的博客 176

边缘网络 eBPF 超能力:eBPF map 原理与性能解析

导读 众所周知,大型 eBPF 程序构建过程中 eBPF map 必不可少。火山引擎边缘计算在数据面也大量使用了 eBPF 及其 map 机制。如何用好 map 是 eBPF 网络编程中关键的一环,不同 map 的性能差异也较大。本文组织 eBPF map 相关的底层实现,为大家详细解析 eBPF map 的原理及性能。

VE_Edge的博客 4698

第5.1节 构网控制:电压源特性与电压调节

摘要: 构网型变流器的核心物理本质是作为可控交流电压源,通过戴维南等效模型(受控电势源+低输出阻抗)自主建立并调节电压,增强电网电压刚度。其电压调节通过无功-电压闭环控制实现,内环为自动电压调节器(AVR),外环为无功-电压下垂特性,模拟同步发电机功能。关键技术包括自适应增益、虚拟阻抗补偿及动态限流策略,以应对弱电网稳定性、故障过流等问题。该设计满足《规范》对电压调节范围、动态无功支撑的要求,是高比例新能源电力系统电压稳定的关键解决方案。

技术工作者 1107

vivado运行时报错 license文件更新操作

vivado使用:SYNTHESIS->Run Synthesis 时报错 [Common 17-345] A valid license was not found for feature ‘Synthesis’ and/or device ‘xc7vx485t’. Please run the Vivado License Manager for assistance in determiningwhich features and devices are licensed for your sy

qq_44692266的博客 2万+

libbpf-tools编译和使用步骤

libbpf-tools的编译和运行

chensong_2000的博客 6430

深入详细分析Skyworks 78113 PAMiD射频方案图

是一种高集成度的射频前端模块,它结合了功率放大器(PA)、双工器(Duplexer)、滤波器和开关等多个组件,主要用于4G LTE移动通信系统中的射频前端链路。是一款典型的PAMiD模块,通过将多个频段的发射(TX)和接收(RX)路径集成在一个模块中,大大减少了传统射频设计中的分立器件,同时提高了信号链路的效率和可靠性。模块化设计的优势PCB空间优化:高集成度设计大幅减小了射频电路的尺寸,适用于紧凑型移动设备。减少设计复杂度:模块化设计减少了器件间的匹配与调试工作,加速产品开发周期。性能稳定性。

weixin_43199439的博客 1480

探索eBPF:Linux内核的黑科技

eBPF 是一个基于寄存器的虚拟机,使用自定义的 64 位 RISC 指令集,能够在 Linux 内核内运行即时本地编译的 “BPF 程序”,并能访问内核功能和内存的一个子集。这是一个完整的虚拟机实现,不要与基于内核的虚拟机(KVM)相混淆,后者是一个模块,目的是使 Linux 能够作为其他虚拟机的管理程序。eBPF 也是主线内核的一部分,所以它不像其他框架那样需要任何第三方模块(LTTng 或 SystemTap),而且几乎所有的 Linux 发行版都默认启用。

嵌入式Linux内核的博客 1872

Ambari 安装部署教程(手把手教学)【超级详细】

本文为读者提供了一套超详细的 Apache Ambari 安装部署教程,采用手把手教学方式,适合初学者及大数据运维人员。内容涵盖前期准备、环境配置、Ambari Server 与 Agent 安装、集群初始化配置、常见问题排查等全流程操作,确保用户能够顺利搭建稳定的 Hadoop 大数据平台管理系统。

Five的博客 1万+

Allegro172版本DFM功能介绍

Allegro172版本提供了实时DFM检查的功能,让违反要求的地方实时以DRC的形式报出来,让加工不可加工因素在设计的时候同步避免。打开Constraint,选择Manufacturing, 所有DFM相关DRC都在这。把所有下面DRC开关(DFM,DFA,DFT)都选择on,选择OK。这个规则和物理和间距规则等等都没有关系,单独的一个加工规则。这些DRC大部分是正常的,正常的可以去修改规则来消除。等待DRC刷新,板子上会出现很多DRC,类似下图。下面介绍如何调用和产生DFM规则。一直往下点击next。

weixin_54787054的博客 1337

使用 ebpf 深入分析容器网络 dup 包问题

| 导语我们日常工作中都会遇到内核网络相关问题,包括网络不通、丢包、重传、性能问题等等,排查和解决非常困难,而容器更加重了这种复杂度,涉及2、3层转发,iptable,ipvs,namespace等,即使是网络专家,面对如此复杂的环境也非常的头大的。我们基于eBPF开发了skbtracer工具,不用修改内核代码,不用编写内核模块而能够使Linux主机网络变成一个彻底的白盒,能够看到一个数据包从产生发出去的全部过程,包括不限于数据流、namespace、路由信息跟踪,丢包原因,netfilter处...

flynetcn的专栏 1097

8.30华为OD机试真题 新系统 - 单词搜索计数 (Java/Py/C/C++/Js/Go)

8.30华为OD机试真题 新系统 - 单词搜索计数 (Java/Py/C/C++/Js/Go)

算法大师! 598

eBPF专题一 | 手把手教你用eBPF诊断MySQL(含源码)

当我们定义uprobe时,内核会在附加的指令上创建快速断点指令,当程序执行到该指令时,内核将触发事件,程序陷入到内核态,并以回调函数的方式调用探针函数,执行完探针函数再返回到用户态继续执行后序的指令。您的hello world出现了吗?BCC中的宏定义从PT_REGS_PARM1(x)~PT_REGS_PARM5(x),栈传递的参数,是从右边向左压栈,想要获取探测函数的入参可以通过PT_REGS_PARM来获取,但X86寄存器的个数是有限的,BCC的定义是能取到5个参数,对于超过6个参数的不能直接获取。

DBdoctor_off的博客 1386

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

LLVM 编译器学习笔记之七十 -- 添加pragma

1、在中定义了循环关键字。

~ 飞 扬 的 天 空 ~ 899

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

过河

有一条河,河中有n块石头,现在从河的一边只能通过走石头到达对岸,每一步可以跨越至多3个石头。 但是不幸的是,有一块石头被上一个过河的人踩松后被踩松了,所以为了安全后来的人就不能再踩这一块石头了。 若现在有一个人想要到河的对岸去,他有多少种方法? 注:若我们将石头从1到n进行编号的话,那么被踩松的石头编号为k。 输入格式: 多组输入 对于每组输入在一行中给出2个整数n和...

wyyyyyyyy_的博客 848

BPF( 伯克利数据包过滤器 ) Performance Tools》 第五章 bpftrace

bpftrace是一款基于BPF和BCC的开源跟踪器。和BCC一样,bpftrace自带了许多性能工具和支持文档。它同时还提供了一个高级编程语言环境,可以用来创建强大的单行程序和小工具。比如,下面的单行程序以直方图形式统计vfs_read() 的返回值(读取的字节数或错误码) :Alastair Robertson阿拉斯泰尔·罗伯逊Alastair Robertson于2016年12月创建了bpftrace, 当时它还只是一个业余项目。

weixin_55255438的博客 2257

eBPF verifier

eBPF verifier — The Linux Kernel documentation Check if a digraph is a DAG (Directed Acyclic Graph 有向无环图) or not_rtoax的博客-优快云博客 eBPF verifier The safety of the eBPF program is determined in two steps. First step does DAG check to disallow loops and

RToax 896

eBPF编写避坑指南

0x1:基本概念 当使用tracepoint的时候,函数参数如何确认? cat /sys/kernel/debug/tracing/events/syscalls/xxx/format. xxx为要跟踪的函数,在这里有函数参数定义。 0x2:注意事项 写结构体的时候一定要注意内存对齐,防止被编译器优化填充。 使用 LLVM 内置的函数做内存操作 #ifndef memset # define memset(dest, chr, n) __builtin_memset((dest

SenberHu的博客 863

#pragma unroll的用法

在默认情形下,编译器对已知次数的小循环进行展开,#pragma unroll 可以用来控制任意一个给定的循环。但 #pragma unroll必须放在被控制的循环的前面,后面可以带展开次数选项。后跟参数1则编译器不会展开循环。如果没有参数,并且循环次数是一常数时编译器会将循环完全展开,如果不是常数就根本不会展开。

Zender 9638

#pragma UNROLL 4

在图像处理程序中,你会看到类似于#pragma unroll 4这样的语句(注:在DX中,也许你会见到[unroll(3)] for(int i = 0;i < Total; i++)类似情况),现具体解释如下:  语法: #pragma UNROLL(n) #pragma UNROLL告诉编译器一个循环中应该展开n次(其实我觉得是告诉编译器循环展开n次是安全的),有助于对那些不容易展开的循环有更大概率的进行软件流水。 其实很多很多时候编译器会自动判断各种信息,但是这增加了冗余开销,反倒不如直接

Jackery's Special Column 2947
上一篇: error: failed to run custom build command for `blazesym v0.1.0`
下一篇: kprobe:register_kprobe failed, returned -2
libocdf
博客等级 码龄16年 544粉丝 6原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值