[RFC PATCH v3 09/37] bpf tools: Open eBPF object file and do basic validation

Wang Nan
2015-05-17T11:05:00Z

This patch adds basic 'struct bpf_object' which will be used for eBPF
object file loading. eBPF object files are compiled by LLVM as ELF
format. In this patch, libelf is used to open those files, read EHDR
and do basic validation according to e_type and e_machine.

All elf related staffs are grouped together and reside in elf field of
'struct bpf_object'. bpf_obj_clear_elf() is introduced to clear it.

Signed-off-by: Wang Nan <wangnan0@huawei.com>
---
tools/lib/bpf/libbpf.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 154 insertions(+), 2 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index f8decff..43c16bc 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -12,9 +12,12 @@
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
+#include <fcntl.h>
#include <errno.h>
#include <asm/unistd.h>
#include <linux/bpf.h>
+#include <libelf.h>
+#include <gelf.h>

#include "libbpf.h"

@@ -58,12 +61,161 @@ void libbpf_set_print(int (*warn)(const char *format, ...),
__pr_debug = debug;
}

-struct bpf_object *bpf_open_object(const char *path)
+#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
+
+struct bpf_object {
+ char *path;
+
+ /*
+ * Information when doing elf related work. Only valid if fd
+ * is valid.
+ */
+ struct {
+ int fd;
+ Elf *elf;
+ GElf_Ehdr ehdr;
+ } elf;
+};
+#define obj_elf_valid(o) ((o)->elf.fd >= 0)
+
+static struct bpf_object *__bpf_obj_alloc(const char *path)
+{
+ struct bpf_object *obj;
+
+ obj = calloc(1, sizeof(struct bpf_object));
+ if (!obj) {
+ pr_warning("alloc memory failed for %s\n", path);
+ return NULL;
+ }
+
+ obj->path = strdup(path);
+ if (!obj->path) {
+ pr_warning("failed to strdup '%s'\n", path);
+ free(obj);
+ return NULL;
+ }
+ return obj;
+}
+
+static struct bpf_object *bpf_obj_alloc(const char *path)
{
+ struct bpf_object *obj;
+
+ obj = __bpf_obj_alloc(path);
+ if (!obj)
+ goto out;
+
+ obj->elf.fd = -1;
+ return obj;
+out:
+ bpf_close_object(obj);
return NULL;
}

-void bpf_close_object(struct bpf_object *object)
+static void bpf_obj_clear_elf(struct bpf_object *obj)
+{
+ if (!obj_elf_valid(obj))
+ return;
+
+ if (obj->elf.elf) {
+ elf_end(obj->elf.elf);
+ obj->elf.elf = NULL;
+ }
+ if (obj->elf.fd >= 0) {
+ close(obj->elf.fd);
+ obj->elf.fd = -1;
+ }
+}
+
+static int bpf_obj_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 -EEXIST;
+ }
+
+ obj->elf.fd = open(obj->path, O_RDONLY);
+ if (obj->elf.fd < 0) {
+ pr_warning("failed to open %s: %s\n", obj->path,
+ strerror(errno));
+ return -errno;
+ }
+
+ obj->elf.elf = elf_begin(obj->elf.fd,
+ LIBBPF_ELF_C_READ_MMAP,
+ NULL);
+ if (!obj->elf.elf) {
+ pr_warning("failed to open %s as ELF file\n",
+ obj->path);
+ err = -EINVAL;
+ goto errout;
+ }
+
+ if (!gelf_getehdr(obj->elf.elf, &obj->elf.ehdr)) {
+ pr_warning("failed to get EHDR from %s\n",
+ obj->path);
+ err = -EINVAL;
+ goto errout;
+ }
+ ep = &obj->elf.ehdr;
+
+ if ((ep->e_type != ET_REL) || (ep->e_machine != 0)) {
+ pr_warning("%s is not an eBPF object file\n",
+ obj->path);
+ err = -EINVAL;
+ goto errout;
+ }
+
return 0;
+errout:
+ bpf_obj_clear_elf(obj);
+ return err;
+}
+
+struct bpf_object *bpf_open_object(const char *path)
+{
+ struct bpf_object *obj;
+
+ /* param validation */
+ if (!path)
+ return NULL;
+
+ pr_debug("loading %s\n", path);
+
+ if (elf_version(EV_CURRENT) == EV_NONE) {
+ pr_warning("failed to init libelf for %s\n", path);
+ return NULL;
+ }
+
+ obj = bpf_obj_alloc(path);
+ if (!obj)
+ return NULL;
+
+ if (bpf_obj_elf_init(obj))
+ goto out;
+
+ return obj;
+
+out:
+ bpf_close_object(obj);
+ return NULL;
+}
+
+void bpf_close_object(struct bpf_object *obj)
+{
+ if (!obj)
+ return;
+
+ bpf_obj_clear_elf(obj);
+
+ if (obj->path)
+ free(obj->path);
+ free(obj);
}
--
1.8.3.4