drdot
drdot

Reputation: 3347

How to let user space to populate an ebpf global data at load time?

I want to pass a variable value specified by the user from the command line from the user space program to the ebpf program. I know how to do it using bpf maps, but i heard there is a more efficient way to do this using bpf global data.

Can anyone give some code sample? (I am using libbpf)

Upvotes: 2

Views: 1505

Answers (1)

Dylan Reimerink
Dylan Reimerink

Reputation: 7978

Clang will take global data and put it into the .data, .rodata, or .bss section. .data if your value is initialized and can also change value, .rodata for const values and .bss for non-initialized values(will be initialized as 0).

In a normal program these sections would be loaded into the heap, but since eBPF has no heap these sections are loaded as special maps. libbpf calls these internal maps, they are essentially array maps with 1 key/value, the value is the size of the elf section. The generated eBPF then reads data at the appropriate offset in this data blob (using special instructions to improve performance over normal map loads).

Libbpf allows you to access and changes these maps. With the exception of .rodata which can't be modified since libbpf freezes it during loading.

Changing the value of the .bss secion can still be done, I believe you can do it with bpf_map__set_initial_value before calling bpf_object__load. Didn't go that route for the example.

Since a datasection can contain multiple values, we have to figure out how clang laid out the memory. For this we can use BTF, which encodes this data.

Disclaimer, this is likely not working code, slapped this together from examples and header files(I don't use libbpf that often, didn't test/compile it). But this should get you started in the right direction:

int main(int ac, char **argv)
{
    struct bpf_object *obj;
    struct bpf_program *prog;
    struct bpf_map *map;
    struct btf *btf;
    const struct btf_type *datasec;
    struct btf_var_secinfo *infos;
    int map_fd, prog_fd;
    __s32 datasec_id;
    char filename[256];
    int i, err;
    int zero = 0;
    FILE *f;

    snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);

    obj = bpf_object__open_file(filename, NULL);
    if (libbpf_get_error(obj))
        return 1;

    map = bpf_object__find_map_by_name(obj, ".data");
    if (libbpf_get_error(map))
        return 1;

    err = bpf_object__load(obj);
    if (err)
        return 1;

    map_fd = bpf_map__fd(map);
    if (libbpf_get_error(map_fd))
        return 1;

    // Create buffer the size of .data
    buff = malloc(bpf_map__def(map)->value_size);
    if (!buff)
        return 1;
    
    // Read .data into the buffer
    err = bpf_map_lookup_elem(map_fd, &zero, buff, 0);
    if (err)
        return 1;

    // Get BTF, we need it do find out the memory layout of .data
    btf = bpf_object__btf(obj);
    if (libbpf_get_error(btf))
        return 1;

    // Get the type ID of the datasection of .data
    datasec_id = btf__find_by_name(btf, ".data");
    if (libbpf_get_error(datasec_id))
        return 1;

    // Get the actual BTF type from the ID
    datasec = btf__type_by_id(btf, datasec_id)
    if (libbpf_get_error(datasec))
        return 1;

    // Get all secinfos, each of which will be a global variable
    infos = btf_var_secinfos(datasec);
    // Loop over all sections
    for(i = 0; i < btf_vlen(datasec); i++) {
        // Get the BTF type of the current var
        const struct btf_type *t = btf__type_by_id(btf, infos[i]->type);
        // Get the name of the global variable
        const char *name = btf__name_by_offset(btf, t->name_off);
        // If it matches the name of the var we want to change at runtime
        if (!strcmp(name, "global_var")) {
            // Overwrite its value (this code assumes just a single byte)
            // for multibyte values you will obviusly have to edit more bytes.
            // the total size of the var can be gotten from infos[i]->size
            buff[infos[i]->offset] = 0x12;
        }
    }

    // Write the updated datasection to the map
    err = bpf_map_update_elem(map_fd, &zero, buff, 0);
    if (err)
        return 1;

    free(buff);

    // TODO attach program somewhere
}

Upvotes: 2

Related Questions