bpf: add ability to load eBPF program from ELF object file
authorKonstantin Ananyev <konstantin.ananyev@intel.com>
Thu, 10 May 2018 10:23:04 +0000 (11:23 +0100)
committerThomas Monjalon <thomas@monjalon.net>
Fri, 11 May 2018 22:35:20 +0000 (00:35 +0200)
Introduce rte_bpf_elf_load() function to provide ability to
load eBPF program from ELF object file.
It also adds dependency on libelf.

Signed-off-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Acked-by: Ferruh Yigit <ferruh.yigit@intel.com>
config/common_base
doc/guides/rel_notes/release_18_05.rst
lib/librte_bpf/Makefile
lib/librte_bpf/bpf_load.c
lib/librte_bpf/bpf_load_elf.c [new file with mode: 0644]
lib/librte_bpf/meson.build
lib/librte_bpf/rte_bpf.h
lib/librte_bpf/rte_bpf_version.map
mk/rte.app.mk

index 10f9f90..b8fc657 100644 (file)
@@ -872,6 +872,8 @@ CONFIG_RTE_LIBRTE_IFCVF_VDPA_PMD=n
 # Compile librte_bpf
 #
 CONFIG_RTE_LIBRTE_BPF=y
+# allow load BPF from ELF files (requires libelf)
+CONFIG_RTE_LIBRTE_BPF_ELF=n
 
 #
 # Compile the test application
index aef6b62..0188756 100644 (file)
@@ -226,6 +226,7 @@ New Features
 
   The BPF Library provides the ability to load and execute
   Enhanced Berkeley Packet Filter (eBPF) within user-space dpdk application.
+  It also adds dependency on libelf.
 
 
 API Changes
index da93065..885a313 100644 (file)
@@ -12,6 +12,9 @@ CFLAGS += -DALLOW_EXPERIMENTAL_API
 LDLIBS += -lrte_net -lrte_eal
 LDLIBS += -lrte_mempool -lrte_ring
 LDLIBS += -lrte_mbuf -lrte_ethdev
+ifeq ($(CONFIG_RTE_LIBRTE_BPF_ELF),y)
+LDLIBS += -lelf
+endif
 
 EXPORT_MAP := rte_bpf_version.map
 
@@ -22,6 +25,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_BPF) += bpf.c
 SRCS-$(CONFIG_RTE_LIBRTE_BPF) += bpf_exec.c
 SRCS-$(CONFIG_RTE_LIBRTE_BPF) += bpf_load.c
 SRCS-$(CONFIG_RTE_LIBRTE_BPF) += bpf_validate.c
+ifeq ($(CONFIG_RTE_LIBRTE_BPF_ELF),y)
+SRCS-$(CONFIG_RTE_LIBRTE_BPF) += bpf_load_elf.c
+endif
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_BPF)-include += bpf_def.h
index f28ecfb..d1c9abd 100644 (file)
@@ -83,3 +83,19 @@ rte_bpf_load(const struct rte_bpf_prm *prm)
 
        return bpf;
 }
