simple .o parser and loader using BPF syscall. .o is a standard ELF generated by LLVM backend /* parses elf file compiled by llvm .c->.o * creates maps and stores FD into map_fd array * loads eBPF programs * returns zero on success */ int load_bpf_file(char *path); Signed-off-by: Alexei Starovoitov <ast@xxxxxxxxxxxx> --- samples/bpf/bpf_helpers.h | 21 +++++ samples/bpf/bpf_load.c | 228 +++++++++++++++++++++++++++++++++++++++++++++ samples/bpf/bpf_load.h | 18 ++++ 3 files changed, 267 insertions(+) create mode 100644 samples/bpf/bpf_helpers.h create mode 100644 samples/bpf/bpf_load.c create mode 100644 samples/bpf/bpf_load.h diff --git a/samples/bpf/bpf_helpers.h b/samples/bpf/bpf_helpers.h new file mode 100644 index 000000000000..356e7d34d174 --- /dev/null +++ b/samples/bpf/bpf_helpers.h @@ -0,0 +1,21 @@ +#ifndef __BPF_HELPERS_H +#define __BPF_HELPERS_H + +#define SEC(NAME) __attribute__((section(NAME), used)) + +static void *(*bpf_load_pointer)(void *unsafe_ptr) = (void *) BPF_FUNC_load_pointer; +static int (*bpf_printk)(const char *fmt, int fmt_size, ...) = (void *) BPF_FUNC_printk; +static int (*bpf_memcmp)(void *unsafe_ptr, void *safe_ptr, int size) = (void *) BPF_FUNC_memcmp; +static void *(*bpf_map_lookup_elem)(void *map, void *key) = (void*) BPF_FUNC_map_lookup_elem; +static int (*bpf_map_update_elem)(void *map, void *key, void *value) = (void*) BPF_FUNC_map_update_elem; +static int (*bpf_map_delete_elem)(void *map, void *key) = (void *) BPF_FUNC_map_delete_elem; +static void (*bpf_dump_stack)(void) = (void *) BPF_FUNC_dump_stack; + +struct bpf_map_def { + int type; + int key_size; + int value_size; + int max_entries; +}; + +#endif diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c new file mode 100644 index 000000000000..7614f19d5282 --- /dev/null +++ b/samples/bpf/bpf_load.c @@ -0,0 +1,228 @@ +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <libelf.h> +#include <gelf.h> +#include <errno.h> +#include <unistd.h> +#include <string.h> +#include <stdbool.h> +#include <linux/bpf.h> +#include "libbpf.h" +#include "bpf_helpers.h" +#include "bpf_load.h" + +#define DEBUGFS "/sys/kernel/debug/tracing/" + +static char license[128]; +static bool processed_sec[128]; +int map_fd[MAX_MAPS]; + +static int load_and_attach(const char *event, char *prog, int size, + const struct bpf_map_fixup *fixups, int fixup_len) +{ + int fd, event_fd, err; + char fmt[32]; + char path[256] = DEBUGFS; + + fd = bpf_prog_load(BPF_PROG_TYPE_TRACING_FILTER, + (struct bpf_insn *)prog, size, license, + fixups, fixup_len); + + if (fd < 0) { + printf("err %d errno %d\n", fd, errno); + return fd; + } + + + snprintf(fmt, sizeof(fmt), "bpf-%d", fd); + + strcat(path, event); + strcat(path, "/filter"); + + printf("writing %s -> %s\n", fmt, path); + + event_fd = open(path, O_WRONLY, 0); + if (event_fd < 0) { + printf("failed to open event %s\n", event); + return event_fd; + } + + err = write(event_fd, fmt, strlen(fmt)); + (void) err; + + return 0; +} + +static int load_maps(struct bpf_map_def *maps, int len) +{ + int i; + + for (i = 0; i < len / sizeof(struct bpf_map_def); i++) { + + map_fd[i] = bpf_create_map(maps[i].type, + maps[i].key_size, + maps[i].value_size, + maps[i].max_entries); + if (map_fd[i] < 0) + return 1; + } + return 0; +} + +static int get_sec(Elf *elf, int i, GElf_Ehdr *ehdr, char **shname, + GElf_Shdr *shdr, Elf_Data **data) +{ + Elf_Scn *scn; + + scn = elf_getscn(elf, i); + if (!scn) + return 1; + + if (gelf_getshdr(scn, shdr) != shdr) + return 2; + + *shname = elf_strptr(elf, ehdr->e_shstrndx, shdr->sh_name); + if (!*shname || !shdr->sh_size) + return 3; + + *data = elf_getdata(scn, 0); + if (!*data || elf_getdata(scn, *data) != NULL) + return 4; + + return 0; +} + +static struct bpf_map_fixup fixup[128]; + +static int parse_relo_into_map_fixup(Elf_Data *data, Elf_Data *symbols, + GElf_Shdr *shdr) +{ + GElf_Sym sym; + GElf_Rel rel; + int i, nrels; + + nrels = shdr->sh_size / shdr->sh_entsize; + + for (i = 0; i < nrels; i++) { + gelf_getrel(data, i, &rel); + + fixup[i].insn_idx = rel.r_offset / sizeof(struct bpf_insn); + + gelf_getsym(symbols, GELF_R_SYM(rel.r_info), &sym); + + fixup[i].fd = map_fd[sym.st_value / sizeof(struct bpf_map_def)]; + } + + return nrels; +} + +int load_bpf_file(char *path) +{ + int fd, i, nrels; + Elf *elf; + GElf_Ehdr ehdr; + GElf_Shdr shdr, shdr_prog; + Elf_Data *data, *data_prog, *symbols = NULL; + char *shname, *shname_prog; + + if (elf_version(EV_CURRENT) == EV_NONE) + return 1; + + fd = open(path, O_RDONLY, 0); + if (fd < 0) + return 1; + + elf = elf_begin(fd, ELF_C_READ, NULL); + + if (!elf) + return 1; + + if (gelf_getehdr(elf, &ehdr) != &ehdr) + return 1; + + /* scan over all elf sections to get license and map info */ + for (i = 1; i < ehdr.e_shnum; i++) { + + if (get_sec(elf, i, &ehdr, &shname, &shdr, &data)) + continue; + + if (0) + printf("section %d:%s data %p size %zd link %d flags %d\n", + i, shname, data->d_buf, data->d_size, + shdr.sh_link, (int) shdr.sh_flags); + + if (strcmp(shname, "license") == 0) { + processed_sec[i] = true; + memcpy(license, data->d_buf, data->d_size); + } else if (strcmp(shname, "maps") == 0) { + processed_sec[i] = true; + if (load_maps(data->d_buf, data->d_size)) + return 1; + } else if (shdr.sh_type == SHT_SYMTAB) { + symbols = data; + } + } + + /* load programs that need map fixup (relocations) */ + for (i = 1; i < ehdr.e_shnum; i++) { + + if (get_sec(elf, i, &ehdr, &shname, &shdr, &data)) + continue; + if (shdr.sh_type == SHT_REL) { + + if (get_sec(elf, shdr.sh_info, &ehdr, &shname_prog, + &shdr_prog, &data_prog)) + continue; + + if (0) + printf("relo %s into %s\n", shname, shname_prog); + + processed_sec[shdr.sh_info] = true; + processed_sec[i] = true; + + nrels = parse_relo_into_map_fixup(data, symbols, &shdr); + + if (memcmp(shname_prog, "events/", sizeof("events/") - 1) == 0) + load_and_attach(shname_prog, + data_prog->d_buf, data_prog->d_size, + fixup, nrels * sizeof(fixup[0])); + } + } + + /* load programs that don't use maps */ + for (i = 1; i < ehdr.e_shnum; i++) { + + if (processed_sec[i]) + continue; + + if (get_sec(elf, i, &ehdr, &shname, &shdr, &data)) + continue; + + if (memcmp(shname, "events/", sizeof("events/") - 1) == 0) + load_and_attach(shname, data->d_buf, data->d_size, + NULL, 0); + } + + close(fd); + return 0; +} + +void read_trace_pipe(void) +{ + int trace_fd; + + trace_fd = open(DEBUGFS "trace_pipe", O_RDONLY, 0); + if (trace_fd < 0) + return; + + while (1) { + static char buf[4096]; + ssize_t sz; + + sz = read(trace_fd, buf, sizeof(buf)); + if (sz) + puts(buf); + } +} diff --git a/samples/bpf/bpf_load.h b/samples/bpf/bpf_load.h new file mode 100644 index 000000000000..d34e61a101d2 --- /dev/null +++ b/samples/bpf/bpf_load.h @@ -0,0 +1,18 @@ +#ifndef __BPF_LOAD_H +#define __BPF_LOAD_H + +#define MAX_MAPS 64 + +extern int map_fd[MAX_MAPS]; + +/* parses elf file compiled by llvm .c->.o + * creates maps and stores FD into map_fd array + * loads eBPF programs + * returns zero on success + */ +int load_bpf_file(char *path); + +/* forever reads /sys/.../trace_pipe */ +void read_trace_pipe(void); + +#endif -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-api" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html