libbpf: Generate loader program out of BPF ELF file.
The BPF program loading process performed by libbpf is quite complex
and consists of the following steps:
"open" phase:
- parse elf file and remember relocations, sections
- collect externs and ksyms including their btf_ids in prog's BTF
- patch BTF datasec (since llvm couldn't do it)
- init maps (old style map_def, BTF based, global data map, kconfig map)
- collect relocations against progs and maps
"load" phase:
- probe kernel features
- load vmlinux BTF
- resolve externs (kconfig and ksym)
- load program BTF
- init struct_ops
- create maps
- apply CO-RE relocations
- patch ld_imm64 insns with src_reg=PSEUDO_MAP, PSEUDO_MAP_VALUE, PSEUDO_BTF_ID
- reposition subprograms and adjust call insns
- sanitize and load progs
During this process libbpf does sys_bpf() calls to load BTF, create maps,
populate maps and finally load programs.
Instead of actually doing the syscalls generate a trace of what libbpf
would have done and represent it as the "loader program".
The "loader program" consists of single map with:
- union bpf_attr(s)
- BTF bytes
- map value bytes
- insns bytes
and single bpf program that passes bpf_attr(s) and data into bpf_sys_bpf() helper.
Executing such "loader program" via bpf_prog_test_run() command will
replay the sequence of syscalls that libbpf would have done which will result
the same maps created and programs loaded as specified in the elf file.
The "loader program" removes libelf and majority of libbpf dependency from
program loading process.
kconfig, typeless ksym, struct_ops and CO-RE are not supported yet.
The order of relocate_data and relocate_calls had to change, so that
bpf_gen__prog_load() can see all relocations for a given program with
correct insn_idx-es.
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20210514003623.28033-15-alexei.starovoitov@gmail.com
2021-05-14 08:36:16 +08:00
|
|
|
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
|
|
|
/* Copyright (c) 2021 Facebook */
|
|
|
|
#ifndef __SKEL_INTERNAL_H
|
|
|
|
#define __SKEL_INTERNAL_H
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/syscall.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
|
|
|
|
/* This file is a base header for auto-generated *.lskel.h files.
|
|
|
|
* Its contents will change and may become part of auto-generation in the future.
|
|
|
|
*
|
|
|
|
* The layout of bpf_[map|prog]_desc and bpf_loader_ctx is feature dependent
|
|
|
|
* and will change from one version of libbpf to another and features
|
|
|
|
* requested during loader program generation.
|
|
|
|
*/
|
|
|
|
struct bpf_map_desc {
|
|
|
|
union {
|
|
|
|
/* input for the loader prog */
|
|
|
|
struct {
|
|
|
|
__aligned_u64 initial_value;
|
|
|
|
__u32 max_entries;
|
|
|
|
};
|
|
|
|
/* output of the loader prog */
|
|
|
|
struct {
|
|
|
|
int map_fd;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
struct bpf_prog_desc {
|
|
|
|
int prog_fd;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct bpf_loader_ctx {
|
|
|
|
size_t sz;
|
|
|
|
__u32 log_level;
|
|
|
|
__u32 log_size;
|
|
|
|
__u64 log_buf;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct bpf_load_and_run_opts {
|
|
|
|
struct bpf_loader_ctx *ctx;
|
|
|
|
const void *data;
|
|
|
|
const void *insns;
|
|
|
|
__u32 data_sz;
|
|
|
|
__u32 insns_sz;
|
|
|
|
const char *errstr;
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline int skel_sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
|
|
|
|
unsigned int size)
|
|
|
|
{
|
|
|
|
return syscall(__NR_bpf, cmd, attr, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int skel_closenz(int fd)
|
|
|
|
{
|
|
|
|
if (fd > 0)
|
|
|
|
return close(fd);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts)
|
|
|
|
{
|
|
|
|
int map_fd = -1, prog_fd = -1, key = 0, err;
|
|
|
|
union bpf_attr attr;
|
|
|
|
|
|
|
|
map_fd = bpf_create_map_name(BPF_MAP_TYPE_ARRAY, "__loader.map", 4,
|
|
|
|
opts->data_sz, 1, 0);
|
|
|
|
if (map_fd < 0) {
|
|
|
|
opts->errstr = "failed to create loader map";
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = bpf_map_update_elem(map_fd, &key, opts->data, 0);
|
|
|
|
if (err < 0) {
|
|
|
|
opts->errstr = "failed to update loader map";
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
|
|
attr.prog_type = BPF_PROG_TYPE_SYSCALL;
|
|
|
|
attr.insns = (long) opts->insns;
|
|
|
|
attr.insn_cnt = opts->insns_sz / sizeof(struct bpf_insn);
|
|
|
|
attr.license = (long) "Dual BSD/GPL";
|
|
|
|
memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog"));
|
|
|
|
attr.fd_array = (long) &map_fd;
|
|
|
|
attr.log_level = opts->ctx->log_level;
|
|
|
|
attr.log_size = opts->ctx->log_size;
|
|
|
|
attr.log_buf = opts->ctx->log_buf;
|
|
|
|
attr.prog_flags = BPF_F_SLEEPABLE;
|
|
|
|
prog_fd = skel_sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
|
|
|
|
if (prog_fd < 0) {
|
|
|
|
opts->errstr = "failed to load loader prog";
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
|
|
attr.test.prog_fd = prog_fd;
|
|
|
|
attr.test.ctx_in = (long) opts->ctx;
|
|
|
|
attr.test.ctx_size_in = opts->ctx->sz;
|
2021-05-19 09:40:32 +08:00
|
|
|
err = skel_sys_bpf(BPF_PROG_RUN, &attr, sizeof(attr));
|
libbpf: Generate loader program out of BPF ELF file.
The BPF program loading process performed by libbpf is quite complex
and consists of the following steps:
"open" phase:
- parse elf file and remember relocations, sections
- collect externs and ksyms including their btf_ids in prog's BTF
- patch BTF datasec (since llvm couldn't do it)
- init maps (old style map_def, BTF based, global data map, kconfig map)
- collect relocations against progs and maps
"load" phase:
- probe kernel features
- load vmlinux BTF
- resolve externs (kconfig and ksym)
- load program BTF
- init struct_ops
- create maps
- apply CO-RE relocations
- patch ld_imm64 insns with src_reg=PSEUDO_MAP, PSEUDO_MAP_VALUE, PSEUDO_BTF_ID
- reposition subprograms and adjust call insns
- sanitize and load progs
During this process libbpf does sys_bpf() calls to load BTF, create maps,
populate maps and finally load programs.
Instead of actually doing the syscalls generate a trace of what libbpf
would have done and represent it as the "loader program".
The "loader program" consists of single map with:
- union bpf_attr(s)
- BTF bytes
- map value bytes
- insns bytes
and single bpf program that passes bpf_attr(s) and data into bpf_sys_bpf() helper.
Executing such "loader program" via bpf_prog_test_run() command will
replay the sequence of syscalls that libbpf would have done which will result
the same maps created and programs loaded as specified in the elf file.
The "loader program" removes libelf and majority of libbpf dependency from
program loading process.
kconfig, typeless ksym, struct_ops and CO-RE are not supported yet.
The order of relocate_data and relocate_calls had to change, so that
bpf_gen__prog_load() can see all relocations for a given program with
correct insn_idx-es.
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20210514003623.28033-15-alexei.starovoitov@gmail.com
2021-05-14 08:36:16 +08:00
|
|
|
if (err < 0 || (int)attr.test.retval < 0) {
|
|
|
|
opts->errstr = "failed to execute loader prog";
|
|
|
|
if (err < 0)
|
|
|
|
err = -errno;
|
|
|
|
else
|
|
|
|
err = (int)attr.test.retval;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
err = 0;
|
|
|
|
out:
|
|
|
|
if (map_fd >= 0)
|
|
|
|
close(map_fd);
|
|
|
|
if (prog_fd >= 0)
|
|
|
|
close(prog_fd);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|