Search code examples
networkinglinux-kernelllvm-clangbpf

eBPF: global variables and structs


So I have a simple eBPF code:

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;

I build the code with llvm-5.0, with no errors/warnings, however bpftool prog load ... fails:

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)
$

Section 6 contains my my_foo structure, I could dump its contents with llvm-objdump.

This error does not happen if I define my_foo inside main() function for instance. Does it mean such global declarations are not permitted by eBPF convention?


Solution

  • eBPF knows nothing about global variables. When bpftool sends your program to the kernel, it only sends one piece of bytecode instructions that is supposed to be “self-contained” (at least if you don't use eBPF function calls, but eBPF functions are not yet supported by libbpf and bpftool so I assume this is not the case).

    Anyway: when bpftool calls libbpf to load your program from the ELF file, it expects to find the whole self-contained program in one ELF section. There is an exception for maps, for which some metadata is placed into a specific ELF section. Other than this, libbpf does not know how to get the definition of your global variable my_foo from the .data section and to move it into the main section. This is why it warns you about non-map related relo[cation] data in this .data section.

    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                    |
    +----------------------------+
    

    I'm a true artist, aren't I?

    So the problem actually comes from how clang handles your global variable here. If you move the definition inside the main function, clang apparently does not move it to its own .data section in the object file it creates. I suppose you are trying to move the variable to a header file, possibly to share it with other source files; I don't know if this is possible to have this to compile correctly, there may exist some flags for clang or some preprocessing directives that would help you, but this is beyond my knowledge.