目录

从C到目标文件

深入说明

eBPF与LLVM循序渐进

从C编译到eBPF程序集

组装到ELF目标文件

llvm-objdump的人性化输出

内联汇编

结论


此职位作为草稿留了很长时间。它的大部分内容是在2017年12月编写的。我希望它能在今天发布,尽管Cilium指南也涵盖了该功能。

eBPF(扩展的Berkeley数据包过滤器)相对于旧BPF版本(对于经典BPF而言,是cBPF)最有用的发展之一就是基于clang和LLVM的后端的可用性,从而可以从C源代码生成eBPF字节码。1个

从C到目标文件

例如,可以从以下代码编译返回零的简单eBPF程序:

$ cat my_bpf_program.c
int func()
{return 0;
}

命令行如下所示:

$ clang -target bpf -Wall -O2 -c my_bpf_program.c -o my_bpf_objfile.o

注意:某些程序比本示例更先进,可能需要将该-mcpu选项传递 给llc,并使用更接近以下命令的内容:

$ clang -O2 -emit-llvm -c my_bpf_program.c -o - | \llc -march=bpf -mcpu=probe -filetype=obj -o my_bpf_objfile.o

这将以ELF格式创建一个目标文件,其中包含已编译的字节码。默认情况下,代码在.textELF部分下。让我们转储它:

$ readelf -x .text my_bpf_objfile.oHex dump of section '.text':0x00000000 b7000000 00000000 95000000 00000000 ................

有效!我们在这里有两个eBPF指令:

b7 0 0 0000 00000000    # r0 = 0
95 0 0 0000 00000000    # exit and return r0

如果您不熟悉eBPF汇编语法,则可能对此简短参考资料 (或 完整的文档,但内容密集)感兴趣 。

深入说明

从C编译为eBPF字节码作为目标文件非常有用。生成的ELF文件可直接用于将程序附加到各种钩子上:TC,XDP,kprobes等。将高级程序作为字节码编写将非常耗时,并且在2020年我完成对本文的编辑时,将更加复杂诸如 CO-RE之类的功能 根本无法手动完成。Clang和LLVM是eBPF工作流程的组成部分。

但是,对于需要测试非常特定的eBPF指令序列或微调程序特定方面的人员来说,这可能不是一个方便的解决方案。如果我想修改说明怎么办?如果我想在程序末尾添加第三条eBPF指令,以便在程序退出后为寄存器r0设置另一个值,该怎么办?当然,这个例子没有用,但是我仍然可以尝试一下!

为此,我们可以:

  • 从头开始用eBPF字节码编写一个eBPF程序。这是完全可行的,但可能又长又乏味,而且绝对不友好。为了保持与tc之类工具的兼容性,无论如何该程序都必须转换为目标文件,这为该过程增加了额外的步骤。

  • 使用汇编语言从头开始编写程序,至少不要以字节码编写程序,然后使用专用的汇编器对其进行编译(例如:ebpf_asm在Python中,由Solarflare编写 )。

作为LLVM近期改进的一部分,出现了另一个解决方案,我们还可以:

  • 从C编译为eBPF汇编语言。编辑程序集,然后将其作为字节码组装到目标文件中。

Clang和LLVM现在可以做到这一点!在一侧生成程序的可读版本,然后在另一侧进行组装。奖励: llvm-objdump甚至可以用来转储包含在目标文件中的程序。

eBPF与LLVM循序渐进

要使用本节中介绍的所有clang和LLVM功能,您需要在6.0版或更高版本中使用这些工具。当我开始起草本文时,它是开发分支,但是到我完成它时,版本10已经发布了,因此应该没有问题。

从C编译到eBPF程序集

让我们用clang编译从C到eBPF程序集的程序。实际上,这与通常使用C源代码为处理器生成汇编程序的方式相同,只是您告诉clang目标是 bpf。2

$ cat bpf.c
int func()
{return 0;
}$ clang -target bpf -S -o bpf.s bpf.c
$ cat bpf.s.text.globl  func                    # -- Begin function func.p2align    3
func:                                   # @func
# %bb.0:r1 = 0*(u32 *)(r10 - 4) = r1r0 = r1exit# -- End function

太好了,现在让我们对其进行修改并在底部添加我们的说明!

$ sed -i '$a \\tr0 = 3' bpf.s
$ cat bpf.s.text.globl  func                    # -- Begin function func.p2align    3
func:                                   # @func
# %bb.0:r1 = 0*(u32 *)(r10 - 4) = r1r0 = r1exit# -- End functionr0 = 3

组装到ELF目标文件

我们可以将该文件汇编为包含该程序字节码的ELF目标文件。它需要llvm-mc处理机器代码并与LLVM一起提供的工具。

$ llvm-mc -triple bpf -filetype=obj -o bpf.o foo.s

我们有ELF文件!让我们转储字节码:

$ readelf -x .text my_bpf_objfile.oHex dump of section '.text':0x00000000 b7010000 00000000 631afcff 00000000 ........c.......0x00000010 bf100000 00000000 95000000 00000000 ................0x00000020 b7000000 03000000 b7000000 03000000 ................

