【问题标题】:How to get linux ebpf assembly?如何获得 linux ebpf 程序集?
【发布时间】:2016-10-12 11:59:05
【问题描述】:

我想学linux ebpf vm,如果我写了一个ebpf程序test.c,用到了llvm:
clang -O2 -target bpf -o test.o test.c。如何在经典 bpf 中获得类似 tcpdump -d 的 ebpf 程序集,谢谢。

【问题讨论】:

  • 显示tcpdump 输出的小例子。我怀疑它可能类似于objdump -d
  • 像这样:# tcpdump -i any -d ip (000) ldh [14] (001) jeq #0x800 jt 2 jf 3 (002) ret #65535 (003) ret #0
  • @JunliOu tcpdump -d 将以旧 BPF(又名 cBPF)格式打印指令。提示:tcpdump -ddd 显示的每条指令有 4 个字段(eBPF 有 5 个字段)。

标签: linux assembly linux-kernel


【解决方案1】:

这取决于“学习[ing] linux ebpf vm”的确切含义。

语言本身

如果您的意思是了解eBPF 的指令,类似汇编语言本身,您可以查看the documentation from the kernel(相当密集)或来自 bcc 项目的this summarized version of the syntax

虚拟机

如果您想了解 eBPF 虚拟机的内部结构 是如何工作的,您可以查看各种演示文稿(我推荐 D. Borkmann 的演示文稿),我在这里有一个列表 @ 987654323@;或者您可以直接阅读内核源代码,位于linux/kernel/bpf(特别是文件core.c)下。或者,有一个simpler userspace implementation 可用。

转储 eBPF 指令

现在,如果您想查看从 C 编译到 eBPF 的代码,这里有几个解决方案。

读取目标文件

就我而言,我使用tc-bpf man page 中提供的命令进行编译:

__bcc() {
        clang -O2 -emit-llvm -c $1 -o - | \
        llc -march=bpf -filetype=obj -o "`basename $1 .c`.o"
}
alias bcc=__bcc

代码被翻译成 eBPF 并存储在生成的 ELF 文件的一个部分中。然后我可以使用objdumpreadelf 等工具检查我的程序。例如,如果我的程序在classifier 部分:

$ bcc return_zero.c
$ readelf -x classifier return_zero.o

Hex dump of section 'classifier':
   0x00000000 b7000000 02000000 95000000 00000000 ................

在上面的输出中,显示了两条指令(小端序——第一个以0x 开头的字段是节内的偏移量)。我们可以对其进行解析以形成指令并获得:

b7 0 0 0000 00000002 // Load 0x02 in register r0
95 0 0 0000 00000000 // Exit and return value in r0

[2019 年 4 月编辑] 转储内核中加载的 eBPF 程序

可以将加载的程序指令(然后可能附加到可用的 BPF 钩子之一)转储到内核中,作为 eBPF 汇编指令,或者如果程序已经过 JIT 编译,则作为机器指令转储。 bpftool,依赖于 libbpf,是做这些事情的首选工具。例如,可以查看当前加载了哪些程序,并记下它们的 id,其中:

# bpftool prog show

然后转储给定 id 的程序的指令很简单:

# bpftool prog dump xlated id <id>
# bpftool prog dump jited  id <id>

分别用于 eBPF 或 JITed(如果可用)指令。如有必要,输出也可以格式化为 JSON。

高级工具

根据您用于将 BPF 注入内核的工具,您通常可以转储内核内验证程序的输出,其中包含以人性化方式格式化的大部分指令。

使用bcc set of tools(与前面的命令没有直接关系,与旧的16位编译器完全没有关系),你可以得到这个by using the relevant flags作为BPF对象实例,而使用tc filter add dev eth0 bpf obj … verbose这个使用 verbose 关键字完成。

反汇编程序

前面提到的用户空间实现(uBPF)有它自己的汇编器和反汇编器,你可能会感兴趣:它以“人类友好”(add32 r0, r1 等)指令作为输入并转换为目标文件,或者反过来。

但可能更有趣的是,LLVM 本身支持调试信息,以及 BPF 反汇编程序:截至今天,它最近已被合并,其作者 (A. Starovoitov) 已发送 an email about它在 netdev 邮件列表中。这意味着使用 clang/LLVM 4.0+,您应该能够使用 llvm-objdump -S -no-show-raw-insn my_file.o 来获得格式良好的输出。

【讨论】:

  • bcc 也是a 16-bit x86 compiler 的名称。我猜字母表中只有这么多字母,但一开始我在浏览这个时感到非常惊讶。另外顺便说一句,你可以调用 shell 函数bcc。为什么要使用别名?
  • 也不要忘记引用你的变量扩展:clang -O2 -Wall -emit-llvm -c "$@" -o - | llc -march=bpf -filetype=obj -o "$(basename "$1" .c).o"。请注意,与反引号相比,$() 允许轻松嵌套双引号。我在 clang 命令行中使用了"$@",所以你可以传递foo.c -O3 -Wextra -mtune=native 或其他任何东西。嗯,我猜llc 命令行上需要一些优化/调整选项?所以也许llc "${@:2}" 可以选择 1 美元以外的选项。
  • @PeterCordes 谢谢。在此,我直接从手册页复制了代码。这只是一个例子,我猜 bcc 项目对 llvm 库有一个微调和抛光的调用。我认为他们给它起别名的原因是确切的命令可能会演变(例如,llvm-objdump -S 在编译时将需要-g 标志)。无论如何,这里的重点是字节码转储而不是编译步骤。说了这么多,您的命令仍然有效,再次感谢。
  • 啊,我想我应该在手册页上提交一份错误报告,其中包含他们引用的修复程序。不过,这种推理并不能解释alias bcc=__bcc。您可以重新定义已定义的 shell 函数,因此更改函数的内容不需要额外的间接层。 (另外,我认为你错过了关于另一个 bcc 的观点。这是一个旧项目,主要用于制作引导加载程序。16 位 x86 机器代码远比 32 位过时,对Linux 内核,因为它运行在 32 位或 64 位模式下!)
  • @PeterCordes 实际上,我倾向于同意别名,在我自己的设置中,老实说,我摆脱了它。对不起,我没听懂,我错过了什么?我看不出与 16 位 bcc 编译器以及讨论中的任何其他内容没有任何关系——当然保留名称。据我所知,用于 eBPF (this) 的 bcc 工具集与 16 位编译器没有任何关系。我听起来不一样吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-09-23
  • 2014-11-15
  • 1970-01-01
  • 2023-02-22
  • 2021-11-07
  • 1970-01-01
  • 2017-03-30
相关资源
最近更新 更多