eBPF:全局变量和结构
eBPF: global variables and structs
所以我有一个简单的eBPF
代码:
my.h:
#ifndef __MY_COMMON_H__
#define __MY_COMMON_H__
#include <linux/types.h>
struct foo {
int a;
int b;
int c;
int d;
};
#endif /* __MY_COMMON_H__ */
my_kern.c:
...
struct bpf_map_def SEC("maps") my_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = ...,
.value_size = ...,
.max_entries = MAX_ENTRIES,
};
struct foo my_foo = {
.a = 150000,
.b = 100,
.c = 10,
.d = 40,
};
SEC("sockops")
int my_bpf(struct bpf_sock_ops *sk_ops)
{
...
};
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;
我使用 llvm-5.0
构建代码,没有 errors/warnings,但是 bpftool prog load ...
失败:
libbpf: Program 'sockops' contains non-map related relo data pointing to section 6
Error: failed to load program
$ llvm-readelf-5.0 -s my_kern.o
There are 12 section headers, starting at offset 0xa90:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .strtab STRTAB 0000000000000000 0009c0 0000cc 00 0 0 1
[ 2] .text PROGBITS 0000000000000000 000040 000000 00 AX 0 0 4
[ 3] sockops PROGBITS 0000000000000000 000040 0006e0 00 AX 0 0 8
[ 4] .relsockops REL 0000000000000000 000980 000040 10 11 3 8
[ 5] maps PROGBITS 0000000000000000 000720 00001c 00 WA 0 0 4
[ 6] .data PROGBITS 0000000000000000 00073c 00001c 00 WA 0 0 4
[ 7] .rodata.str1.16 PROGBITS 0000000000000000 000760 000093 01 AMS 0 0 16
[ 8] .rodata.str1.1 PROGBITS 0000000000000000 0007f3 00001d 01 AMS 0 0 1
[ 9] license PROGBITS 0000000000000000 000810 000004 00 WA 0 0 1
[10] version PROGBITS 0000000000000000 000814 000004 00 WA 0 0 4
[11] .symtab SYMTAB 0000000000000000 000818 000168 18 1 10 8
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
$
第 6 节包含我的 my_foo
结构,我可以将其内容转储为 llvm-objdump
。
例如,如果我在 main()
函数中定义 my_foo
,则不会发生此错误。这是否意味着 eBPF 公约不允许这样的全局声明?
eBPF 对全局变量一无所知。当 bpftool
将你的程序发送到内核时,它只发送一段字节码指令,该指令应该是“自包含的”(至少如果你不使用 eBPF 函数调用,但 eBPF 函数还没有由 libbpf 和 bpftool
支持,所以我认为情况并非如此。
总之:当bpftool
调用libbpf 从ELF 文件加载您的程序时,它期望在一个ELF 部分中找到整个自包含程序。地图有一个例外,一些元数据被放入特定的 ELF 部分。除此之外,libbpf 不知道如何从 .data
部分获取全局变量 my_foo
的定义并将其移动到主要部分。这就是为什么它会在 .data
部分警告您有关 non-map related relo[cation] data
的原因。
my_kern.o
+----------------------------+
| ELF header |
+----------------------------+
|sockops |
| |
| eBPF instructions |
| | |
| ->“get my_foo from .data” | <- libbpf: “What am I supposed to do with this??”
| |
+----------------------------+
| Other ELF sections… |
+----------------------------+
|.data | <- libbpf: “I don't care about this section”
| my_foo |
+----------------------------+
我是一个真正的艺术家,不是吗?
所以问题实际上来自这里clang如何处理你的全局变量。如果将定义移动到 main 函数内,clang 显然不会将其移动到它创建的目标文件中自己的 .data
部分。我想您正在尝试将变量移动到头文件中,可能是为了与其他源文件共享它;我不知道是否可以正确编译它,可能存在一些 clang 标志或一些预处理指令可以帮助你,但这超出了我的知识范围。
似乎静态全局变量重定位现在可以工作了(内核 5.4,Clang 10,Ubuntu 20.04)。在我的代码中,变量 test
的值在 BPF prog 的运行之间持续存在。
static __u64 test = 0;
SEC("cgroup_skb/egress")
int cb_pkt(struct __sk_buff *skb)
{
bpf_printk("Packet with size: %d\n", test);
test = skb->len;
return 1;
}
所以我有一个简单的eBPF
代码:
my.h:
#ifndef __MY_COMMON_H__
#define __MY_COMMON_H__
#include <linux/types.h>
struct foo {
int a;
int b;
int c;
int d;
};
#endif /* __MY_COMMON_H__ */
my_kern.c:
...
struct bpf_map_def SEC("maps") my_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = ...,
.value_size = ...,
.max_entries = MAX_ENTRIES,
};
struct foo my_foo = {
.a = 150000,
.b = 100,
.c = 10,
.d = 40,
};
SEC("sockops")
int my_bpf(struct bpf_sock_ops *sk_ops)
{
...
};
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;
我使用 llvm-5.0
构建代码,没有 errors/warnings,但是 bpftool prog load ...
失败:
libbpf: Program 'sockops' contains non-map related relo data pointing to section 6
Error: failed to load program
$ llvm-readelf-5.0 -s my_kern.o
There are 12 section headers, starting at offset 0xa90:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .strtab STRTAB 0000000000000000 0009c0 0000cc 00 0 0 1
[ 2] .text PROGBITS 0000000000000000 000040 000000 00 AX 0 0 4
[ 3] sockops PROGBITS 0000000000000000 000040 0006e0 00 AX 0 0 8
[ 4] .relsockops REL 0000000000000000 000980 000040 10 11 3 8
[ 5] maps PROGBITS 0000000000000000 000720 00001c 00 WA 0 0 4
[ 6] .data PROGBITS 0000000000000000 00073c 00001c 00 WA 0 0 4
[ 7] .rodata.str1.16 PROGBITS 0000000000000000 000760 000093 01 AMS 0 0 16
[ 8] .rodata.str1.1 PROGBITS 0000000000000000 0007f3 00001d 01 AMS 0 0 1
[ 9] license PROGBITS 0000000000000000 000810 000004 00 WA 0 0 1
[10] version PROGBITS 0000000000000000 000814 000004 00 WA 0 0 4
[11] .symtab SYMTAB 0000000000000000 000818 000168 18 1 10 8
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
$
第 6 节包含我的 my_foo
结构,我可以将其内容转储为 llvm-objdump
。
例如,如果我在 main()
函数中定义 my_foo
,则不会发生此错误。这是否意味着 eBPF 公约不允许这样的全局声明?
eBPF 对全局变量一无所知。当 bpftool
将你的程序发送到内核时,它只发送一段字节码指令,该指令应该是“自包含的”(至少如果你不使用 eBPF 函数调用,但 eBPF 函数还没有由 libbpf 和 bpftool
支持,所以我认为情况并非如此。
总之:当bpftool
调用libbpf 从ELF 文件加载您的程序时,它期望在一个ELF 部分中找到整个自包含程序。地图有一个例外,一些元数据被放入特定的 ELF 部分。除此之外,libbpf 不知道如何从 .data
部分获取全局变量 my_foo
的定义并将其移动到主要部分。这就是为什么它会在 .data
部分警告您有关 non-map related relo[cation] data
的原因。
my_kern.o
+----------------------------+
| ELF header |
+----------------------------+
|sockops |
| |
| eBPF instructions |
| | |
| ->“get my_foo from .data” | <- libbpf: “What am I supposed to do with this??”
| |
+----------------------------+
| Other ELF sections… |
+----------------------------+
|.data | <- libbpf: “I don't care about this section”
| my_foo |
+----------------------------+
我是一个真正的艺术家,不是吗?
所以问题实际上来自这里clang如何处理你的全局变量。如果将定义移动到 main 函数内,clang 显然不会将其移动到它创建的目标文件中自己的 .data
部分。我想您正在尝试将变量移动到头文件中,可能是为了与其他源文件共享它;我不知道是否可以正确编译它,可能存在一些 clang 标志或一些预处理指令可以帮助你,但这超出了我的知识范围。
似乎静态全局变量重定位现在可以工作了(内核 5.4,Clang 10,Ubuntu 20.04)。在我的代码中,变量 test
的值在 BPF prog 的运行之间持续存在。
static __u64 test = 0;
SEC("cgroup_skb/egress")
int cb_pkt(struct __sk_buff *skb)
{
bpf_printk("Packet with size: %d\n", test);
test = skb->len;
return 1;
}