eBPF: глобальные переменные и структуры

Итак, у меня есть простой 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без ошибок / предупреждений, однако 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,

Эта ошибка не произойдет, если я определю my_foo внутри main() функция, например. Означает ли это, что такие глобальные объявления не разрешены соглашением eBPF?

1 ответ

Решение

eBPF ничего не знает о глобальных переменных. когда bpftool отправляет вашу программу ядру, она отправляет только одну часть инструкций байт-кода, которая должна быть "автономной" (по крайней мере, если вы не используете вызовы функций eBPF, но функции eBPF еще не поддерживаются libbpf и bpftool так что я предполагаю, что это не так).

Во всяком случае, когда bpftool вызывает libbpf для загрузки вашей программы из файла ELF, он ожидает найти всю автономную программу в одном разделе ELF. Существует исключение для карт, для которых некоторые метаданные помещаются в определенный раздел ELF. Кроме этого, libbpf не знает, как получить определение вашей глобальной переменной my_foo от .data раздел и переместить его в основной раздел. Вот почему он предупреждает вас о non-map related relo[cation] data в этом .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 обрабатывает вашу глобальную переменную здесь. Если вы переместите определение в основную функцию, clang, по-видимому, не переместит его в свою собственную .data раздел в объектном файле, который он создает. Я предполагаю, что вы пытаетесь переместить переменную в заголовочный файл, возможно, чтобы поделиться им с другими исходными файлами; Я не знаю, возможно ли это сделать для правильной компиляции, могут существовать некоторые флаги для clang или какие-то директивы предварительной обработки, которые могут вам помочь, но это вне моего понимания.

Похоже, теперь работает статическое перемещение глобальных переменных (ядро 5.4, Clang 10, Ubuntu 20.04). В моем коде значение переменнойtest сохраняется между прогонами программы BPF.

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;
}
Другие вопросы по тегам