eBPF:全局变量和结构

Mar*_*ark 2 networking linux-kernel llvm-clang bpf

所以我有一个简单的eBPF代码:

我的.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__ */
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

我使用 构建代码llvm-5.0,没有错误/警告,但是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)
$
Run Code Online (Sandbox Code Playgroud)

第 6 节包含我的my_foo结构,我可以使用llvm-objdump.

例如,如果我定义my_foo内部函数,则不会发生此错误。main()这是否意味着 eBPF 约定不允许此类全局声明?

Qeo*_*ole 8

eBPF 对全局变量一无所知。当bpftool你的程序发送到内核时,它只发送一条字节码指令,该指令应该是 \xe2\x80\x9cself-contained\xe2\x80\x9d (至少如果你不使用 eBPF 函数调用,但 eBPF libbpf 尚不支持函数,bpftool因此我认为情况并非如此)。

\n\n

无论如何:当bpftool调用 libbpf 从 ELF 文件加载程序时,它期望在一个 ELF 部分中找到整个独立程序。映射有一个例外,其中一些元数据被放置到特定的 ELF 部分中。除此之外,libbpf 不知道如何my_foo从该.data部分获取全局变量的定义并将其移至主部分。这就是它non-map related relo[cation] data在本.data节中向您发出警告的原因。

\n\n
my_kern.o\n+----------------------------+\n| ELF header                 |\n+----------------------------+\n|sockops                     |\n|                            |\n|  eBPF instructions         |\n|  |                         |\n|  ->\xe2\x80\x9cget my_foo from .data\xe2\x80\x9d | <- libbpf: \xe2\x80\x9cWhat am I supposed to do with this??\xe2\x80\x9d\n|                            |\n+----------------------------+\n| Other ELF sections\xe2\x80\xa6        |\n+----------------------------+\n|.data                       | <- libbpf: \xe2\x80\x9cI don't care about this section\xe2\x80\x9d\n|  my_foo                    |\n+----------------------------+\n
Run Code Online (Sandbox Code Playgroud)\n\n

我是一个真正的艺术家,不是吗?

\n\n

所以问题实际上来自于 clang 如何处理你的全局变量。如果将定义移动到 main 函数内,clang 显然不会将其移动到.data它创建的目标文件中自己的部分。我想您正在尝试将变量移动到头文件中,可能与其他源文件共享它;我不知道是否可以正确编译,可能存在一些 clang 标志或一些预处理指令可以帮助您,但这超出了我的知识范围。

\n

  • 我认为这个答案需要更新,因为现在支持全局变量。 (3认同)

SPY*_*YFF 5

看起来静态全局变量重定位现在可以工作了(内核 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;
}
Run Code Online (Sandbox Code Playgroud)