mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-25 20:05:18 +07:00
94e5adece8
Commit4708bbda5c
("tools lib bpf: Fix maps resolution") attempted to fix map resolution by identifying the number of symbols that point to maps, and using this number to resolve each of the maps. However, during relocation the original definition of the map size was still in use. For up to two maps, the calculation was correct if there was a small difference in size between the map definition in libbpf and the one that the client library uses. However if the difference was large, particularly if more than two maps were used in the BPF program, the relocation would fail. For example, when using a map definition with size 28, with three maps, map relocation would count: (sym_offset / sizeof(struct bpf_map_def) => map_idx) (0 / 16 => 0), ie map_idx = 0 (28 / 16 => 1), ie map_idx = 1 (56 / 16 => 3), ie map_idx = 3 So, libbpf reports: libbpf: bpf relocation: map_idx 3 large than 2 Fix map relocation by checking the exact offset of maps when doing relocation. Signed-off-by: Joe Stringer <joe@ovn.org> [Allow different map size in an object] Signed-off-by: Wang Nan <wangnan0@huawei.com> Cc: Alexei Starovoitov <ast@fb.com> Cc: Daniel Borkmann <daniel@iogearbox.net> Cc: netdev@vger.kernel.org Fixes:4708bbda5c
("tools lib bpf: Fix maps resolution") Link: http://lkml.kernel.org/r/20170123011128.26534-2-joe@ovn.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
1549 lines
34 KiB
C
1549 lines
34 KiB
C
/*
|
|
* Common eBPF ELF object loading operations.
|
|
*
|
|
* Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
|
|
* Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
|
|
* Copyright (C) 2015 Huawei Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License (not later!)
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <inttypes.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <asm/unistd.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/bpf.h>
|
|
#include <linux/list.h>
|
|
#include <libelf.h>
|
|
#include <gelf.h>
|
|
|
|
#include "libbpf.h"
|
|
#include "bpf.h"
|
|
|
|
#ifndef EM_BPF
|
|
#define EM_BPF 247
|
|
#endif
|
|
|
|
#define __printf(a, b) __attribute__((format(printf, a, b)))
|
|
|
|
__printf(1, 2)
|
|
static int __base_pr(const char *format, ...)
|
|
{
|
|
va_list args;
|
|
int err;
|
|
|
|
va_start(args, format);
|
|
err = vfprintf(stderr, format, args);
|
|
va_end(args);
|
|
return err;
|
|
}
|
|
|
|
static __printf(1, 2) libbpf_print_fn_t __pr_warning = __base_pr;
|
|
static __printf(1, 2) libbpf_print_fn_t __pr_info = __base_pr;
|
|
static __printf(1, 2) libbpf_print_fn_t __pr_debug;
|
|
|
|
#define __pr(func, fmt, ...) \
|
|
do { \
|
|
if ((func)) \
|
|
(func)("libbpf: " fmt, ##__VA_ARGS__); \
|
|
} while (0)
|
|
|
|
#define pr_warning(fmt, ...) __pr(__pr_warning, fmt, ##__VA_ARGS__)
|
|
#define pr_info(fmt, ...) __pr(__pr_info, fmt, ##__VA_ARGS__)
|
|
#define pr_debug(fmt, ...) __pr(__pr_debug, fmt, ##__VA_ARGS__)
|
|
|
|
void libbpf_set_print(libbpf_print_fn_t warn,
|
|
libbpf_print_fn_t info,
|
|
libbpf_print_fn_t debug)
|
|
{
|
|
__pr_warning = warn;
|
|
__pr_info = info;
|
|
__pr_debug = debug;
|
|
}
|
|
|
|
#define STRERR_BUFSIZE 128
|
|
|
|
#define ERRNO_OFFSET(e) ((e) - __LIBBPF_ERRNO__START)
|
|
#define ERRCODE_OFFSET(c) ERRNO_OFFSET(LIBBPF_ERRNO__##c)
|
|
#define NR_ERRNO (__LIBBPF_ERRNO__END - __LIBBPF_ERRNO__START)
|
|
|
|
static const char *libbpf_strerror_table[NR_ERRNO] = {
|
|
[ERRCODE_OFFSET(LIBELF)] = "Something wrong in libelf",
|
|
[ERRCODE_OFFSET(FORMAT)] = "BPF object format invalid",
|
|
[ERRCODE_OFFSET(KVERSION)] = "'version' section incorrect or lost",
|
|
[ERRCODE_OFFSET(ENDIAN)] = "Endian mismatch",
|
|
[ERRCODE_OFFSET(INTERNAL)] = "Internal error in libbpf",
|
|
[ERRCODE_OFFSET(RELOC)] = "Relocation failed",
|
|
[ERRCODE_OFFSET(VERIFY)] = "Kernel verifier blocks program loading",
|
|
[ERRCODE_OFFSET(PROG2BIG)] = "Program too big",
|
|
[ERRCODE_OFFSET(KVER)] = "Incorrect kernel version",
|
|
[ERRCODE_OFFSET(PROGTYPE)] = "Kernel doesn't support this program type",
|
|
};
|
|
|
|
int libbpf_strerror(int err, char *buf, size_t size)
|
|
{
|
|
if (!buf || !size)
|
|
return -1;
|
|
|
|
err = err > 0 ? err : -err;
|
|
|
|
if (err < __LIBBPF_ERRNO__START) {
|
|
int ret;
|
|
|
|
ret = strerror_r(err, buf, size);
|
|
buf[size - 1] = '\0';
|
|
return ret;
|
|
}
|
|
|
|
if (err < __LIBBPF_ERRNO__END) {
|
|
const char *msg;
|
|
|
|
msg = libbpf_strerror_table[ERRNO_OFFSET(err)];
|
|
snprintf(buf, size, "%s", msg);
|
|
buf[size - 1] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
snprintf(buf, size, "Unknown libbpf error %d", err);
|
|
buf[size - 1] = '\0';
|
|
return -1;
|
|
}
|
|
|
|
#define CHECK_ERR(action, err, out) do { \
|
|
err = action; \
|
|
if (err) \
|
|
goto out; \
|
|
} while(0)
|
|
|
|
|
|
/* Copied from tools/perf/util/util.h */
|
|
#ifndef zfree
|
|
# define zfree(ptr) ({ free(*ptr); *ptr = NULL; })
|
|
#endif
|
|
|
|
#ifndef zclose
|
|
# define zclose(fd) ({ \
|
|
int ___err = 0; \
|
|
if ((fd) >= 0) \
|
|
___err = close((fd)); \
|
|
fd = -1; \
|
|
___err; })
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBELF_MMAP_SUPPORT
|
|
# define LIBBPF_ELF_C_READ_MMAP ELF_C_READ_MMAP
|
|
#else
|
|
# define LIBBPF_ELF_C_READ_MMAP ELF_C_READ
|
|
#endif
|
|
|
|
/*
|
|
* bpf_prog should be a better name but it has been used in
|
|
* linux/filter.h.
|
|
*/
|
|
struct bpf_program {
|
|
/* Index in elf obj file, for relocation use. */
|
|
int idx;
|
|
char *section_name;
|
|
struct bpf_insn *insns;
|
|
size_t insns_cnt;
|
|
enum bpf_prog_type type;
|
|
|
|
struct {
|
|
int insn_idx;
|
|
int map_idx;
|
|
} *reloc_desc;
|
|
int nr_reloc;
|
|
|
|
struct {
|
|
int nr;
|
|
int *fds;
|
|
} instances;
|
|
bpf_program_prep_t preprocessor;
|
|
|
|
struct bpf_object *obj;
|
|
void *priv;
|
|
bpf_program_clear_priv_t clear_priv;
|
|
};
|
|
|
|
struct bpf_map {
|
|
int fd;
|
|
char *name;
|
|
size_t offset;
|
|
struct bpf_map_def def;
|
|
void *priv;
|
|
bpf_map_clear_priv_t clear_priv;
|
|
};
|
|
|
|
static LIST_HEAD(bpf_objects_list);
|
|
|
|
struct bpf_object {
|
|
char license[64];
|
|
u32 kern_version;
|
|
|
|
struct bpf_program *programs;
|
|
size_t nr_programs;
|
|
struct bpf_map *maps;
|
|
size_t nr_maps;
|
|
|
|
bool loaded;
|
|
|
|
/*
|
|
* Information when doing elf related work. Only valid if fd
|
|
* is valid.
|
|
*/
|
|
struct {
|
|
int fd;
|
|
void *obj_buf;
|
|
size_t obj_buf_sz;
|
|
Elf *elf;
|
|
GElf_Ehdr ehdr;
|
|
Elf_Data *symbols;
|
|
size_t strtabidx;
|
|
struct {
|
|
GElf_Shdr shdr;
|
|
Elf_Data *data;
|
|
} *reloc;
|
|
int nr_reloc;
|
|
int maps_shndx;
|
|
} efile;
|
|
/*
|
|
* All loaded bpf_object is linked in a list, which is
|
|
* hidden to caller. bpf_objects__<func> handlers deal with
|
|
* all objects.
|
|
*/
|
|
struct list_head list;
|
|
|
|
void *priv;
|
|
bpf_object_clear_priv_t clear_priv;
|
|
|
|
char path[];
|
|
};
|
|
#define obj_elf_valid(o) ((o)->efile.elf)
|
|
|
|
static void bpf_program__unload(struct bpf_program *prog)
|
|
{
|
|
int i;
|
|
|
|
if (!prog)
|
|
return;
|
|
|
|
/*
|
|
* If the object is opened but the program was never loaded,
|
|
* it is possible that prog->instances.nr == -1.
|
|
*/
|
|
if (prog->instances.nr > 0) {
|
|
for (i = 0; i < prog->instances.nr; i++)
|
|
zclose(prog->instances.fds[i]);
|
|
} else if (prog->instances.nr != -1) {
|
|
pr_warning("Internal error: instances.nr is %d\n",
|
|
prog->instances.nr);
|
|
}
|
|
|
|
prog->instances.nr = -1;
|
|
zfree(&prog->instances.fds);
|
|
}
|
|
|
|
static void bpf_program__exit(struct bpf_program *prog)
|
|
{
|
|
if (!prog)
|
|
return;
|
|
|
|
if (prog->clear_priv)
|
|
prog->clear_priv(prog, prog->priv);
|
|
|
|
prog->priv = NULL;
|
|
prog->clear_priv = NULL;
|
|
|
|
bpf_program__unload(prog);
|
|
zfree(&prog->section_name);
|
|
zfree(&prog->insns);
|
|
zfree(&prog->reloc_desc);
|
|
|
|
prog->nr_reloc = 0;
|
|
prog->insns_cnt = 0;
|
|
prog->idx = -1;
|
|
}
|
|
|
|
static int
|
|
bpf_program__init(void *data, size_t size, char *name, int idx,
|
|
struct bpf_program *prog)
|
|
{
|
|
if (size < sizeof(struct bpf_insn)) {
|
|
pr_warning("corrupted section '%s'\n", name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
bzero(prog, sizeof(*prog));
|
|
|
|
prog->section_name = strdup(name);
|
|
if (!prog->section_name) {
|
|
pr_warning("failed to alloc name for prog %s\n",
|
|
name);
|
|
goto errout;
|
|
}
|
|
|
|
prog->insns = malloc(size);
|
|
if (!prog->insns) {
|
|
pr_warning("failed to alloc insns for %s\n", name);
|
|
goto errout;
|
|
}
|
|
prog->insns_cnt = size / sizeof(struct bpf_insn);
|
|
memcpy(prog->insns, data,
|
|
prog->insns_cnt * sizeof(struct bpf_insn));
|
|
prog->idx = idx;
|
|
prog->instances.fds = NULL;
|
|
prog->instances.nr = -1;
|
|
prog->type = BPF_PROG_TYPE_KPROBE;
|
|
|
|
return 0;
|
|
errout:
|
|
bpf_program__exit(prog);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static int
|
|
bpf_object__add_program(struct bpf_object *obj, void *data, size_t size,
|
|
char *name, int idx)
|
|
{
|
|
struct bpf_program prog, *progs;
|
|
int nr_progs, err;
|
|
|
|
err = bpf_program__init(data, size, name, idx, &prog);
|
|
if (err)
|
|
return err;
|
|
|
|
progs = obj->programs;
|
|
nr_progs = obj->nr_programs;
|
|
|
|
progs = realloc(progs, sizeof(progs[0]) * (nr_progs + 1));
|
|
if (!progs) {
|
|
/*
|
|
* In this case the original obj->programs
|
|
* is still valid, so don't need special treat for
|
|
* bpf_close_object().
|
|
*/
|
|
pr_warning("failed to alloc a new program '%s'\n",
|
|
name);
|
|
bpf_program__exit(&prog);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
pr_debug("found program %s\n", prog.section_name);
|
|
obj->programs = progs;
|
|
obj->nr_programs = nr_progs + 1;
|
|
prog.obj = obj;
|
|
progs[nr_progs] = prog;
|
|
return 0;
|
|
}
|
|
|
|
static struct bpf_object *bpf_object__new(const char *path,
|
|
void *obj_buf,
|
|
size_t obj_buf_sz)
|
|
{
|
|
struct bpf_object *obj;
|
|
|
|
obj = calloc(1, sizeof(struct bpf_object) + strlen(path) + 1);
|
|
if (!obj) {
|
|
pr_warning("alloc memory failed for %s\n", path);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
|
|
strcpy(obj->path, path);
|
|
obj->efile.fd = -1;
|
|
|
|
/*
|
|
* Caller of this function should also calls
|
|
* bpf_object__elf_finish() after data collection to return
|
|
* obj_buf to user. If not, we should duplicate the buffer to
|
|
* avoid user freeing them before elf finish.
|
|
*/
|
|
obj->efile.obj_buf = obj_buf;
|
|
obj->efile.obj_buf_sz = obj_buf_sz;
|
|
obj->efile.maps_shndx = -1;
|
|
|
|
obj->loaded = false;
|
|
|
|
INIT_LIST_HEAD(&obj->list);
|
|
list_add(&obj->list, &bpf_objects_list);
|
|
return obj;
|
|
}
|
|
|
|
static void bpf_object__elf_finish(struct bpf_object *obj)
|
|
{
|
|
if (!obj_elf_valid(obj))
|
|
return;
|
|
|
|
if (obj->efile.elf) {
|
|
elf_end(obj->efile.elf);
|
|
obj->efile.elf = NULL;
|
|
}
|
|
obj->efile.symbols = NULL;
|
|
|
|
zfree(&obj->efile.reloc);
|
|
obj->efile.nr_reloc = 0;
|
|
zclose(obj->efile.fd);
|
|
obj->efile.obj_buf = NULL;
|
|
obj->efile.obj_buf_sz = 0;
|
|
}
|
|
|
|
static int bpf_object__elf_init(struct bpf_object *obj)
|
|
{
|
|
int err = 0;
|
|
GElf_Ehdr *ep;
|
|
|
|
if (obj_elf_valid(obj)) {
|
|
pr_warning("elf init: internal error\n");
|
|
return -LIBBPF_ERRNO__LIBELF;
|
|
}
|
|
|
|
if (obj->efile.obj_buf_sz > 0) {
|
|
/*
|
|
* obj_buf should have been validated by
|
|
* bpf_object__open_buffer().
|
|
*/
|
|
obj->efile.elf = elf_memory(obj->efile.obj_buf,
|
|
obj->efile.obj_buf_sz);
|
|
} else {
|
|
obj->efile.fd = open(obj->path, O_RDONLY);
|
|
if (obj->efile.fd < 0) {
|
|
pr_warning("failed to open %s: %s\n", obj->path,
|
|
strerror(errno));
|
|
return -errno;
|
|
}
|
|
|
|
obj->efile.elf = elf_begin(obj->efile.fd,
|
|
LIBBPF_ELF_C_READ_MMAP,
|
|
NULL);
|
|
}
|
|
|
|
if (!obj->efile.elf) {
|
|
pr_warning("failed to open %s as ELF file\n",
|
|
obj->path);
|
|
err = -LIBBPF_ERRNO__LIBELF;
|
|
goto errout;
|
|
}
|
|
|
|
if (!gelf_getehdr(obj->efile.elf, &obj->efile.ehdr)) {
|
|
pr_warning("failed to get EHDR from %s\n",
|
|
obj->path);
|
|
err = -LIBBPF_ERRNO__FORMAT;
|
|
goto errout;
|
|
}
|
|
ep = &obj->efile.ehdr;
|
|
|
|
/* Old LLVM set e_machine to EM_NONE */
|
|
if ((ep->e_type != ET_REL) || (ep->e_machine && (ep->e_machine != EM_BPF))) {
|
|
pr_warning("%s is not an eBPF object file\n",
|
|
obj->path);
|
|
err = -LIBBPF_ERRNO__FORMAT;
|
|
goto errout;
|
|
}
|
|
|
|
return 0;
|
|
errout:
|
|
bpf_object__elf_finish(obj);
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
bpf_object__check_endianness(struct bpf_object *obj)
|
|
{
|
|
static unsigned int const endian = 1;
|
|
|
|
switch (obj->efile.ehdr.e_ident[EI_DATA]) {
|
|
case ELFDATA2LSB:
|
|
/* We are big endian, BPF obj is little endian. */
|
|
if (*(unsigned char const *)&endian != 1)
|
|
goto mismatch;
|
|
break;
|
|
|
|
case ELFDATA2MSB:
|
|
/* We are little endian, BPF obj is big endian. */
|
|
if (*(unsigned char const *)&endian != 0)
|
|
goto mismatch;
|
|
break;
|
|
default:
|
|
return -LIBBPF_ERRNO__ENDIAN;
|
|
}
|
|
|
|
return 0;
|
|
|
|
mismatch:
|
|
pr_warning("Error: endianness mismatch.\n");
|
|
return -LIBBPF_ERRNO__ENDIAN;
|
|
}
|
|
|
|
static int
|
|
bpf_object__init_license(struct bpf_object *obj,
|
|
void *data, size_t size)
|
|
{
|
|
memcpy(obj->license, data,
|
|
min(size, sizeof(obj->license) - 1));
|
|
pr_debug("license of %s is %s\n", obj->path, obj->license);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
bpf_object__init_kversion(struct bpf_object *obj,
|
|
void *data, size_t size)
|
|
{
|
|
u32 kver;
|
|
|
|
if (size != sizeof(kver)) {
|
|
pr_warning("invalid kver section in %s\n", obj->path);
|
|
return -LIBBPF_ERRNO__FORMAT;
|
|
}
|
|
memcpy(&kver, data, sizeof(kver));
|
|
obj->kern_version = kver;
|
|
pr_debug("kernel version of %s is %x\n", obj->path,
|
|
obj->kern_version);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
bpf_object__validate_maps(struct bpf_object *obj)
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* If there's only 1 map, the only error case should have been
|
|
* catched in bpf_object__init_maps().
|
|
*/
|
|
if (!obj->maps || !obj->nr_maps || (obj->nr_maps == 1))
|
|
return 0;
|
|
|
|
for (i = 1; i < obj->nr_maps; i++) {
|
|
const struct bpf_map *a = &obj->maps[i - 1];
|
|
const struct bpf_map *b = &obj->maps[i];
|
|
|
|
if (b->offset - a->offset < sizeof(struct bpf_map_def)) {
|
|
pr_warning("corrupted map section in %s: map \"%s\" too small\n",
|
|
obj->path, a->name);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int compare_bpf_map(const void *_a, const void *_b)
|
|
{
|
|
const struct bpf_map *a = _a;
|
|
const struct bpf_map *b = _b;
|
|
|
|
return a->offset - b->offset;
|
|
}
|
|
|
|
static int
|
|
bpf_object__init_maps(struct bpf_object *obj)
|
|
{
|
|
int i, map_idx, nr_maps = 0;
|
|
Elf_Scn *scn;
|
|
Elf_Data *data;
|
|
Elf_Data *symbols = obj->efile.symbols;
|
|
|
|
if (obj->efile.maps_shndx < 0)
|
|
return -EINVAL;
|
|
if (!symbols)
|
|
return -EINVAL;
|
|
|
|
scn = elf_getscn(obj->efile.elf, obj->efile.maps_shndx);
|
|
if (scn)
|
|
data = elf_getdata(scn, NULL);
|
|
if (!scn || !data) {
|
|
pr_warning("failed to get Elf_Data from map section %d\n",
|
|
obj->efile.maps_shndx);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Count number of maps. Each map has a name.
|
|
* Array of maps is not supported: only the first element is
|
|
* considered.
|
|
*
|
|
* TODO: Detect array of map and report error.
|
|
*/
|
|
for (i = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) {
|
|
GElf_Sym sym;
|
|
|
|
if (!gelf_getsym(symbols, i, &sym))
|
|
continue;
|
|
if (sym.st_shndx != obj->efile.maps_shndx)
|
|
continue;
|
|
nr_maps++;
|
|
}
|
|
|
|
/* Alloc obj->maps and fill nr_maps. */
|
|
pr_debug("maps in %s: %d maps in %zd bytes\n", obj->path,
|
|
nr_maps, data->d_size);
|
|
|
|
if (!nr_maps)
|
|
return 0;
|
|
|
|
obj->maps = calloc(nr_maps, sizeof(obj->maps[0]));
|
|
if (!obj->maps) {
|
|
pr_warning("alloc maps for object failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
obj->nr_maps = nr_maps;
|
|
|
|
/*
|
|
* fill all fd with -1 so won't close incorrect
|
|
* fd (fd=0 is stdin) when failure (zclose won't close
|
|
* negative fd)).
|
|
*/
|
|
for (i = 0; i < nr_maps; i++)
|
|
obj->maps[i].fd = -1;
|
|
|
|
/*
|
|
* Fill obj->maps using data in "maps" section.
|
|
*/
|
|
for (i = 0, map_idx = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) {
|
|
GElf_Sym sym;
|
|
const char *map_name;
|
|
struct bpf_map_def *def;
|
|
|
|
if (!gelf_getsym(symbols, i, &sym))
|
|
continue;
|
|
if (sym.st_shndx != obj->efile.maps_shndx)
|
|
continue;
|
|
|
|
map_name = elf_strptr(obj->efile.elf,
|
|
obj->efile.strtabidx,
|
|
sym.st_name);
|
|
obj->maps[map_idx].offset = sym.st_value;
|
|
if (sym.st_value + sizeof(struct bpf_map_def) > data->d_size) {
|
|
pr_warning("corrupted maps section in %s: last map \"%s\" too small\n",
|
|
obj->path, map_name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
obj->maps[map_idx].name = strdup(map_name);
|
|
if (!obj->maps[map_idx].name) {
|
|
pr_warning("failed to alloc map name\n");
|
|
return -ENOMEM;
|
|
}
|
|
pr_debug("map %d is \"%s\"\n", map_idx,
|
|
obj->maps[map_idx].name);
|
|
def = (struct bpf_map_def *)(data->d_buf + sym.st_value);
|
|
obj->maps[map_idx].def = *def;
|
|
map_idx++;
|
|
}
|
|
|
|
qsort(obj->maps, obj->nr_maps, sizeof(obj->maps[0]), compare_bpf_map);
|
|
return bpf_object__validate_maps(obj);
|
|
}
|
|
|
|
static int bpf_object__elf_collect(struct bpf_object *obj)
|
|
{
|
|
Elf *elf = obj->efile.elf;
|
|
GElf_Ehdr *ep = &obj->efile.ehdr;
|
|
Elf_Scn *scn = NULL;
|
|
int idx = 0, err = 0;
|
|
|
|
/* Elf is corrupted/truncated, avoid calling elf_strptr. */
|
|
if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) {
|
|
pr_warning("failed to get e_shstrndx from %s\n",
|
|
obj->path);
|
|
return -LIBBPF_ERRNO__FORMAT;
|
|
}
|
|
|
|
while ((scn = elf_nextscn(elf, scn)) != NULL) {
|
|
char *name;
|
|
GElf_Shdr sh;
|
|
Elf_Data *data;
|
|
|
|
idx++;
|
|
if (gelf_getshdr(scn, &sh) != &sh) {
|
|
pr_warning("failed to get section header from %s\n",
|
|
obj->path);
|
|
err = -LIBBPF_ERRNO__FORMAT;
|
|
goto out;
|
|
}
|
|
|
|
name = elf_strptr(elf, ep->e_shstrndx, sh.sh_name);
|
|
if (!name) {
|
|
pr_warning("failed to get section name from %s\n",
|
|
obj->path);
|
|
err = -LIBBPF_ERRNO__FORMAT;
|
|
goto out;
|
|
}
|
|
|
|
data = elf_getdata(scn, 0);
|
|
if (!data) {
|
|
pr_warning("failed to get section data from %s(%s)\n",
|
|
name, obj->path);
|
|
err = -LIBBPF_ERRNO__FORMAT;
|
|
goto out;
|
|
}
|
|
pr_debug("section %s, size %ld, link %d, flags %lx, type=%d\n",
|
|
name, (unsigned long)data->d_size,
|
|
(int)sh.sh_link, (unsigned long)sh.sh_flags,
|
|
(int)sh.sh_type);
|
|
|
|
if (strcmp(name, "license") == 0)
|
|
err = bpf_object__init_license(obj,
|
|
data->d_buf,
|
|
data->d_size);
|
|
else if (strcmp(name, "version") == 0)
|
|
err = bpf_object__init_kversion(obj,
|
|
data->d_buf,
|
|
data->d_size);
|
|
else if (strcmp(name, "maps") == 0)
|
|
obj->efile.maps_shndx = idx;
|
|
else if (sh.sh_type == SHT_SYMTAB) {
|
|
if (obj->efile.symbols) {
|
|
pr_warning("bpf: multiple SYMTAB in %s\n",
|
|
obj->path);
|
|
err = -LIBBPF_ERRNO__FORMAT;
|
|
} else {
|
|
obj->efile.symbols = data;
|
|
obj->efile.strtabidx = sh.sh_link;
|
|
}
|
|
} else if ((sh.sh_type == SHT_PROGBITS) &&
|
|
(sh.sh_flags & SHF_EXECINSTR) &&
|
|
(data->d_size > 0)) {
|
|
err = bpf_object__add_program(obj, data->d_buf,
|
|
data->d_size, name, idx);
|
|
if (err) {
|
|
char errmsg[STRERR_BUFSIZE];
|
|
|
|
strerror_r(-err, errmsg, sizeof(errmsg));
|
|
pr_warning("failed to alloc program %s (%s): %s",
|
|
name, obj->path, errmsg);
|
|
}
|
|
} else if (sh.sh_type == SHT_REL) {
|
|
void *reloc = obj->efile.reloc;
|
|
int nr_reloc = obj->efile.nr_reloc + 1;
|
|
|
|
reloc = realloc(reloc,
|
|
sizeof(*obj->efile.reloc) * nr_reloc);
|
|
if (!reloc) {
|
|
pr_warning("realloc failed\n");
|
|
err = -ENOMEM;
|
|
} else {
|
|
int n = nr_reloc - 1;
|
|
|
|
obj->efile.reloc = reloc;
|
|
obj->efile.nr_reloc = nr_reloc;
|
|
|
|
obj->efile.reloc[n].shdr = sh;
|
|
obj->efile.reloc[n].data = data;
|
|
}
|
|
}
|
|
if (err)
|
|
goto out;
|
|
}
|
|
|
|
if (!obj->efile.strtabidx || obj->efile.strtabidx >= idx) {
|
|
pr_warning("Corrupted ELF file: index of strtab invalid\n");
|
|
return LIBBPF_ERRNO__FORMAT;
|
|
}
|
|
if (obj->efile.maps_shndx >= 0)
|
|
err = bpf_object__init_maps(obj);
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static struct bpf_program *
|
|
bpf_object__find_prog_by_idx(struct bpf_object *obj, int idx)
|
|
{
|
|
struct bpf_program *prog;
|
|
size_t i;
|
|
|
|
for (i = 0; i < obj->nr_programs; i++) {
|
|
prog = &obj->programs[i];
|
|
if (prog->idx == idx)
|
|
return prog;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
bpf_program__collect_reloc(struct bpf_program *prog,
|
|
size_t nr_maps, GElf_Shdr *shdr,
|
|
Elf_Data *data, Elf_Data *symbols,
|
|
int maps_shndx, struct bpf_map *maps)
|
|
{
|
|
int i, nrels;
|
|
|
|
pr_debug("collecting relocating info for: '%s'\n",
|
|
prog->section_name);
|
|
nrels = shdr->sh_size / shdr->sh_entsize;
|
|
|
|
prog->reloc_desc = malloc(sizeof(*prog->reloc_desc) * nrels);
|
|
if (!prog->reloc_desc) {
|
|
pr_warning("failed to alloc memory in relocation\n");
|
|
return -ENOMEM;
|
|
}
|
|
prog->nr_reloc = nrels;
|
|
|
|
for (i = 0; i < nrels; i++) {
|
|
GElf_Sym sym;
|
|
GElf_Rel rel;
|
|
unsigned int insn_idx;
|
|
struct bpf_insn *insns = prog->insns;
|
|
size_t map_idx;
|
|
|
|
if (!gelf_getrel(data, i, &rel)) {
|
|
pr_warning("relocation: failed to get %d reloc\n", i);
|
|
return -LIBBPF_ERRNO__FORMAT;
|
|
}
|
|
|
|
if (!gelf_getsym(symbols,
|
|
GELF_R_SYM(rel.r_info),
|
|
&sym)) {
|
|
pr_warning("relocation: symbol %"PRIx64" not found\n",
|
|
GELF_R_SYM(rel.r_info));
|
|
return -LIBBPF_ERRNO__FORMAT;
|
|
}
|
|
|
|
if (sym.st_shndx != maps_shndx) {
|
|
pr_warning("Program '%s' contains non-map related relo data pointing to section %u\n",
|
|
prog->section_name, sym.st_shndx);
|
|
return -LIBBPF_ERRNO__RELOC;
|
|
}
|
|
|
|
insn_idx = rel.r_offset / sizeof(struct bpf_insn);
|
|
pr_debug("relocation: insn_idx=%u\n", insn_idx);
|
|
|
|
if (insns[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) {
|
|
pr_warning("bpf: relocation: invalid relo for insns[%d].code 0x%x\n",
|
|
insn_idx, insns[insn_idx].code);
|
|
return -LIBBPF_ERRNO__RELOC;
|
|
}
|
|
|
|
/* TODO: 'maps' is sorted. We can use bsearch to make it faster. */
|
|
for (map_idx = 0; map_idx < nr_maps; map_idx++) {
|
|
if (maps[map_idx].offset == sym.st_value) {
|
|
pr_debug("relocation: find map %zd (%s) for insn %u\n",
|
|
map_idx, maps[map_idx].name, insn_idx);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (map_idx >= nr_maps) {
|
|
pr_warning("bpf relocation: map_idx %d large than %d\n",
|
|
(int)map_idx, (int)nr_maps - 1);
|
|
return -LIBBPF_ERRNO__RELOC;
|
|
}
|
|
|
|
prog->reloc_desc[i].insn_idx = insn_idx;
|
|
prog->reloc_desc[i].map_idx = map_idx;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
bpf_object__create_maps(struct bpf_object *obj)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < obj->nr_maps; i++) {
|
|
struct bpf_map_def *def = &obj->maps[i].def;
|
|
int *pfd = &obj->maps[i].fd;
|
|
|
|
*pfd = bpf_create_map(def->type,
|
|
def->key_size,
|
|
def->value_size,
|
|
def->max_entries,
|
|
0);
|
|
if (*pfd < 0) {
|
|
size_t j;
|
|
int err = *pfd;
|
|
|
|
pr_warning("failed to create map: %s\n",
|
|
strerror(errno));
|
|
for (j = 0; j < i; j++)
|
|
zclose(obj->maps[j].fd);
|
|
return err;
|
|
}
|
|
pr_debug("create map %s: fd=%d\n", obj->maps[i].name, *pfd);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj)
|
|
{
|
|
int i;
|
|
|
|
if (!prog || !prog->reloc_desc)
|
|
return 0;
|
|
|
|
for (i = 0; i < prog->nr_reloc; i++) {
|
|
int insn_idx, map_idx;
|
|
struct bpf_insn *insns = prog->insns;
|
|
|
|
insn_idx = prog->reloc_desc[i].insn_idx;
|
|
map_idx = prog->reloc_desc[i].map_idx;
|
|
|
|
if (insn_idx >= (int)prog->insns_cnt) {
|
|
pr_warning("relocation out of range: '%s'\n",
|
|
prog->section_name);
|
|
return -LIBBPF_ERRNO__RELOC;
|
|
}
|
|
insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD;
|
|
insns[insn_idx].imm = obj->maps[map_idx].fd;
|
|
}
|
|
|
|
zfree(&prog->reloc_desc);
|
|
prog->nr_reloc = 0;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
bpf_object__relocate(struct bpf_object *obj)
|
|
{
|
|
struct bpf_program *prog;
|
|
size_t i;
|
|
int err;
|
|
|
|
for (i = 0; i < obj->nr_programs; i++) {
|
|
prog = &obj->programs[i];
|
|
|
|
err = bpf_program__relocate(prog, obj);
|
|
if (err) {
|
|
pr_warning("failed to relocate '%s'\n",
|
|
prog->section_name);
|
|
return err;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int bpf_object__collect_reloc(struct bpf_object *obj)
|
|
{
|
|
int i, err;
|
|
|
|
if (!obj_elf_valid(obj)) {
|
|
pr_warning("Internal error: elf object is closed\n");
|
|
return -LIBBPF_ERRNO__INTERNAL;
|
|
}
|
|
|
|
for (i = 0; i < obj->efile.nr_reloc; i++) {
|
|
GElf_Shdr *shdr = &obj->efile.reloc[i].shdr;
|
|
Elf_Data *data = obj->efile.reloc[i].data;
|
|
int idx = shdr->sh_info;
|
|
struct bpf_program *prog;
|
|
size_t nr_maps = obj->nr_maps;
|
|
|
|
if (shdr->sh_type != SHT_REL) {
|
|
pr_warning("internal error at %d\n", __LINE__);
|
|
return -LIBBPF_ERRNO__INTERNAL;
|
|
}
|
|
|
|
prog = bpf_object__find_prog_by_idx(obj, idx);
|
|
if (!prog) {
|
|
pr_warning("relocation failed: no %d section\n",
|
|
idx);
|
|
return -LIBBPF_ERRNO__RELOC;
|
|
}
|
|
|
|
err = bpf_program__collect_reloc(prog, nr_maps,
|
|
shdr, data,
|
|
obj->efile.symbols,
|
|
obj->efile.maps_shndx,
|
|
obj->maps);
|
|
if (err)
|
|
return err;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
load_program(enum bpf_prog_type type, struct bpf_insn *insns,
|
|
int insns_cnt, char *license, u32 kern_version, int *pfd)
|
|
{
|
|
int ret;
|
|
char *log_buf;
|
|
|
|
if (!insns || !insns_cnt)
|
|
return -EINVAL;
|
|
|
|
log_buf = malloc(BPF_LOG_BUF_SIZE);
|
|
if (!log_buf)
|
|
pr_warning("Alloc log buffer for bpf loader error, continue without log\n");
|
|
|
|
ret = bpf_load_program(type, insns, insns_cnt, license,
|
|
kern_version, log_buf, BPF_LOG_BUF_SIZE);
|
|
|
|
if (ret >= 0) {
|
|
*pfd = ret;
|
|
ret = 0;
|
|
goto out;
|
|
}
|
|
|
|
ret = -LIBBPF_ERRNO__LOAD;
|
|
pr_warning("load bpf program failed: %s\n", strerror(errno));
|
|
|
|
if (log_buf && log_buf[0] != '\0') {
|
|
ret = -LIBBPF_ERRNO__VERIFY;
|
|
pr_warning("-- BEGIN DUMP LOG ---\n");
|
|
pr_warning("\n%s\n", log_buf);
|
|
pr_warning("-- END LOG --\n");
|
|
} else if (insns_cnt >= BPF_MAXINSNS) {
|
|
pr_warning("Program too large (%d insns), at most %d insns\n",
|
|
insns_cnt, BPF_MAXINSNS);
|
|
ret = -LIBBPF_ERRNO__PROG2BIG;
|
|
} else {
|
|
/* Wrong program type? */
|
|
if (type != BPF_PROG_TYPE_KPROBE) {
|
|
int fd;
|
|
|
|
fd = bpf_load_program(BPF_PROG_TYPE_KPROBE, insns,
|
|
insns_cnt, license, kern_version,
|
|
NULL, 0);
|
|
if (fd >= 0) {
|
|
close(fd);
|
|
ret = -LIBBPF_ERRNO__PROGTYPE;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (log_buf)
|
|
ret = -LIBBPF_ERRNO__KVER;
|
|
}
|
|
|
|
out:
|
|
free(log_buf);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
bpf_program__load(struct bpf_program *prog,
|
|
char *license, u32 kern_version)
|
|
{
|
|
int err = 0, fd, i;
|
|
|
|
if (prog->instances.nr < 0 || !prog->instances.fds) {
|
|
if (prog->preprocessor) {
|
|
pr_warning("Internal error: can't load program '%s'\n",
|
|
prog->section_name);
|
|
return -LIBBPF_ERRNO__INTERNAL;
|
|
}
|
|
|
|
prog->instances.fds = malloc(sizeof(int));
|
|
if (!prog->instances.fds) {
|
|
pr_warning("Not enough memory for BPF fds\n");
|
|
return -ENOMEM;
|
|
}
|
|
prog->instances.nr = 1;
|
|
prog->instances.fds[0] = -1;
|
|
}
|
|
|
|
if (!prog->preprocessor) {
|
|
if (prog->instances.nr != 1) {
|
|
pr_warning("Program '%s' is inconsistent: nr(%d) != 1\n",
|
|
prog->section_name, prog->instances.nr);
|
|
}
|
|
err = load_program(prog->type, prog->insns, prog->insns_cnt,
|
|
license, kern_version, &fd);
|
|
if (!err)
|
|
prog->instances.fds[0] = fd;
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < prog->instances.nr; i++) {
|
|
struct bpf_prog_prep_result result;
|
|
bpf_program_prep_t preprocessor = prog->preprocessor;
|
|
|
|
bzero(&result, sizeof(result));
|
|
err = preprocessor(prog, i, prog->insns,
|
|
prog->insns_cnt, &result);
|
|
if (err) {
|
|
pr_warning("Preprocessing the %dth instance of program '%s' failed\n",
|
|
i, prog->section_name);
|
|
goto out;
|
|
}
|
|
|
|
if (!result.new_insn_ptr || !result.new_insn_cnt) {
|
|
pr_debug("Skip loading the %dth instance of program '%s'\n",
|
|
i, prog->section_name);
|
|
prog->instances.fds[i] = -1;
|
|
if (result.pfd)
|
|
*result.pfd = -1;
|
|
continue;
|
|
}
|
|
|
|
err = load_program(prog->type, result.new_insn_ptr,
|
|
result.new_insn_cnt,
|
|
license, kern_version, &fd);
|
|
|
|
if (err) {
|
|
pr_warning("Loading the %dth instance of program '%s' failed\n",
|
|
i, prog->section_name);
|
|
goto out;
|
|
}
|
|
|
|
if (result.pfd)
|
|
*result.pfd = fd;
|
|
prog->instances.fds[i] = fd;
|
|
}
|
|
out:
|
|
if (err)
|
|
pr_warning("failed to load program '%s'\n",
|
|
prog->section_name);
|
|
zfree(&prog->insns);
|
|
prog->insns_cnt = 0;
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
bpf_object__load_progs(struct bpf_object *obj)
|
|
{
|
|
size_t i;
|
|
int err;
|
|
|
|
for (i = 0; i < obj->nr_programs; i++) {
|
|
err = bpf_program__load(&obj->programs[i],
|
|
obj->license,
|
|
obj->kern_version);
|
|
if (err)
|
|
return err;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int bpf_object__validate(struct bpf_object *obj)
|
|
{
|
|
if (obj->kern_version == 0) {
|
|
pr_warning("%s doesn't provide kernel version\n",
|
|
obj->path);
|
|
return -LIBBPF_ERRNO__KVERSION;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct bpf_object *
|
|
__bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
|
|
{
|
|
struct bpf_object *obj;
|
|
int err;
|
|
|
|
if (elf_version(EV_CURRENT) == EV_NONE) {
|
|
pr_warning("failed to init libelf for %s\n", path);
|
|
return ERR_PTR(-LIBBPF_ERRNO__LIBELF);
|
|
}
|
|
|
|
obj = bpf_object__new(path, obj_buf, obj_buf_sz);
|
|
if (IS_ERR(obj))
|
|
return obj;
|
|
|
|
CHECK_ERR(bpf_object__elf_init(obj), err, out);
|
|
CHECK_ERR(bpf_object__check_endianness(obj), err, out);
|
|
CHECK_ERR(bpf_object__elf_collect(obj), err, out);
|
|
CHECK_ERR(bpf_object__collect_reloc(obj), err, out);
|
|
CHECK_ERR(bpf_object__validate(obj), err, out);
|
|
|
|
bpf_object__elf_finish(obj);
|
|
return obj;
|
|
out:
|
|
bpf_object__close(obj);
|
|
return ERR_PTR(err);
|
|
}
|
|
|
|
struct bpf_object *bpf_object__open(const char *path)
|
|
{
|
|
/* param validation */
|
|
if (!path)
|
|
return NULL;
|
|
|
|
pr_debug("loading %s\n", path);
|
|
|
|
return __bpf_object__open(path, NULL, 0);
|
|
}
|
|
|
|
struct bpf_object *bpf_object__open_buffer(void *obj_buf,
|
|
size_t obj_buf_sz,
|
|
const char *name)
|
|
{
|
|
char tmp_name[64];
|
|
|
|
/* param validation */
|
|
if (!obj_buf || obj_buf_sz <= 0)
|
|
return NULL;
|
|
|
|
if (!name) {
|
|
snprintf(tmp_name, sizeof(tmp_name), "%lx-%lx",
|
|
(unsigned long)obj_buf,
|
|
(unsigned long)obj_buf_sz);
|
|
tmp_name[sizeof(tmp_name) - 1] = '\0';
|
|
name = tmp_name;
|
|
}
|
|
pr_debug("loading object '%s' from buffer\n",
|
|
name);
|
|
|
|
return __bpf_object__open(name, obj_buf, obj_buf_sz);
|
|
}
|
|
|
|
int bpf_object__unload(struct bpf_object *obj)
|
|
{
|
|
size_t i;
|
|
|
|
if (!obj)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < obj->nr_maps; i++)
|
|
zclose(obj->maps[i].fd);
|
|
|
|
for (i = 0; i < obj->nr_programs; i++)
|
|
bpf_program__unload(&obj->programs[i]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bpf_object__load(struct bpf_object *obj)
|
|
{
|
|
int err;
|
|
|
|
if (!obj)
|
|
return -EINVAL;
|
|
|
|
if (obj->loaded) {
|
|
pr_warning("object should not be loaded twice\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
obj->loaded = true;
|
|
|
|
CHECK_ERR(bpf_object__create_maps(obj), err, out);
|
|
CHECK_ERR(bpf_object__relocate(obj), err, out);
|
|
CHECK_ERR(bpf_object__load_progs(obj), err, out);
|
|
|
|
return 0;
|
|
out:
|
|
bpf_object__unload(obj);
|
|
pr_warning("failed to load object '%s'\n", obj->path);
|
|
return err;
|
|
}
|
|
|
|
void bpf_object__close(struct bpf_object *obj)
|
|
{
|
|
size_t i;
|
|
|
|
if (!obj)
|
|
return;
|
|
|
|
if (obj->clear_priv)
|
|
obj->clear_priv(obj, obj->priv);
|
|
|
|
bpf_object__elf_finish(obj);
|
|
bpf_object__unload(obj);
|
|
|
|
for (i = 0; i < obj->nr_maps; i++) {
|
|
zfree(&obj->maps[i].name);
|
|
if (obj->maps[i].clear_priv)
|
|
obj->maps[i].clear_priv(&obj->maps[i],
|
|
obj->maps[i].priv);
|
|
obj->maps[i].priv = NULL;
|
|
obj->maps[i].clear_priv = NULL;
|
|
}
|
|
zfree(&obj->maps);
|
|
obj->nr_maps = 0;
|
|
|
|
if (obj->programs && obj->nr_programs) {
|
|
for (i = 0; i < obj->nr_programs; i++)
|
|
bpf_program__exit(&obj->programs[i]);
|
|
}
|
|
zfree(&obj->programs);
|
|
|
|
list_del(&obj->list);
|
|
free(obj);
|
|
}
|
|
|
|
struct bpf_object *
|
|
bpf_object__next(struct bpf_object *prev)
|
|
{
|
|
struct bpf_object *next;
|
|
|
|
if (!prev)
|
|
next = list_first_entry(&bpf_objects_list,
|
|
struct bpf_object,
|
|
list);
|
|
else
|
|
next = list_next_entry(prev, list);
|
|
|
|
/* Empty list is noticed here so don't need checking on entry. */
|
|
if (&next->list == &bpf_objects_list)
|
|
return NULL;
|
|
|
|
return next;
|
|
}
|
|
|
|
const char *bpf_object__name(struct bpf_object *obj)
|
|
{
|
|
return obj ? obj->path : ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
unsigned int bpf_object__kversion(struct bpf_object *obj)
|
|
{
|
|
return obj ? obj->kern_version : 0;
|
|
}
|
|
|
|
int bpf_object__set_priv(struct bpf_object *obj, void *priv,
|
|
bpf_object_clear_priv_t clear_priv)
|
|
{
|
|
if (obj->priv && obj->clear_priv)
|
|
obj->clear_priv(obj, obj->priv);
|
|
|
|
obj->priv = priv;
|
|
obj->clear_priv = clear_priv;
|
|
return 0;
|
|
}
|
|
|
|
void *bpf_object__priv(struct bpf_object *obj)
|
|
{
|
|
return obj ? obj->priv : ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
struct bpf_program *
|
|
bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
|
|
{
|
|
size_t idx;
|
|
|
|
if (!obj->programs)
|
|
return NULL;
|
|
/* First handler */
|
|
if (prev == NULL)
|
|
return &obj->programs[0];
|
|
|
|
if (prev->obj != obj) {
|
|
pr_warning("error: program handler doesn't match object\n");
|
|
return NULL;
|
|
}
|
|
|
|
idx = (prev - obj->programs) + 1;
|
|
if (idx >= obj->nr_programs)
|
|
return NULL;
|
|
return &obj->programs[idx];
|
|
}
|
|
|
|
int bpf_program__set_priv(struct bpf_program *prog, void *priv,
|
|
bpf_program_clear_priv_t clear_priv)
|
|
{
|
|
if (prog->priv && prog->clear_priv)
|
|
prog->clear_priv(prog, prog->priv);
|
|
|
|
prog->priv = priv;
|
|
prog->clear_priv = clear_priv;
|
|
return 0;
|
|
}
|
|
|
|
void *bpf_program__priv(struct bpf_program *prog)
|
|
{
|
|
return prog ? prog->priv : ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
const char *bpf_program__title(struct bpf_program *prog, bool needs_copy)
|
|
{
|
|
const char *title;
|
|
|
|
title = prog->section_name;
|
|
if (needs_copy) {
|
|
title = strdup(title);
|
|
if (!title) {
|
|
pr_warning("failed to strdup program title\n");
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
}
|
|
|
|
return title;
|
|
}
|
|
|
|
int bpf_program__fd(struct bpf_program *prog)
|
|
{
|
|
return bpf_program__nth_fd(prog, 0);
|
|
}
|
|
|
|
int bpf_program__set_prep(struct bpf_program *prog, int nr_instances,
|
|
bpf_program_prep_t prep)
|
|
{
|
|
int *instances_fds;
|
|
|
|
if (nr_instances <= 0 || !prep)
|
|
return -EINVAL;
|
|
|
|
if (prog->instances.nr > 0 || prog->instances.fds) {
|
|
pr_warning("Can't set pre-processor after loading\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
instances_fds = malloc(sizeof(int) * nr_instances);
|
|
if (!instances_fds) {
|
|
pr_warning("alloc memory failed for fds\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* fill all fd with -1 */
|
|
memset(instances_fds, -1, sizeof(int) * nr_instances);
|
|
|
|
prog->instances.nr = nr_instances;
|
|
prog->instances.fds = instances_fds;
|
|
prog->preprocessor = prep;
|
|
return 0;
|
|
}
|
|
|
|
int bpf_program__nth_fd(struct bpf_program *prog, int n)
|
|
{
|
|
int fd;
|
|
|
|
if (n >= prog->instances.nr || n < 0) {
|
|
pr_warning("Can't get the %dth fd from program %s: only %d instances\n",
|
|
n, prog->section_name, prog->instances.nr);
|
|
return -EINVAL;
|
|
}
|
|
|
|
fd = prog->instances.fds[n];
|
|
if (fd < 0) {
|
|
pr_warning("%dth instance of program '%s' is invalid\n",
|
|
n, prog->section_name);
|
|
return -ENOENT;
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
|
|
static void bpf_program__set_type(struct bpf_program *prog,
|
|
enum bpf_prog_type type)
|
|
{
|
|
prog->type = type;
|
|
}
|
|
|
|
int bpf_program__set_tracepoint(struct bpf_program *prog)
|
|
{
|
|
if (!prog)
|
|
return -EINVAL;
|
|
bpf_program__set_type(prog, BPF_PROG_TYPE_TRACEPOINT);
|
|
return 0;
|
|
}
|
|
|
|
int bpf_program__set_kprobe(struct bpf_program *prog)
|
|
{
|
|
if (!prog)
|
|
return -EINVAL;
|
|
bpf_program__set_type(prog, BPF_PROG_TYPE_KPROBE);
|
|
return 0;
|
|
}
|
|
|
|
static bool bpf_program__is_type(struct bpf_program *prog,
|
|
enum bpf_prog_type type)
|
|
{
|
|
return prog ? (prog->type == type) : false;
|
|
}
|
|
|
|
bool bpf_program__is_tracepoint(struct bpf_program *prog)
|
|
{
|
|
return bpf_program__is_type(prog, BPF_PROG_TYPE_TRACEPOINT);
|
|
}
|
|
|
|
bool bpf_program__is_kprobe(struct bpf_program *prog)
|
|
{
|
|
return bpf_program__is_type(prog, BPF_PROG_TYPE_KPROBE);
|
|
}
|
|
|
|
int bpf_map__fd(struct bpf_map *map)
|
|
{
|
|
return map ? map->fd : -EINVAL;
|
|
}
|
|
|
|
const struct bpf_map_def *bpf_map__def(struct bpf_map *map)
|
|
{
|
|
return map ? &map->def : ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
const char *bpf_map__name(struct bpf_map *map)
|
|
{
|
|
return map ? map->name : NULL;
|
|
}
|
|
|
|
int bpf_map__set_priv(struct bpf_map *map, void *priv,
|
|
bpf_map_clear_priv_t clear_priv)
|
|
{
|
|
if (!map)
|
|
return -EINVAL;
|
|
|
|
if (map->priv) {
|
|
if (map->clear_priv)
|
|
map->clear_priv(map, map->priv);
|
|
}
|
|
|
|
map->priv = priv;
|
|
map->clear_priv = clear_priv;
|
|
return 0;
|
|
}
|
|
|
|
void *bpf_map__priv(struct bpf_map *map)
|
|
{
|
|
return map ? map->priv : ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
struct bpf_map *
|
|
bpf_map__next(struct bpf_map *prev, struct bpf_object *obj)
|
|
{
|
|
size_t idx;
|
|
struct bpf_map *s, *e;
|
|
|
|
if (!obj || !obj->maps)
|
|
return NULL;
|
|
|
|
s = obj->maps;
|
|
e = obj->maps + obj->nr_maps;
|
|
|
|
if (prev == NULL)
|
|
return s;
|
|
|
|
if ((prev < s) || (prev >= e)) {
|
|
pr_warning("error in %s: map handler doesn't belong to object\n",
|
|
__func__);
|
|
return NULL;
|
|
}
|
|
|
|
idx = (prev - obj->maps) + 1;
|
|
if (idx >= obj->nr_maps)
|
|
return NULL;
|
|
return &obj->maps[idx];
|
|
}
|
|
|
|
struct bpf_map *
|
|
bpf_object__find_map_by_name(struct bpf_object *obj, const char *name)
|
|
{
|
|
struct bpf_map *pos;
|
|
|
|
bpf_map__for_each(pos, obj) {
|
|
if (pos->name && !strcmp(pos->name, name))
|
|
return pos;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
struct bpf_map *
|
|
bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < obj->nr_maps; i++) {
|
|
if (obj->maps[i].offset == offset)
|
|
return &obj->maps[i];
|
|
}
|
|
return ERR_PTR(-ENOENT);
|
|
}
|