[RFC PATCH v4 06/29] bpf tools: Open eBPF object file and do basic validation

From: Wang Nan
Date: Wed May 27 2015 - 01:24:40 EST


This patch defines basic interface of libbpf. 'struct bpf_object' will
be the handler of each object file. Its internal structure is hide to
user. 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 efile field of
'struct bpf_object'. bpf_object__elf_finish() is introduced to clear it.

After all eBPF programs in an object file are loaded, related ELF
information is useless. Close the object file and free those memory.

zfree() and zclose() are introduced to ensure setting NULL pointers and
negative file descriptors after resources are released.

Signed-off-by: Wang Nan <wangnan0@xxxxxxxxxx>
---
tools/lib/bpf/libbpf.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 8 +++
2 files changed, 160 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 49091c3..e910fb8 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -11,8 +11,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"

@@ -55,3 +59,151 @@ void libbpf_set_print(int (*warn)(const char *format, ...),
__pr_info = info;
__pr_debug = debug;
}
+
+/* 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
+
+struct bpf_object {
+ /*
+ * Information when doing elf related work. Only valid if fd
+ * is valid.
+ */
+ struct {
+ int fd;
+ Elf *elf;
+ GElf_Ehdr ehdr;
+ } efile;
+ char path[];
+};
+#define obj_elf_valid(o) ((o)->efile.fd >= 0)
+
+static struct bpf_object *bpf_object__new(const char *path)
+{
+ 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 NULL;
+ }
+
+ strcpy(obj->path, path);
+ obj->efile.fd = -1;
+ 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;
+ }
+ zclose(obj->efile.fd);
+}
+
+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 -EEXIST;
+ }
+
+ 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 = -EINVAL;
+ goto errout;
+ }
+
+ if (!gelf_getehdr(obj->efile.elf, &obj->efile.ehdr)) {
+ pr_warning("failed to get EHDR from %s\n",
+ obj->path);
+ err = -EINVAL;
+ goto errout;
+ }
+ ep = &obj->efile.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_object__elf_finish(obj);
+ return err;
+}
+
+struct bpf_object *bpf_object__open(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_object__new(path);
+ if (!obj)
+ return NULL;
+
+ if (bpf_object__elf_init(obj))
+ goto out;
+
+ bpf_object__elf_finish(obj);
+ return obj;
+out:
+ bpf_object__close(obj);
+ return NULL;
+}
+
+void bpf_object__close(struct bpf_object *obj)
+{
+ if (!obj)
+ return;
+
+ bpf_object__elf_finish(obj);
+
+ free(obj);
+}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 430b122..88d62de 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -8,8 +8,16 @@
#ifndef __BPF_LIBBPF_H
#define __BPF_LIBBPF_H

+#include <stdio.h>
+
void libbpf_set_print(int (*warn)(const char *format, ...),
int (*info)(const char *format, ...),
int (*debug)(const char *format, ...));

+/* Hide internal to user */
+struct bpf_object;
+
+struct bpf_object *bpf_object__open(const char *path);
+void bpf_object__close(struct bpf_object *object);
+
#endif
--
1.8.3.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/