Loading Objects
ebpf-go 提供了一个 eBPF 对象(ELF)加载器,旨在与上游的 libbpf 和 iproute2( tc / ip )项目保持兼容。ELF 文件通常是通过使用 LLVM 工具链( clang )编译 eBPF C 程序获得的。
本文描述了从已编译的 eBPF ELF 到内核中资源的整个过程。这涉及将 ELF 解析为 intermediate Go (Spec) types,这些类型可以在加载到内核之前进行修改和复制。

CollectionSpec
CollectionSpec 表示从 ELF 中提取的 eBPF 对象,可以通过调用 LoadCollectionSpec 获取。
在下面的示例中,在 eBPF C 中声明一个 Map 和 Program,然后使用 Go 加载并检查它们。
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
// Declare a hash map called 'my_map' with a u32 key and a u64 value.
// The __uint, __type and SEC macros are from libbpf's bpf_helpers.h.
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, __u32);
__type(value, __u64);
__uint(max_entries, 1);
} my_map SEC(".maps");
// Declare a dummy socket program called 'my_prog'.
SEC("socket") int my_prog() {
return 0;
}
// Parse an ELF into a CollectionSpec.
// bpf_prog.o is the result of compiling BPF C code.
spec, err := ebpf.LoadCollectionSpec("bpf_prog.o")
if err != nil {
panic(err)
}
// Look up the MapSpec and ProgramSpec in the CollectionSpec.
m := spec.Maps["my_map"]
p := spec.Programs["my_prog"]
// Note: We've omitted nil checks for brevity, take a look at
// LoadAndAssign for an automated way of checking for maps/programs.
// Inspect the map and program type.
fmt.Println(m.Type, p.Type)
// Print the map's key and value BTF types.
fmt.Println(m.Key, m.Value)
// Print the program's instructions in a human-readable form,
// similar to llvm-objdump -S.
fmt.Println(p.Instructions)
可以以 ProgramSpec 的定义为例,更加深入的理解一下 Spec 的概念:
type ProgramSpec struct {
// Name is passed to the kernel as a debug aid.
//
// Unsupported characters will be stripped.
Name string
// Type determines at which hook in the kernel a program will run.
Type ProgramType
// Network interface index the user intends to attach this program to after
// loading. Only valid for some program types.
//
// Provides driver-specific context about the target interface to the
// verifier, required when using certain BPF helpers.
Ifindex uint32
// AttachType of the program, needed to differentiate allowed context
// accesses in some newer program types like CGroupSockAddr.
//
// Available on kernels 4.17 and later.
AttachType AttachType
// Name of a kernel data structure or function to attach to. Its
// interpretation depends on Type and AttachType.
AttachTo string
// The program to attach to. Must be provided manually.
AttachTarget *Program
// The name of the ELF section this program originated from.
SectionName string
Instructions asm.Instructions
// Flags is passed to the kernel and specifies additional program
// load attributes.
Flags uint32
// License of the program. Some helpers are only available if
// the license is deemed compatible with the GPL.
//
// See https://www.kernel.org/doc/html/latest/process/license-rules.html#id1
License string
// Version used by Kprobe programs.
//
// Deprecated on kernels 5.0 and later. Leave empty to let the library
// detect this value automatically.
KernelVersion uint32
// The byte order this program was compiled for, may be nil.
ByteOrder binary.ByteOrder
}
ProgramSpec 包含以下信息:
- 程序身份信息
Name string: 程序名字,传给内核,主要用于调试和标识SectionName string: 该程序所源自的ELF节名称
Type ProgramType: 这个程序属于哪种 eBPF 类型,例如:XDP、Kporbe、Tracepoint 等- attach 相关信息
Ifindex uint32: 这个程序之后可能会 attach 到的网卡索引AttachType AttachType: 区分更细的 attach 场景。比如同样是 cgroup 类型程序,不同 attach 类型的上下文访问规则可能不同.AttachTo string: 这个程序 attach 到什么地方AttachTarget *Program: 这个程序要 attach 到另一个已经加载的 Program 上.
Instructions asm.Instructions:.bpf.c编译后得到的字节码指令- 加载参数
Flags uint32: 表示额外加载选项。可以理解为传给bpf(BPF_PROG_LOAD, ...)的 flags.License stringKernelVersion uint32ByteOrder binary.ByteOrder: 字节序.
可以看出,同时也是顾名思义:ProgramSpec 是 eBPF Program 的加载说明书/规范.
它不是已经加载到内核里的 eBPF 程序,而是告诉 ebpf-go / Linux kernel:要加载一个什么样的 eBPF 程序。
而多个 xxxSpec 组成了 CollectionSpec.
NewCollection
将 ELF 解析为 CollectionSpec 后,可使用 NewCollection 将其加载到内核中,从而得到一个 Collection。
spec, err := ebpf.LoadCollectionSpec("bpf_prog.o")
if err != nil {
panic(err)
}
// Instantiate a Collection from a CollectionSpec.
coll, err := ebpf.NewCollection(spec)
if err != nil {
panic(err)
}
// Close the Collection before the enclosing function returns.
defer coll.Close()
// Obtain a reference to 'my_map'.
m := coll.Maps["my_map"]
// Set map key '1' to value '2'.
if err := m.Put(uint32(1), uint64(2)); err != nil {
panic(err)
}
Collection.Close 会关闭集合中的所有 Maps 和 Programs。在 Close() 之后与任何资源交互都将返回错误,因为它们底层的文件描述符会被关闭。请参阅 Object Lifecycle,以更好地理解 ebpf-go 如何管理其资源,以及处理 Maps 和 Programs 的最佳实践。
LoadAndAssign¶
LoadAndAssign 是一个便捷 API,可用于替代 NewCollection 。它有两个主要优势:
- 它会自动从 Collection 中提取 Maps 和 Programs。不再需要
if m := coll.Maps["my_map"]; m == nil { return ... } - 选择性加载 Maps 和 Programs!仅将感兴趣的资源及其依赖项加载到内核中。非常适合处理只需部分加载的大型 CollectionSpecs。
先声明一个结构体,用来在把 Map 和 Program 加载到内核之后,接收指向它们的指针。再给这个结构体定义一个 Close() 方法,以便更方便地清理资源。
type myObjs struct {
MyMap *ebpf.Map `ebpf:"my_map"`
MyProg *ebpf.Program `ebpf:"my_prog"`
}
func (objs *myObjs) Close() error {
if err := objs.MyMap.Close(); err != nil {
return err
}
if err := objs.MyProg.Close(); err != nil {
return err
}
return nil
}
如果前面的代码片段看起来繁琐,可以使用 bpf2go。bpf2go 可以自动生成这类样板代码,并确保其与你的 C 代码保持同步。
接下来,实例化一个我们新声明类型的变量,并将其指针传递给 LoadAndAssign 。
spec, err := ebpf.LoadCollectionSpec("bpf_prog.o")
if err != nil {
panic(err)
}
// Insert only the resources specified in 'obj' into the kernel and assign
// them to their respective fields. If any requested resources are not found
// in the ELF, this will fail. Any errors encountered while loading Maps or
// Programs will also be returned here.
// 仅将 'obj' 中指定的资源插入(加载到)内核,并分配至各自字段。若ELF中未找到任何请求的资源,此操作将失败。加载映射或程序时遇到的任何错误也将在此处返回。
var objs myObjs
if err := spec.LoadAndAssign(&objs, nil); err != nil {
panic(err)
}
defer objs.Close()
// Interact with MyMap through the custom struct.
if err := objs.MyMap.Put(uint32(1), uint64(2)); err != nil {
panic(err)
}
总结
整体阶段:
eBPF ELF .o 文件
↓
ebpf.CollectionSpec // 加载前:描述信息
↓
ebpf.Collection / objects // 加载后:真实内核对象 fd
↓ attach
真正被内核 hook 触发执行
- CollectionSpec = 加载前,描述 eBPF object 里有什么
- Collection = 加载后,持有内核里真实创建出来的对象
评论