4

程序中的每个程序部分都必须有唯一的名称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吗?

4

1 回答 1

3

显然,编译器将同一部分中的所有内容连接起来(我简化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 文件中,并在您的程序中调用它们。求职信中的更多信息。同样,到目前为止,我还没有时间尝试它。

于 2018-01-16T19:26:49.753 回答