GCC 16及以后版本中的BPF支持
摘要
何塞·马奇西(José Marchesi)和GCC-BPF团队提供了GCC 16中BPF支持的更新,突出了在与LLVM功能对等方面取得的进展,以及内核BPF自测通过率的提升。
<p><a href="https://lobste.rs/s/q5enyi/bpf_support_gcc_16_beyond">评论</a></p>
查看缓存全文
缓存时间: 2026/06/02 15:50
# GCC 16 及后续版本中的 BPF 支持
来源:https://lwn.net/SubscriberLink/1071973/19e2866f07249dfb/
> ### 欢迎来到 LWN.net
> 以下仅限订阅用户阅读的内容已由一位 LWN 订阅者提供给您。成千上万的订阅者依赖 LWN 获取来自 Linux 和自由软件社区的最佳新闻。如果您喜欢这篇文章,请考虑[订阅 LWN](https://lwn.net/subscribe/)。感谢您访问 LWN.net!
José Marchesi 和 GCC-BPF 开发者在 2026 年 [Linux 存储、文件系统、内存管理和 BPF 峰会](https://events.linuxfoundation.org/lsfmmbpf/) 的 BPF 分会场中,以 90 分钟的总结拉开了序幕,介绍了过去一年 GCC 对 BPF 支持的变化。这类会议已成为一种传统。此前在 [2025 年](https://lwn.net/Articles/1015747/) 和 [2024 年](https://lwn.net/Articles/975412/) 也有类似的更新。这一次,GCC 似乎在功能上与 LLVM 工具链趋于一致——正如[幻灯片](https://drive.google.com/file/d/1MLTPaBBCTAVwN31fC8FhfGDq2Uq18uOT/view)所详述的那样。
Marchesi 表示,通常 GCC-BPF 开发者来参加会议时,会演讲一小时,回答一些问题,然后讨论就结束了。他今年想做得更好,并承诺在整个会议期间保持可联系状态。他希望在会议上讨论剩下的少数修复工作,以便 GCC 能够通过内核的 BPF 自测,这些内容在后半部分的演讲中会详细说明。
[![[José Marchesi]](https://static.lwn.net/images/2026/jos%C3%A9-marchesi-small.png)](https://lwn.net/Articles/1072530#marchesi)
Marchesi 继续说道,现在整个 GNU 工具链都支持 BPF。当然包括 GCC,但也包括像 [binutils](https://sourceware.org/binutils/)、 [DejaGNU](https://www.gnu.org/software/dejagnu/)、 [GNU poke](https://www.gnu.org/software/poke/) 甚至 [GDB](https://www.sourceware.org/gdb/) 这样的项目都支持 BPF。然而,其中一些“支持”并未得到及时更新。例如,GDB 的 BPF 模拟器使用不多,因此已经过时了。尽管如此,工具链的其他组件正在取得良好进展。
GCC 16.1 已于 4 月 30 日[发布](https://lwn.net/ml/all/170o3r2r-3r4s-opp9-q8or-2no672o6q390%40fhfr.qr/)。这是首次包含 Vineet Gupta 工作的版本,他最近加入了 GCC-BPF 团队,并且他贡献的质量“已经让我们其他人相形见绌”。现在还有一个专门讨论 BPF 的 GCC 邮件列表:[email protected](https://gcc.gnu.org/mailman/listinfo/bpf),以及每周一在 [Software Freedom Conservancy 的 BBB 实例](https://bbb-new.sfconservancy.org/rooms/wgy-wvm-vyt-id4/join) 上举行的会议。“这很有趣。我们很开心。如果你无聊的话,周一可以来……”
与此同时,GCC 现在能够通过越来越多的内核 BPF 自测(601 个,共 5488 个)[Faust 后来写信解释说我误读了自测输出,实际上是 713 个测试中的 601 个,包含 5488 个子测试]。Marchesi 说,许多剩余的问题只出现在大量测试中,因此需要修复的细节相对较少,就能让自测全部通过。不过,即使在那之前,GCC 也是可以用来编译 systemd 使用的许多简单 BPF 程序的。一些发行版(如 Gentoo)将 GCC 作为默认的 BPF 编译器,这很好,因为这意味着 GCC 开发者能收到真实的 bug 报告。
GCC 还对 [Solana](https://solana.com/) 使用的 BPF 变体提供了实验中支持——这是一个区块链项目,使用 BPF 进行链上合约。“我对这些东西了解不多,”Marchesi 承认。他也不确定为什么他们要使用修改版的 BPF。但既然他们用了,这给了我们借鉴他们一些想法的机会。例如,Solana 有 64 位的乘积、商和余数指令,可能值得合并到 BPF 标准中。
GCC 的其他便利功能也取得了进展。GCC 现在会为 BPF 程序生成行号信息,因此验证器的诊断可以引用具体行号。Marchesi 说,Gupta 一直在处理一些 ABI 错误,代码生成逻辑也有各种修复。特别是, `memmove()` 和 `memset()` 现在能正确内联。
去年 GCC 遇到问题的“一次编译,到处运行”(CO-RE)重定位功能,今年仍然麻烦不断。最终,GCC 团队决定直接实现与 Clang 用于支持该功能相同的属性 pragma 的推送和弹出支持。“我们受够了。所以,我们要实现这些,哪怕只是为了结构体。”
Marchesi 表示,随着 GCC 越来越接近通过内核的 BPF 自测,团队还在 GCC 的测试套件中增加了 BPF 测试。DejaGNU 测试框架中对 BPF 的支持(由 Piyush Raj 添加)对此很有帮助;现在,在 GCC 仓库中运行 `make check` 会自动下载并编译合适的内核,在虚拟机中运行,并用它来运行一系列 BPF 测试。从事工具链其他领域的 GCC 开发者不需要了解任何 BPF 知识就能测试它。希望这能确保 GCC 无关的更改不会影响其生成的 BPF 字节码的可验证性。
在回答听众问题时,Gupta 澄清说,这些测试是作为 GCC 持续集成(CI)测试的一部分运行的,但它们也可以成为内核 CI 测试的一部分。GCC 测试本质上是为了确保使用固定内核版本更改编译器不会破坏任何东西;内核测试则应确保使用固定 GCC 版本更改内核不会导致回归。不过,Marchesi 补充说,两种用途可以共享代码。
他用一张表格总结了所有这些工作的状态:
> [![[一张总结了 GCC 和 LLVM 拥有哪些功能的表格]](https://static.lwn.net/images/2026/bpf-gcc-status.png)](https://lwn.net/Articles/1072530)
与会的内核开发者们认为,表格中唯一缺少的是间接调用和间接跳转的支持状态;除此之外,总结是准确的。
#### CO-RE 问题
此时,Cupertino Miranda 上台更详细地讨论了 GCC 对 CO-RE 重定位的支持细节。为了让 BPF 程序兼容多个内核版本,它们需要能够以正确的偏移量访问内核结构体中的字段,即使这些字段的位置发生了变化。CO-RE 重定位记录了程序需要更新以应对这些变化的位置等内容。C 头文件使用 `preserve-index-access` 属性来指示需要为哪些结构体发出这些重定位。
[![[Cupertino Miranda]](https://static.lwn.net/images/2026/cupertino-miranda-small.png)](https://lwn.net/Articles/1072530#miranda)
Clang 会将结构体属性传播到包含的结构体,而 GCC 则不会。这种不兼容性给 GCC 的 CO-RE 重定位带来了问题。解决方案是添加对推送和弹出编译器 pragma 的支持,该 pragma 指示 GCC 将遇到的每个结构体都视为具有 `preserve-index-access` 属性。
在 Miranda 转向讨论位字段之前,进行了一个关于如何按照核心 GCC 开发者的意愿来实现和合并该功能的简短讨论。不出所料,位字段给 CO-RE 重定位带来了额外的复杂性。Andrii Nakryiko 解释说,内核的网络代码有时会有字段在定义为位字段和整数之间切换,反之亦然。Clang 目前不能正确处理这种情况——它会生成提取位字段的代码,但偏移可能是错误的——这就是为什么网络代码使用宏来封装 CO-RE 可重定位结构体中的“位字段”,并手动执行访问。
Miranda 同意实现正确的可重定位位字段支持是棘手的,并问在场的开发者们,如果实际代码已经使用了宏来解决这个问题,那么是否还有必要真正实现。Nakryiko 认为 GCC 应该尝试生成正确的代码,但是当位字段出现在 CO-RE 可重定位结构体中时,它应该发出警告。Miranda 表示同意,这没问题。
紧凑结构体在代码生成方面存在与位字段相同的一些问题。Nakryiko 说,网络代码确实存在现有的紧凑结构体,因此这些也需要工作。不过将来,网络子系统将朝着更有选择性地使用紧凑结构体的方向发展。在 Miranda 和 Nakryiko 同意离线进一步讨论之前,还有更多的关于实现的讨论。
#### 类型与优化
这时,David Faust 起身谈到了团队在 2025 年讨论过的 [BTF 类型和声明标签问题](https://lwn.net/Articles/1015747/#tags)。GCC 最终支持了与 Clang 相同的一组标签,但支持方式略有不同。DWARF 调试格式以难以扩展而著称,为了获得其他 GCC 维护者的批准,Faust 不得不为添加的标签使用不同的标识符。Faust 说,作为内核构建过程一部分需要处理这些调试信息的 `poke-a-hole` 工具(https://lwn.net/Articles/335942/)能够识别新标识符,因此这应该不是什么大问题。除此区别之外,GCC 和 Clang 现在应该为 BPF 程序生成格式完全相同的调试信息。这个新支持已在 GCC 16 中可用,因此“我们可以开始认真使用它了”。
[![[David Faust]](https://static.lwn.net/images/2026/david-faust-small.png)](https://lwn.net/Articles/1072530#faust)
GCC 团队想提出的最后一点是如何处理内核优化构建改变了暴露给 BPF 的函数原型的情况。例如,GCC 的优化器可以看到某个函数的一个参数始终以固定常量值被调用,从而从函数中消除该参数。然而,BTF 依赖函数签名来允许 BPF 程序查找和调用内核函数。
那个特定情况足够简单,应该可以从 DWARF 调试信息中重建,但有些转换则更复杂。例如,按值传递的结构体可能只传递被访问的字段——这种转换 DWARF 无法表示,并且上游 DWARF 项目也无意支持。Faust 说,GCC 显然知道所有相关的转换是什么,只是没有地方可以放置这些信息以便内核访问。如果 BTF 可以扩展来处理这些信息,并且内核构建过程可以使用 GCC 直接生成的 BTF,那就足够了。
Alexei Starovoitov 提到,当 Clang 添加了对直接生成 BTF 的支持时,Clang 开发者复制了 libbpf 中的去重逻辑。他担心如果 GCC 也这样做,就会存在*三种*略有不同、各自独立维护的相同逻辑版本,这会很混乱。他说,实际上只有 libbpf 中的去重器是真正可用的。Nakryiko 说,尝试同时对弱和非弱 BTF map 定义进行去重也会引入复杂性。
[![[Vineet Gupta]](https://static.lwn.net/images/2026/vineet-gupta-small.png)](https://lwn.net/Articles/1072530#gupta)
Faust 还询问,添加实现常见位操作编译器内建函数的 kfuncs 是否有意义,这些函数目前无论出现在哪里都会被内联。例如,`__builtin_clz()` 会展开为大约 30 条 BPF 指令,“这是次优的”。Nakryiko 同意这是可以接受的,并且实际上正是首先将快速 kfunc 调用添加到 BPF 的动机——允许内核加速 BPF 中的常见操作。他要求一次性添加所有的位操作函数,以便它们具有匹配的名称;Faust 欣然同意。
Gupta 在会议结束时解释了 GCC 生成的代码与 LLVM 生成的代码之间的一些差异;两种代码都是合法的,但验证器更容易处理 LLVM 的版本。他计划通过为 BPF 添加成本模型来部分解决这个问题,以便 GCC 的优化器生成更像 LLVM 的代码,并通过扩展验证器能理解的内容以更好地适应 GCC 的输出来解决另一部分。总的来说,GCC 对 BPF 的支持进展顺利。它已经可以用于简单的实际程序,并且随着更多项目开始使用它并提交 bug 报告来指导剩余的工作,它将变得更加可用。
本文索引条目
- [内核](https://lwn.net/Kernel/Index)
- [BPF](https://lwn.net/Kernel/Index#BPF)
- [会议](https://lwn.net/Archives/ConferenceIndex/)
- [存储、文件系统、内存管理和 BPF 峰会/2026](https://lwn.net/Archives/ConferenceIndex/#Storage_Filesystem_Memory-Management_and_BPF_Summit-2026)
相似文章
GCC 16 的新特性:改进的错误信息与 SARIF 输出
GCC 16 引入了针对 C++ 模板的改进分层错误信息以及更新的 SARIF 机器可读诊断输出,从而提升开发者体验。
Gobee:用Go编写eBPF程序,通过clang转译
Gobee是一个将Go的子集转译为BPF C的工具,允许开发者用Go而非C编写eBPF程序。它为用户空间生成类型化的Go绑定,并利用clang的后端进行编译。
QBE - 编译器后端:版本 1.3
QBE 1.3 是一个重要的编译器后端版本,新增了 7000 行代码,引入了一种新的 IL 匹配算法,针对 coremark 基准测试进行了优化(性能从 gcc -O2 的 40% 提升到超过 63%),支持 Windows ABI 和位置无关代码生成。
偏差累积,方差抵消
本文证明,对BF16优化器状态使用随机舍入可以匹配FP32性能,因为无偏误差随时间抵消,而四舍五入则因累积偏差而停滞。一项使用MLP的实验表明,BF16+SR在减少内存使用的同时达到了与FP32相似的损失。
Isolation Forest + eBPF 事件打造 Linux 端点检测系统 [项目]
guardd 是一款开源 Linux 端点检测工具,利用 eBPF 事件与 Isolation Forest 在 60 秒窗口内发现异常进程/网络行为,但对浏览器类误报较敏感。