相对于程序的原始版本,前两个说明没有变化。第三条指令对应于我们添加到汇编文件中的内容:它将3加载到寄存器r0中。我们已成功编辑了说明。例如,我们现在可以使用将程序加载到内核中bpftool。任务完成!

llvm-objdump的人性化输出

请注意,LLVM还提供了一种以人类可读的方式转储eBPF对象文件的方法(如果我没有记错的话,从4.0版开始)。这可以通过以下方式完成llvm-objdump

$ llvm-objdump -d bpf.obpf.o:    file format ELF64-BPFDisassembly of section .text:
func:0: b7 01 00 00 00 00 00 00     r1 = 01:   63 1a fc ff 00 00 00 00     *(u32 *)(r10 - 4) = r12:   bf 10 00 00 00 00 00 00     r0 = r13:  95 00 00 00 00 00 00 00     exit4:  b7 00 00 00 03 00 00 00     r0 = 3

我们获得了LLVM使用的语法的汇编指令(我们需要编写或编辑eBPF汇编的语法,因此这很有用)。注意我们在程序末尾添加的无效指令。

除了字节码和汇编指令外,LLVM还可以嵌入调试符号,以便将其转储以进行检查。具体来说,我们可以将C指令与字节码同时使用。记住原始程序是很方便的,但是最重要的是了解C指令如何映射到eBPF代码非常有帮助。嵌入指令是通过从C编译并-g传递给clang的标志来完成的。试一试吧:

