forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
simple .o parser and loader using BPF syscall. .o is a standard ELF generated by LLVM backend It parses elf file compiled by llvm .c->.o - parses 'maps' section and creates maps via BPF syscall - parses 'license' section and passes it to syscall - parses elf relocations for BPF maps and adjusts BPF_LD_IMM64 insns by storing map_fd into insn->imm and marking such insns as BPF_PSEUDO_MAP_FD - loads eBPF programs via BPF syscall One ELF file can contain multiple BPF programs. int load_bpf_file(char *path); populates prog_fd[] and map_fd[] with FDs received from bpf syscall bpf_helpers.h - helper functions available to eBPF programs written in C Signed-off-by: Alexei Starovoitov <[email protected]> Signed-off-by: David S. Miller <[email protected]>
- Loading branch information
Showing
3 changed files
with
267 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
#ifndef __BPF_HELPERS_H | ||
#define __BPF_HELPERS_H | ||
|
||
/* helper macro to place programs, maps, license in | ||
* different sections in elf_bpf file. Section names | ||
* are interpreted by elf_bpf loader | ||
*/ | ||
#define SEC(NAME) __attribute__((section(NAME), used)) | ||
|
||
/* helper functions called from eBPF programs written in C */ | ||
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, | ||
unsigned long long flags) = | ||
(void *) BPF_FUNC_map_update_elem; | ||
static int (*bpf_map_delete_elem)(void *map, void *key) = | ||
(void *) BPF_FUNC_map_delete_elem; | ||
|
||
/* llvm builtin functions that eBPF C program may use to | ||
* emit BPF_LD_ABS and BPF_LD_IND instructions | ||
*/ | ||
struct sk_buff; | ||
unsigned long long load_byte(void *skb, | ||
unsigned long long off) asm("llvm.bpf.load.byte"); | ||
unsigned long long load_half(void *skb, | ||
unsigned long long off) asm("llvm.bpf.load.half"); | ||
unsigned long long load_word(void *skb, | ||
unsigned long long off) asm("llvm.bpf.load.word"); | ||
|
||
/* a helper structure used by eBPF C program | ||
* to describe map attributes to elf_bpf loader | ||
*/ | ||
struct bpf_map_def { | ||
unsigned int type; | ||
unsigned int key_size; | ||
unsigned int value_size; | ||
unsigned int max_entries; | ||
}; | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
#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 <linux/filter.h> | ||
#include "libbpf.h" | ||
#include "bpf_helpers.h" | ||
#include "bpf_load.h" | ||
|
||
static char license[128]; | ||
static bool processed_sec[128]; | ||
int map_fd[MAX_MAPS]; | ||
int prog_fd[MAX_PROGS]; | ||
int prog_cnt; | ||
|
||
static int load_and_attach(const char *event, struct bpf_insn *prog, int size) | ||
{ | ||
int fd; | ||
bool is_socket = strncmp(event, "socket", 6) == 0; | ||
|
||
if (!is_socket) | ||
/* tracing events tbd */ | ||
return -1; | ||
|
||
fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, | ||
prog, size, license); | ||
|
||
if (fd < 0) { | ||
printf("bpf_prog_load() err=%d\n%s", errno, bpf_log_buf); | ||
return -1; | ||
} | ||
|
||
prog_fd[prog_cnt++] = fd; | ||
|
||
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 int parse_relo_and_apply(Elf_Data *data, Elf_Data *symbols, | ||
GElf_Shdr *shdr, struct bpf_insn *insn) | ||
{ | ||
int i, nrels; | ||
|
||
nrels = shdr->sh_size / shdr->sh_entsize; | ||
|
||
for (i = 0; i < nrels; i++) { | ||
GElf_Sym sym; | ||
GElf_Rel rel; | ||
unsigned int insn_idx; | ||
|
||
gelf_getrel(data, i, &rel); | ||
|
||
insn_idx = rel.r_offset / sizeof(struct bpf_insn); | ||
|
||
gelf_getsym(symbols, GELF_R_SYM(rel.r_info), &sym); | ||
|
||
if (insn[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) { | ||
printf("invalid relo for insn[%d].code 0x%x\n", | ||
insn_idx, insn[insn_idx].code); | ||
return 1; | ||
} | ||
insn[insn_idx].src_reg = BPF_PSEUDO_MAP_FD; | ||
insn[insn_idx].imm = map_fd[sym.st_value / sizeof(struct bpf_map_def)]; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
int load_bpf_file(char *path) | ||
{ | ||
int fd, i; | ||
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) /* helpful for llvm debugging */ | ||
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) { | ||
struct bpf_insn *insns; | ||
|
||
if (get_sec(elf, shdr.sh_info, &ehdr, &shname_prog, | ||
&shdr_prog, &data_prog)) | ||
continue; | ||
|
||
insns = (struct bpf_insn *) data_prog->d_buf; | ||
|
||
processed_sec[shdr.sh_info] = true; | ||
processed_sec[i] = true; | ||
|
||
if (parse_relo_and_apply(data, symbols, &shdr, insns)) | ||
continue; | ||
|
||
if (memcmp(shname_prog, "events/", 7) == 0 || | ||
memcmp(shname_prog, "socket", 6) == 0) | ||
load_and_attach(shname_prog, insns, data_prog->d_size); | ||
} | ||
} | ||
|
||
/* 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/", 7) == 0 || | ||
memcmp(shname, "socket", 6) == 0) | ||
load_and_attach(shname, data->d_buf, data->d_size); | ||
} | ||
|
||
close(fd); | ||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
#ifndef __BPF_LOAD_H | ||
#define __BPF_LOAD_H | ||
|
||
#define MAX_MAPS 32 | ||
#define MAX_PROGS 32 | ||
|
||
extern int map_fd[MAX_MAPS]; | ||
extern int prog_fd[MAX_PROGS]; | ||
|
||
/* parses elf file compiled by llvm .c->.o | ||
* . parses 'maps' section and creates maps via BPF syscall | ||
* . parses 'license' section and passes it to syscall | ||
* . parses elf relocations for BPF maps and adjusts BPF_LD_IMM64 insns by | ||
* storing map_fd into insn->imm and marking such insns as BPF_PSEUDO_MAP_FD | ||
* . loads eBPF programs via BPF syscall | ||
* | ||
* One ELF file can contain multiple BPF programs which will be loaded | ||
* and their FDs stored stored in prog_fd array | ||
* | ||
* returns zero on success | ||
*/ | ||
int load_bpf_file(char *path); | ||
|
||
#endif |