ebpf - имена разделов

Обязательно ли иметь уникальные имена для каждого раздела программы в bpf программе? Например, эта программа отлично компилируется с llvm-5.0 :

...
SEC("sockops")
int bpf1(struct bpf_sock_ops *sk_ops)
{
   return 1;
}

SEC("sockops")
int bpf2(struct bpf_sock_ops *sk_ops)
{
   return 1;
}

SEC("sockops")
int bpf3(struct bpf_sock_ops *sk_ops)
{
   return 1;
}

SEC("sockops")
int bpf_main(struct bpf_sock_ops *sk_ops)
{
   __u32 port = bpf_ntohl(sk_ops->remote_port);

   switch (port) {
      case 5000: 
         bpf_tail_call(sk_ops, &jmp_table, 1);
         break;
      case 6000:
         bpf_tail_call(sk_ops, &jmp_table, 2);
         break;
      case 7000:
         bpf_tail_call(sk_ops, &jmp_table, 3);
         break;
   }     

   sk_ops->reply = 0;

   return 1;
}  

char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;

Однако llvm-objdump сообщает только об одном разделе программы:

$ llvm-objdump-5.0 -section-headers bpf_main.o

bpf_main.o:        file format ELF64-BPF

Sections:
Idx Name          Size      Address          Type
  0               00000000 0000000000000000 
  1 .strtab       000000a5 0000000000000000 
  2 .text         00000000 0000000000000000 TEXT DATA 
  3 sockops       000001f8 0000000000000000 TEXT DATA 
  4 .relsockops   00000030 0000000000000000 
  5 maps          0000001c 0000000000000000 DATA 
  6 .rodata.str1.16 00000021 0000000000000000 DATA 
  7 .rodata.str1.1 0000000e 0000000000000000 DATA 
  8 license       00000004 0000000000000000 DATA 
  9 version       00000004 0000000000000000 DATA 
 10 .eh_frame     00000090 0000000000000000 DATA 
 11 .rel.eh_frame 00000040 0000000000000000 
 12 .symtab       00000138 0000000000000000

Есть ли опция компилятора, чтобы дать хотя бы предупреждение?

ОБНОВЛЕНИЕ - я так понимаю, что разделы в одном и том же файле bpf должны иметь уникальные имена. Верно ли это в случаях, когда программы bpf находятся в разных файлах и компилируются независимо, чтобы они могли загружаться и вызывать друг друга, например. tail calls?


person Mark    schedule 16.01.2018    source источник


Ответы (1)


По-видимому, компилятор объединяет все в одном разделе (я упростил bpf_main, чтобы он компилировался и тестировался).

$ readelf -x sockops bpf_prog.o

Hex dump of section 'sockops':
  0x00000000 b7000000 01000000 95000000 00000000 ................
  0x00000010 b7000000 01000000 95000000 00000000 ................
  0x00000020 b7000000 01000000 95000000 00000000 ................
  0x00000030 b7000000 00000000 95000000 00000000 ...............

Отсюда я не вижу, как вы намереваетесь позже получить отдельные программы, чтобы загрузить их правильно. Обычно инструменты пользовательского пространства получают одну программу из раздела и пытаются загрузить их, передавая инструкции ядру через системный вызов bpf(); здесь любая программа, которую вы используете, вероятно, попытается загрузить объединенные четыре программы одновременно.

Есть ли особая причина, по которой вы хотели бы иметь все программы в разделах с одинаковыми именами?

Я понятия не имею о предупреждении в LLVM о такой вещи. Думаю, нет: у людей могут быть причины помещать разные вещи в раздел. Дело здесь конкретно в BPF, и я не думаю, что люди обычно стараются ставить несколько программ в один и тот же раздел. Но это всего лишь предположение, могу ошибаться.

Относительно вашего обновления:

Я не считаю, что использование одинаковых имен разделов в разных объектных файлах является проблемой. Пока ваш инструмент пользовательского пространства может извлекать программы из объектных файлов и выполнять перемещение, он должен работать нормально. Ядро все равно не видит ни одного имени раздела. Из аргумента attr он получает массив инструкций:

struct { /* anonymous struct used by BPF_PROG_LOAD command */
    __u32       prog_type;  /* one of enum bpf_prog_type */
    __u32       insn_cnt;
    __aligned_u64   insns;
    __aligned_u64   license;
    __u32       log_level;  /* verbosity level of verifier */
    __u32       log_size;   /* size of user buffer */
    __aligned_u64   log_buf;    /* user supplied buffer */
    __u32       kern_version;   /* checked when prog_type=kprobe */
    __u32       prog_flags;
    char        prog_name[BPF_OBJ_NAME_LEN];
    __u32       prog_ifindex;   /* ifindex of netdev to prep for */
};

(из include/uapi/linux/bpf.h обратите внимание на атрибут insns). Для хвостовых вызовов ваш код пользовательского пространства также должен создавать определенные карты, содержащие ссылки на программы, к которым вы хотите перейти (больше bpf() вызовов). Но опять же, вся ответственность за извлечение информации из файлов ELF лежит на пользовательском пространстве. Я думаю, что Libbpf должен правильно с этим справиться, но я не пробовал.

Замечание в завершение: я не знаю, каков именно ваш вариант использования, но поскольку вы пытаетесь скомпилировать свой код в разных объектных файлах и использовать вызовы, вам может быть интересно узнать, что eBPF теперь поддерживает «вызовы функций». , то есть вы можете определить несколько (не встроенных) функций, возможно, в нескольких ELF-файлах, и вызывать их в своей программе. Дополнительная информация в сопроводительном письме. Опять же, у меня не было времени экспериментировать с этим до сих пор.

person Qeole    schedule 16.01.2018
comment
Ну, я все еще новичок в BPF :-) Я не был уверен, как разделы анализируются и загружаются, теперь я вижу, что они должны иметь уникальные имена. Спасибо. - person Mark; 16.01.2018