这取决于“学习[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 文件的一个部分中。然后我可以使用objdump 或readelf 等工具检查我的程序。例如,如果我的程序在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 来获得格式良好的输出。