[RFC PATCH 05/22] perf bpf: open eBPF object file and do basic validation.

Wang Nan
2015-04-30T11:02:33Z

This patch adds basic 'struct bpf_obj' which will be used for eBPF
object files 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
'struct bpf_obj'. bpf_obj_clear_elf() is introduced to clear it.

Signed-off-by: Wang Nan <wangnan0@huawei.com>
---
tools/perf/util/bpf-loader.c | 133 +++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/bpf-loader.h | 16 ++++++
2 files changed, 149 insertions(+)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 84d3cc3..3eb7504 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -20,10 +20,143 @@
#include <linux/types.h>
#include <linux/bpf.h>

+static LIST_HEAD(bpf_obj_list);
+
+static struct bpf_obj *__bpf_obj_alloc(const char *path)
+{
+ struct bpf_obj *obj;
+
+ obj = calloc(1, sizeof(struct bpf_obj));
+ if (!obj) {
+ pr_err("bpf: alloc memory failed for %s\n", path);
+ return NULL;
+ }
+
+ obj->path = strdup(path);
+ if (!obj->path) {
+ pr_err("bpf: failed to strdup '%s'\n", path);
+ free(obj);
+ return NULL;
+ }
+ return obj;
+}
+
+static void bpf_obj_clear_elf(struct bpf_obj *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 void bpf_obj_close(struct bpf_obj *obj)
+{
+ if (!obj)
+ return;
+
+ bpf_obj_clear_elf(obj);
+
+ if (obj->path)
+ free(obj->path);
+ free(obj);
+}
+
+static struct bpf_obj *bpf_obj_alloc(const char *path)
+{
+ struct bpf_obj *obj;
+
+ obj = __bpf_obj_alloc(path);
+ if (!obj)
+ goto out;
+
+ obj->elf.fd = -1;
+ return obj;
+out:
+ bpf_obj_close(obj);
+ return NULL;
+}
+
+static int bpf_obj_elf_init(struct bpf_obj *obj)
+{
+ int err = 0;
+ GElf_Ehdr *ep;
+
+ if (obj_elf_valid(obj)) {
+ pr_err("bpf: elf init: internal error\n");
+ return -EEXIST;
+ }
+
+ obj->elf.fd = open(obj->path, O_RDONLY);
+ if (obj->elf.fd < 0) {
+ pr_err("bpf: failed to open %s: %s\n", obj->path,
+ strerror(errno));
+ return -errno;
+ }
+
+ obj->elf.elf = elf_begin(obj->elf.fd,
+ PERF_ELF_C_READ_MMAP,
+ NULL);
+ if (!obj->elf.elf) {
+ pr_err("bpf: failed to open %s as ELF file\n",
+ obj->path);
+ err = -EINVAL;
+ goto errout;
+ }
+
+ if (!gelf_getehdr(obj->elf.elf, &obj->elf.ehdr)) {
+ pr_err("bpf: 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_err("bpf: %s is not an eBPF object file\n",
+ obj->path);
+ err = -EINVAL;
+ goto errout;
+ }
+
+ return 0;
+errout:
+ bpf_obj_clear_elf(obj);
+ return err;
+}
+
int bpf__load(const char *path)
{
+ struct bpf_obj *obj;
+ int err;
+
pr_debug("bpf: loading %s\n", path);
+
+ if (elf_version(EV_CURRENT) == EV_NONE) {
+ pr_err("bpf: failed to init libelf for %s\n", path);
+ return -ENOTSUP;
+ }
+
+ obj = bpf_obj_alloc(path);
+ if (!obj) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if ((err = bpf_obj_elf_init(obj)))
+ goto out;
+
+ list_add(&obj->list, &bpf_obj_list);
return 0;
+out:
+ bpf_obj_close(obj);
+ return -1;
}

int bpf__run(void)
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index 122b178..6a6651b 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -18,4 +18,20 @@
int bpf__load(const char *path);
int bpf__run(void);

+struct bpf_obj {
+ /* All bpf objs should be linked together. */
+ struct list_head list;
+ 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)
#endif
--
1.8.3.4