$ clang -target bpf -g -S -o bpf.s bpf.c
$ llvm-mc -triple bpf -filetype=obj -o bpf.o bpf.s
$ llvm-objdump -S bpf.obpf.o:   file format ELF64-BPFDisassembly of section .text:
func:
; int func() {0:    b7 01 00 00 00 00 00 00     r1 = 01:   63 1a fc ff 00 00 00 00     *(u32 *)(r10 - 4) = r1
; return 0;2:   bf 10 00 00 00 00 00 00     r0 = r13:  95 00 00 00 00 00 00 00     exit

请注意,我们传递-gclang,并且还更改了传递给的命令(现在 -S改为-dllvm-objdump。该return 0;指令出现,并映射(放置在eBPF程序中的相关指令上方)。好的。

内联汇编

由于clang和LLVM知道如何生成和编译eBPF程序集,因此存在另一种处理指令的方法。现在可以直接在C程序中内联使用eBPF程序集,再次在字节码中生成特定序列。请参阅以下示例,该示例在某种程度上受到Cilium的BPF和XDP参考指南中示例的启发。

$ cat inline_asm.c
int func()
{unsigned long long foobar = 2, r3 = 3, *foobar_addr = &foobar;asm volatile("lock *(u64 *)(%0+0) += %1" :"=r"(foobar_addr) :"r"(r3), "0"(foobar_addr));return foobar;
}$ clang -target bpf -Wall -O2 -c inline_asm.c -o inline_asm.o
$ llvm-objdump -d inline_asm.o
inline_asm.o:   file format ELF64-BPFDisassembly of section .text:
func:0: b7 01 00 00 02 00 00 00     r1 = 21:   7b 1a f8 ff 00 00 00 00     *(u64 *)(r10 - 8) = r12:   b7 01 00 00 03 00 00 00     r1 = 33:   bf a2 00 00 00 00 00 00     r2 = r104: 07 02 00 00 f8 ff ff ff     r2 += -85:    db 12 00 00 00 00 00 00     lock *(u64 *)(r2 + 0) += r16:    79 a0 f8 ff 00 00 00 00     r0 = *(u64 *)(r10 - 8)7:   95 00 00 00 00 00 00 00     exit

它在r2指向的地址上产生值的原子增量。因为指令是用C源代码编写的,所以我们不需要中间步骤就可以进行汇编。

我们应该使用内联汇编还是中间编译?我认为这两种方法都很有用:在C源文件中以汇编形式插入几个指令,或者创建用于测试特定指令序列的小型程序。在Netronome,我们经常将后者用于单元测试,以检查nfp驱动程序的eBPF硬件卸载功能。

结论

简而言之,您无需将eBPF程序从C编译为ELF目标文件,而是可以将其编译为汇编语言,根据需要进行编辑,然后将此版本汇编为最终目标文件。为此,您需要6.0版或更高版本中的clang和LLVM,命令为:

$ clang -target bpf -S -o bpf.s bpf.c
$ llvm-mc -triple bpf -filetype=obj -o bpf.o foo.s

并以人类可读的格式转储该文件:

$ llvm-objdump -d bpf.o
$ llvm-objdump -S bpf.o         # add C code, if -g was passed to clang

此外,该asm关键字可用于在C程序中内联包括eBPF汇编程序。

LLVM中的eBPF程序集支持允许编写所需的任何eBPF指令序列。包括错误的程序。不要忘记:即使编译,它仍然必须通过验证程序。祝好运并玩得开心点!


  1. 当我完成本文时,现在也有一个GCC后端,但似乎没有clang / LLVM版本完整,后者显然仍然是产生eBPF字节码的参考工具。 ↩

  2. 有关目标以及与默认目标的区别的更多信息,请参见Cilium的BPF和XDP参考指南bpf。 ↩

带有LLVM的eBPF组件相关推荐

  1. 工作118:封装一个带有对话框的button组件

    buttondialog.vue <!--定义一个有按钮的对话框 相当于dialog和按钮组合使用--> <template><!-- 有按钮的对话框 这个位置的代码会被 ...

  2. LLVM 核心类简明示例

    <带有LLVM的eBPF组件> <The LLVM Compiler Infrastructure | LLVM编译器基础设施> <LLVM每日谈 | 知乎> &l ...

  3. react内联样式_React样式化的组件:内联样式+ 3种其他CSS样式化方法(带有示例)...

    react内联样式 There's no one right way to style your React components. It all depends on how complex you ...

  4. eBPF.io eBPF文档:扩展的数据包过滤器(BPF)

       目录 什么是eBPF? 安全 跟踪和分析 联网 可观察性与监控 什么是eBPF.io? eBPF简介 挂钩概述 eBPF程序如何编写? 加载程序和验证架构 地图 辅助呼叫 尾部和函数调用 eBP ...

  5. Cilium 1.7发布:Hubble UI、全集群网络策略、基于eBPF的Direct Server Return以及更多

    在这里,我们要向大家高兴地宣布,Cilium 1.7版本正式发布了!在本轮更新周期当中,由141位开发者组成的项目社区共完成了1551项提交,而且很多朋友是第一次为Cilium项目提交贡献. Hubb ...

  6. 大规模微服务利器:eBPF + Kubernetes

    hi, 大家好,微服务,云原生近来大热,在企业积极进行数字化转型,全面提升效率的今天,几乎无人否认云原生代表着云计算的"下一个时代",IT大厂们都不约而同的将其视为未来云应用的发展 ...

  7. 初识 eBPF(功能、原理、及一些应用)

    非常棒的一些材料 当eBPF遇上Linux内核网络 什么是 eBPF? 待看 基于eBPF监控和排查云原生环境中的磁盘IO性能问题 深度解密基于 eBPF 的 Kubernetes 问题排查全景图 e ...

  8. 大规模微服务利器:eBPF 与 Kubernetes

    Daniel 是 eBPF 两位 maintainer 之一,目前在 eBPF commits 榜单上排名第一,也是 Cilium 的核心开发者之一. 本文内容的时间跨度有 8 年,覆盖了 eBPF ...

  9. 初步了解React Native的新组件库firstBorn

    first-born is a React Native UI Component Framework, which follows the design methodology Atomic Des ...

最新文章

  1. 数组中的第k个最大元素—leetcode215
  2. java floatmath_《Java1.doc
  3. redis value多大会影响性能_选择合适Redis数据结构,减少80%的内存占用
  4. java计算距离_java实现计算地理坐标之间的距离
  5. (3)分布式下的爬虫Scrapy应该如何做-递归爬取方式,数据输出方式以及数据库链接...
  6. 计算机知识点汇总职高,计算机辅导(知识点汇总) - 慈溪职高 网站首页.doc
  7. /usr/include/features.h:356:25: 致命错误: sys/cdefs.h:没有那个文件或目录
  8. 发电机变压器运行状态(温度电压电流)监控系统解决方案
  9. 国产CAM究竟水平如何?看完测试我震惊了
  10. Android之离线词典
  11. Spotfire 常用数据类型
  12. 【单例模式、多例模式、枚举、工厂模式】
  13. Linux面试题及答案
  14. 第一P2P收购中国典当联盟发力供应链金融模式
  15. poi操作word,写入一个图片,并且设置其大小,以及图片和base64之间的互相转换,以及表格内容替换和插入
  16. win7系统盘瘦身秘诀
  17. 中国移动--九天毕昇平台使用测试(薅Telsa V100)
  18. 询问HTG:添加PDF打印机,隐藏Windows登录名和共享USB HDD
  19. 谷粒商城项目笔记总结(2/2)
  20. STM32系列单片机USB下载程序(ISP编程)

热门文章

  1. React-native集成tfs自动发版问题
  2. pyspark对应的scala代码PythonRDD类
  3. 【FZU2178】礼物分配
  4. 一站式WPF--依赖属性(DependencyProperty)
  5. Android新手入门2016(14)--FragmentTabHost实现选项卡和菜单
  6. 15-传智书城后台程序设计
  7. mysql 分区 key 写法_MySQL KEY分区
  8. html 导出excel 乱码问题,Asp导出Excel乱码怎么办
  9. python数据预测_使用Python预测缺失值
  10. java做的web系统 m1 读卡器 结合_IE浏览器接入IC卡读写器实现M1卡的读写功能