2

我正在玩 的tail calls功能BPF,似乎没有加载简单的代码:

struct bpf_map_def SEC("maps") jmp_table = {
   .type = BPF_MAP_TYPE_PROG_ARRAY,
   .key_size = sizeof(u32),
   .value_size = sizeof(u32),
   .max_entries = 8,
};

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;

所以我编译它llv-3.8并加载bpftool

$ sudo ./bpftool prog load bpf_main.o /sys/fs/bpf/p1
libbpf: load bpf program failed: Invalid argument
libbpf: -- BEGIN DUMP LOG ---
libbpf: 
unreachable insn 2

libbpf: -- END LOG --
libbpf: failed to load program 'sockops'
libbpf: failed to load object 'bpf_main.o'
Error: failed to load program

所以man 2 bpf提到:

EINVAL For BPF_PROG_LOAD, indicates an attempt to load an invalid program. eBPF programs can be deemed  invalid  due  to  unrecognized  instructions, the  use  of reserved  fields, jumps  out of range, infinite loops or calls of unknown functions.

我看不出这个小小的简单程序有什么问题,也llvm-objdump失败了:

$ llvm-objdump-3.8 -arch-name=bpf -disassemble ./tcp_metrics_kern.o

./tcp_metrics_kern.o:   file format ELF64-unknown

LLVM ERROR: error: no disassembler for target bpfel-unknown-unknown

更新 1

按照 Qeole 的建议,我升级到clang-5.0,重建了我的程序,现在它的抱怨有所不同:

$ sudo ./bpftool prog load bpf_main.o /sys/fs/bpf/p1
libbpf: relocation failed: no 10 section
Error: failed to load program

现在我可以调查 ELF 部分:

$ llvm-objdump-5.0 -disassemble -source ./bpf_main.o

./bpf_main.o:   file format ELF64-BPF

Disassembly of section sockops:
bpf1:
       0:       b7 00 00 00 01 00 00 00         r0 = 1
       1:       95 00 00 00 00 00 00 00         exit

bpf2:
       2:       b7 00 00 00 01 00 00 00         r0 = 1
       3:       95 00 00 00 00 00 00 00         exit

bpf3:
       4:       b7 00 00 00 01 00 00 00         r0 = 1
       5:       95 00 00 00 00 00 00 00         exit

bpf_main:
...

以下是可用的部分:

    $ 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

看起来bpftool找不到部分.eh_frame

更新 2

我继续试验 :-) 首先我更新libbpf了最新的提交d77be68955475fc2321e73fe006240248f2f8fef修复字符串比较,然后我用 重建程序-fno-asynchronous-unwind-tables,这不包括.eh_frame部分,而且我给出了唯一的部分名称,例如 sockops0、sockops1 等。现在bpftool prog load ..成功但bpftool prog show只转储一个程序,最先运行的程序,在我的例子中是 bpf1()。

目前我可以说 bpf_object__load_progs() 将 obj->nr_programs 报告为 4,这对我的示例来说是有意义的。

4

0 回答 0