+
+__rte_experimental __attribute__ ((weak)) struct rte_bpf *
+rte_bpf_elf_load(const struct rte_bpf_prm *prm, const char *fname,
+       const char *sname)
+{
+       if (prm == NULL || fname == NULL || sname == NULL) {
+               rte_errno = EINVAL;
+               return NULL;
+       }
+
+       RTE_BPF_LOG(ERR, "%s() is not supported with current config\n"
+               "rebuild with libelf installed\n",
+               __func__);
+       rte_errno = ENOTSUP;
+       return NULL;
+}
diff --git a/lib/librte_bpf/bpf_load_elf.c b/lib/librte_bpf/bpf_load_elf.c
new file mode 100644 (file)
index 0000000..6ab03d8
--- /dev/null
@@ -0,0 +1,322 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <fcntl.h>
+
+#include <libelf.h>
+
+#include <rte_common.h>
+#include <rte_log.h>
+#include <rte_debug.h>
+#include <rte_memory.h>
+#include <rte_eal.h>
+#include <rte_byteorder.h>
+#include <rte_errno.h>
+
+#include "bpf_impl.h"
+
+/* To overcome compatibility issue */
+#ifndef EM_BPF
+#define        EM_BPF  247
+#endif
+
+static uint32_t
+bpf_find_xsym(const char *sn, enum rte_bpf_xtype type,
+       const struct rte_bpf_xsym fp[], uint32_t fn)
+{
+       uint32_t i;
+
+       if (sn == NULL || fp == NULL)
+               return UINT32_MAX;
+
+       for (i = 0; i != fn; i++) {
+               if (fp[i].type == type && strcmp(sn, fp[i].name) == 0)
+                       break;
+       }
+
+       return (i != fn) ? i : UINT32_MAX;
+}
+
+/*
+ * update BPF code at offset *ofs* with a proper address(index) for external
+ * symbol *sn*
+ */
+static int
+resolve_xsym(const char *sn, size_t ofs, struct ebpf_insn *ins, size_t ins_sz,
+       const struct rte_bpf_prm *prm)
+{
+       uint32_t idx, fidx;
+       enum rte_bpf_xtype type;
+
+       if (ofs % sizeof(ins[0]) != 0 || ofs >= ins_sz)
+               return -EINVAL;
+
+       idx = ofs / sizeof(ins[0]);
+       if (ins[idx].code == (BPF_JMP | EBPF_CALL))
+               type = RTE_BPF_XTYPE_FUNC;
+       else if (ins[idx].code == (BPF_LD | BPF_IMM | EBPF_DW) &&
+                       ofs < ins_sz - sizeof(ins[idx]))
+               type = RTE_BPF_XTYPE_VAR;
+       else
+               return -EINVAL;
+
+       fidx = bpf_find_xsym(sn, type, prm->xsym, prm->nb_xsym);
+       if (fidx == UINT32_MAX)
+               return -ENOENT;
+
+       /* for function we just need an index in our xsym table */
+       if (type == RTE_BPF_XTYPE_FUNC)
+               ins[idx].imm = fidx;
+       /* for variable we need to store its absolute address */
+       else {
+               ins[idx].imm = (uintptr_t)prm->xsym[fidx].var;
+               ins[idx + 1].imm =
+                       (uint64_t)(uintptr_t)prm->xsym[fidx].var >> 32;
+       }
+
+       return 0;
+}
+
+static int
+check_elf_header(const Elf64_Ehdr *eh)
+{
+       const char *err;
+
+       err = NULL;
+
+#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
+       if (eh->e_ident[EI_DATA] != ELFDATA2LSB)
+#else
+       if (eh->e_ident[EI_DATA] != ELFDATA2MSB)
+#endif
+               err = "not native byte order";
+       else if (eh->e_ident[EI_OSABI] != ELFOSABI_NONE)
+               err = "unexpected OS ABI";
+       else if (eh->e_type != ET_REL)
+               err = "unexpected ELF type";
+       else if (eh->e_machine != EM_NONE && eh->e_machine != EM_BPF)
+               err = "unexpected machine type";
+
+       if (err != NULL) {
+               RTE_BPF_LOG(ERR, "%s(): %s\n", __func__, err);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * helper function, find executable section by name.
+ */
+static int
+find_elf_code(Elf *elf, const char *section, Elf_Data **psd, size_t *pidx)
+{
+       Elf_Scn *sc;
+       const Elf64_Ehdr *eh;
+       const Elf64_Shdr *sh;
+       Elf_Data *sd;
+       const char *sn;
+       int32_t rc;
+
+       eh = elf64_getehdr(elf);
+       if (eh == NULL) {
+               rc = elf_errno();
+               RTE_BPF_LOG(ERR, "%s(%p, %s) error code: %d(%s)\n",
+                       __func__, elf, section, rc, elf_errmsg(rc));
+               return -EINVAL;
+       }
+
+       if (check_elf_header(eh) != 0)
+               return -EINVAL;
+
+       /* find given section by name */
+       for (sc = elf_nextscn(elf, NULL); sc != NULL;
+                       sc = elf_nextscn(elf, sc)) {
+               sh = elf64_getshdr(sc);
+               sn = elf_strptr(elf, eh->e_shstrndx, sh->sh_name);
+               if (sn != NULL && strcmp(section, sn) == 0 &&
+                               sh->sh_type == SHT_PROGBITS &&
+                               sh->sh_flags == (SHF_ALLOC | SHF_EXECINSTR))
+                       break;
+       }
+
+       sd = elf_getdata(sc, NULL);
+       if (sd == NULL || sd->d_size == 0 ||
+                       sd->d_size % sizeof(struct ebpf_insn) != 0) {
+               rc = elf_errno();
+               RTE_BPF_LOG(ERR, "%s(%p, %s) error code: %d(%s)\n",
+                       __func__, elf, section, rc, elf_errmsg(rc));
+               return -EINVAL;
+       }
+
+       *psd = sd;
+       *pidx = elf_ndxscn(sc);
+       return 0;
+}
+
+/*
+ * helper function to process data from relocation table.
+ */
+static int
+process_reloc(Elf *elf, size_t sym_idx, Elf64_Rel *re, size_t re_sz,
+       struct ebpf_insn *ins, size_t ins_sz, const struct rte_bpf_prm *prm)
+{
+       int32_t rc;
+       uint32_t i, n;
+       size_t ofs, sym;
+       const char *sn;
+       const Elf64_Ehdr *eh;
+       Elf_Scn *sc;
+       const Elf_Data *sd;
+       Elf64_Sym *sm;
+
+       eh = elf64_getehdr(elf);
+
+       /* get symtable by section index */
+       sc = elf_getscn(elf, sym_idx);
+       sd = elf_getdata(sc, NULL);
+       if (sd == NULL)
+               return -EINVAL;
+       sm = sd->d_buf;
+
+       n = re_sz / sizeof(re[0]);
+       for (i = 0; i != n; i++) {
+
+               ofs = re[i].r_offset;
+
+               /* retrieve index in the symtable */
+               sym = ELF64_R_SYM(re[i].r_info);
+               if (sym * sizeof(sm[0]) >= sd->d_size)
+                       return -EINVAL;
+
+               sn = elf_strptr(elf, eh->e_shstrndx, sm[sym].st_name);
+
+               rc = resolve_xsym(sn, ofs, ins, ins_sz, prm);
+               if (rc != 0) {
+                       RTE_BPF_LOG(ERR,
+                               "resolve_xsym(%s, %zu) error code: %d\n",
+                               sn, ofs, rc);
+                       return rc;
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * helper function, find relocation information (if any)
+ * and update bpf code.
+ */
+static int
+elf_reloc_code(Elf *elf, Elf_Data *ed, size_t sidx,
+       const struct rte_bpf_prm *prm)
+{
+       Elf64_Rel *re;
+       Elf_Scn *sc;
+       const Elf64_Shdr *sh;
+       const Elf_Data *sd;
+       int32_t rc;
+
+       rc = 0;
+
+       /* walk through all sections */
+       for (sc = elf_nextscn(elf, NULL); sc != NULL && rc == 0;
+                       sc = elf_nextscn(elf, sc)) {
+
+               sh = elf64_getshdr(sc);
+
+               /* relocation data for our code section */
+               if (sh->sh_type == SHT_REL && sh->sh_info == sidx) {
+                       sd = elf_getdata(sc, NULL);
+                       if (sd == NULL || sd->d_size == 0 ||
+                                       sd->d_size % sizeof(re[0]) != 0)
+                               return -EINVAL;
+                       rc = process_reloc(elf, sh->sh_link,
+                               sd->d_buf, sd->d_size, ed->d_buf, ed->d_size,
+                               prm);
+               }
+       }
+
+       return rc;
+}
+
+static struct rte_bpf *
+bpf_load_elf(const struct rte_bpf_prm *prm, int32_t fd, const char *section)
+{
+       Elf *elf;
+       Elf_Data *sd;
+       size_t sidx;
+       int32_t rc;
+       struct rte_bpf *bpf;
+       struct rte_bpf_prm np;
+
+       elf_version(EV_CURRENT);
+       elf = elf_begin(fd, ELF_C_READ, NULL);
+
+       rc = find_elf_code(elf, section, &sd, &sidx);
+       if (rc == 0)
+               rc = elf_reloc_code(elf, sd, sidx, prm);
+
+       if (rc == 0) {
+               np = prm[0];
+               np.ins = sd->d_buf;
+               np.nb_ins = sd->d_size / sizeof(struct ebpf_insn);
+               bpf = rte_bpf_load(&np);
+       } else {
+               bpf = NULL;
+               rte_errno = -rc;
+       }
+
+       elf_end(elf);
+       return bpf;
+}
+
+__rte_experimental struct rte_bpf *
+rte_bpf_elf_load(const struct rte_bpf_prm *prm, const char *fname,
+       const char *sname)
+{
+       int32_t fd, rc;
+       struct rte_bpf *bpf;
+
+       if (prm == NULL || fname == NULL || sname == NULL) {
+               rte_errno = EINVAL;
+               return NULL;
+       }
+
+       fd = open(fname, O_RDONLY);
+       if (fd < 0) {
+               rc = errno;
+               RTE_BPF_LOG(ERR, "%s(%s) error code: %d(%s)\n",
+                       __func__, fname, rc, strerror(rc));
+               rte_errno = EINVAL;
+               return NULL;
+       }
+
+       bpf = bpf_load_elf(prm, fd, sname);
+       close(fd);
+
+       if (bpf == NULL) {
+               RTE_BPF_LOG(ERR,
+                       "%s(fname=\"%s\", sname=\"%s\") failed, "
+                       "error code: %d\n",
+                       __func__, fname, sname, rte_errno);
+               return NULL;
+       }
+
+       RTE_BPF_LOG(INFO, "%s(fname=\"%s\", sname=\"%s\") "
+               "successfully creates %p(jit={.func=%p,.sz=%zu});\n",
+               __func__, fname, sname, bpf, bpf->jit.func, bpf->jit.sz);
+       return bpf;
+}
index 4fa000f..a6a9229 100644 (file)
@@ -11,3 +11,9 @@ install_headers = files('bpf_def.h',
                        'rte_bpf.h')
 
 deps += ['mbuf', 'net']
+
+dep = cc.find_library('elf', required: false)
+if dep.found() == true and cc.has_header('libelf.h', dependencies: dep)
+       sources += files('bpf_load_elf.c')
+       ext_deps += dep
+endif
index 4b3d970..1d6c4a9 100644 (file)
@@ -115,6 +115,26 @@ void rte_bpf_destroy(struct rte_bpf *bpf);
  */
 struct rte_bpf *rte_bpf_load(const struct rte_bpf_prm *prm);
 
+/**
+ * Create a new eBPF execution context and load BPF code from given ELF
+ * file into it.
+ *
+ * @param prm
+ *  Parameters used to create and initialise the BPF exeution context.
+ * @param fname
+ *  Pathname for a ELF file.
+ * @param sname
+ *  Name of the executable section within the file to load.
+ * @return
+ *   BPF handle that is used in future BPF operations,
+ *   or NULL on error, with error code set in rte_errno.
+ *   Possible rte_errno errors include:
+ *   - EINVAL - invalid parameter passed to function
+ *   - ENOMEM - can't reserve enough memory
+ */
+struct rte_bpf *rte_bpf_elf_load(const struct rte_bpf_prm *prm,
+       const char *fname, const char *sname);
+
 /**
  * Execute given BPF bytecode.
  *
index ea1d621..ff65144 100644 (file)
@@ -2,6 +2,7 @@ EXPERIMENTAL {
        global:
 
        rte_bpf_destroy;
+       rte_bpf_elf_load;
        rte_bpf_exec;
        rte_bpf_exec_burst;
        rte_bpf_get_jit;
index 15a0121..438f99d 100644 (file)
@@ -82,6 +82,9 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_POWER)          += -lrte_power
 
 _LDLIBS-$(CONFIG_RTE_LIBRTE_EFD)            += -lrte_efd
 _LDLIBS-$(CONFIG_RTE_LIBRTE_BPF)            += -lrte_bpf
+ifeq ($(CONFIG_RTE_LIBRTE_BPF_ELF),y)
+_LDLIBS-$(CONFIG_RTE_LIBRTE_BPF)            += -lelf
+endif
 
 _LDLIBS-y += --whole-archive