[v4,3/5] bpf: add a test for BPF ELF load
Checks
Commit Message
Create an ELF file to load using clang.
Repackage the object into an array using xdd.
Write a test to see load and run the BPF.
Draft version made with Claude AI, but it didn't work.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
app/test/bpf/load.c | 62 +++++++++++++++++
app/test/bpf/meson.build | 52 ++++++++++++++
app/test/meson.build | 2 +
app/test/test_bpf.c | 147 +++++++++++++++++++++++++++++++++++++++
4 files changed, 263 insertions(+)
create mode 100644 app/test/bpf/load.c
create mode 100644 app/test/bpf/meson.build
Comments
Thank you for doing this! See comments inline.
> -----Original Message-----
> From: Stephen Hemminger <stephen@networkplumber.org>
> Sent: Tuesday 4 November 2025 16:07
> To: dev@dpdk.org
> Cc: Stephen Hemminger <stephen@networkplumber.org>; Konstantin Ananyev <konstantin.ananyev@huawei.com>
> Subject: [PATCH v4 3/5] bpf: add a test for BPF ELF load
>
> Create an ELF file to load using clang.
> Repackage the object into an array using xdd.
> Write a test to see load and run the BPF.
>
> Draft version made with Claude AI, but it didn't work.
>
> Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
> ---
> app/test/bpf/load.c | 62 +++++++++++++++++
> app/test/bpf/meson.build | 52 ++++++++++++++
> app/test/meson.build | 2 +
> app/test/test_bpf.c | 147 +++++++++++++++++++++++++++++++++++++++
> 4 files changed, 263 insertions(+)
> create mode 100644 app/test/bpf/load.c
> create mode 100644 app/test/bpf/meson.build
>
> diff --git a/app/test/bpf/load.c b/app/test/bpf/load.c
> new file mode 100644
> index 0000000000..9678c110d9
> --- /dev/null
> +++ b/app/test/bpf/load.c
> @@ -0,0 +1,62 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * BPF program for testing rte_bpf_elf_load
> + */
> +
> +typedef unsigned char uint8_t;
> +typedef unsigned short uint16_t;
> +typedef unsigned int uint32_t;
> +typedef unsigned long uint64_t;
> +
> +/* Match the structures from test_bpf.c */
> +struct dummy_offset {
> + uint64_t u64;
> + uint32_t u32;
> + uint16_t u16;
> + uint8_t u8;
> +} __attribute__((packed));
> +
> +struct dummy_vect8 {
> + struct dummy_offset in[8];
> + struct dummy_offset out[8];
> +};
Nit: I am sympathetic to re-using types already defined in test_bpf.c, but we
don't really need 8 elements, and first two members in dummy_offset are no
longer atomic which may or may not create problems someday. Perhaps it would be
easier and less confusing to just define another type for this test.
> +
> +/* External function declaration - provided by test via xsym */
> +extern void dummy_func1(const void *p, uint32_t *v32, uint64_t *v64);
Any reason not to make first argument `struct dummy_vect8`? Not that it
mattered for the test, just creates an impression that something is not working
or not trivial here.
> +
> +/*
> + * Test BPF function that will be loaded from ELF
> + * This function:
> + * 1. Reads values from input structure
> + * 2. Performs some computations
> + * 3. Writes results to output structure
> + * 4. Returns sum of values
> + */
> +__attribute__((section("func"), used))
Do we need `used` here and in other section attributes? I do not thing `clang
-c` will eliminate non-static/inline functions from the object file, or maybe I
don't understand the purpose (perhaps needs a comment).
> +uint64_t
> +test_func(struct dummy_vect8 *arg)
> +{
> + uint64_t sum = 0;
> + uint32_t v32;
> + uint64_t v64;
> +
> + /* Load input values */
> + v32 = arg->in[0].u32;
> + v64 = arg->in[0].u64;
> +
> + /* Call external function */
> + dummy_func1(arg, &v32, &v64);
> +
> + /* Store results */
> + arg->out[0].u32 = v32;
> + arg->out[0].u64 = v64;
> +
> + /* Calculate sum */
> + sum = arg->in[0].u64;
> + sum += arg->in[0].u32;
> + sum += arg->in[0].u16;
> + sum += arg->in[0].u8;
> + sum += v32;
> + sum += v64;
> +
> + return sum;
> +}
// snip meson files, lgtm
> --- a/app/test/test_bpf.c
> +++ b/app/test/test_bpf.c
> @@ -6,6 +6,7 @@
> #include <string.h>
> #include <stdint.h>
> #include <inttypes.h>
> +#include <unistd.h>
>
> #include <rte_memory.h>
> #include <rte_debug.h>
> @@ -14,6 +15,8 @@
> #include <rte_random.h>
> #include <rte_byteorder.h>
> #include <rte_errno.h>
> +#include <rte_bpf.h>
> +
> #include "test.h"
>
> #if !defined(RTE_LIB_BPF)
> @@ -3278,6 +3281,150 @@ test_bpf(void)
>
> REGISTER_FAST_TEST(bpf_autotest, true, true, test_bpf);
>
> +#ifdef TEST_BPF_ELF_LOAD
> +
> +/*
> + * Helper function to write BPF object data to temporary file.
> + * Returns temp file path on success, NULL on failure.
> + * Caller must free the returned path and unlink the file.
> + */
> +static char *
> +create_temp_bpf_file(const uint8_t *data, size_t size, const char *suffix)
Nit: last argument is not technically a suffix.
> +{
> + char *tmpfile = NULL;
> + int fd;
> + ssize_t written;
> +
> + if (asprintf(&tmpfile, "/tmp/dpdk_bpf_%s_XXXXXX", suffix) < 0) {
Not sure what the actual standard is, but I found in some previous discussions
a suggestion to use $TMPDIR and only fall back to /tmp if it is not defined.
Also, maybe give it .o suffix and use mkstemps below?
> + printf("%s@%d: asprintf failed: %s\n",
> + __func__, __LINE__, strerror(errno));
> + return NULL;
> + }
> +
> + /* Create and open temp file */
> + fd = mkstemp(tmpfile);
> + if (fd < 0) {
> + printf("%s@%d: mkstemp(%s) failed: %s\n",
> + __func__, __LINE__, tmpfile, strerror(errno));
> + free(tmpfile);
> + return NULL;
> + }
> +
> + /* Write BPF object data */
> + written = write(fd, data, size);
> + close(fd);
> +
> + if (written != (ssize_t)size) {
> + printf("%s@%d: write failed: %s\n",
> + __func__, __LINE__, strerror(errno));
> + unlink(tmpfile);
> + free(tmpfile);
> + return NULL;
> + }
> +
> + return tmpfile;
> +}
> +
> +#include "test_bpf_load.h"
> +
> +static int
> +test_bpf_elf_load(void)
> +{
> + uint8_t tbuf[sizeof(struct dummy_vect8)];
Why do we have to use unit8_t array here instead of just struct dummy_vect8?
If we do have to, how is alignment ensured?
> + const struct rte_bpf_xsym xsym[] = {
> + {
> + .name = RTE_STR(dummy_func1),
> + .type = RTE_BPF_XTYPE_FUNC,
> + .func = {
> + .val = (void *)dummy_func1,
> + .nb_args = 3,
> + .args = {
> + [0] = {
> + .type = RTE_BPF_ARG_PTR,
> + .size = sizeof(struct dummy_offset),
> + },
> + [1] = {
> + .type = RTE_BPF_ARG_PTR,
> + .size = sizeof(uint32_t),
> + },
> + [2] = {
> + .type = RTE_BPF_ARG_PTR,
> + .size = sizeof(uint64_t),
> + },
> + },
> + },
> + },
> + };
> + int ret;
> +
> + /* Create temp file from embedded BPF object */
> + char *tmpfile = create_temp_bpf_file(app_test_bpf_load_o,
> + app_test_bpf_load_o_len,
> + "load");
> + if (tmpfile == NULL)
> + return -1;
> +
> + /* Try to load BPF program from temp file */
> + const struct rte_bpf_prm prm = {
> + .xsym = xsym,
> + .nb_xsym = RTE_DIM(xsym),
> + .prog_arg = {
> + .type = RTE_BPF_ARG_PTR,
> + .size = sizeof(tbuf),
> + },
> + };
> + struct rte_bpf *bpf = rte_bpf_elf_load(&prm, tmpfile, "func");
> + TEST_ASSERT(bpf != NULL, "failed to load BPF from %s %d:%s",
> + tmpfile, rte_errno, strerror(rte_errno));
> +
> + /* Prepare test data */
> + struct dummy_vect8 *dv = (struct dummy_vect8 *)tbuf;
> + memset(dv, 0, sizeof(*dv));
> + dv->in[0].u64 = (int32_t)TEST_FILL_1;
> + dv->in[0].u32 = dv->in[0].u64;
> + dv->in[0].u16 = dv->in[0].u64;
> + dv->in[0].u8 = dv->in[0].u64;
> +
> + /* Execute loaded BPF program */
> + uint64_t sum = rte_bpf_exec(bpf, tbuf);
> + TEST_ASSERT(sum != 0, "BPF execution returned: %" PRIu64, sum);
Could we be more specific with the value we expect?
> +
> + /* Test JIT if available */
> + struct rte_bpf_jit jit;
> + ret = rte_bpf_get_jit(bpf, &jit);
> + TEST_ASSERT(ret == 0, "rte_bpf_get_jit failed: %d", ret);
> +
> + if (jit.func != NULL) {
> + memset(dv, 0, sizeof(*dv));
> + dv->in[0].u64 = (int32_t)TEST_FILL_1;
> + dv->in[0].u32 = dv->in[0].u64;
> + dv->in[0].u16 = dv->in[0].u64;
> + dv->in[0].u8 = dv->in[0].u64;
> +
> + uint64_t jsum = jit.func(tbuf);
> + TEST_ASSERT_EQUAL(sum, jsum, "BPF JIT execution difference");
> + }
> +
> + rte_bpf_destroy(bpf);
> + unlink(tmpfile);
> + free(tmpfile);
If any of the above asserts fail, leak sanitizer will also be unhappy. Perhaps
we could unlink tmpfile just after the rte_bpf_elf_load call, and unload BPF
before verifying return values from calls.
> +
> + printf("%s: ELF load test passed\n", __func__);
> + return TEST_SUCCESS;
> +}
> +#else
> +
> +static int
> +test_bpf_elf_load(void)
> +{
> + printf("BPF compile not supported, skipping test\n");
> + return TEST_SKIPPED;
> +}
> +
> +#endif /* !TEST_BPF_ELF_LOAD */
> +
> +REGISTER_FAST_TEST(bpf_elf_load_autotest, true, true, test_bpf_elf_load);
> +
> #ifndef RTE_HAS_LIBPCAP
>
> static int
> --
> 2.51.0
P.S. When running with UB sanitizer:
RTE>>bpf_elf_load_autotest
BPF: resolve_xsym(40): EBPF_PSEUDO_CALL to external function: dummy_func1
../../src/lib/bpf/bpf_validate.c:247:24: runtime error: signed integer overflow: 9223372036854775807 + 2147483647 cannot be represented in type 'long int'
#0 0x2b1fdca in eval_add ../../src/lib/bpf/bpf_validate.c:247
#1 0x2b2b9dc in eval_alu ../../src/lib/bpf/bpf_validate.c:691
#2 0x2b3b187 in evaluate ../../src/lib/bpf/bpf_validate.c:2446
#3 0x2b3bf0f in __rte_bpf_validate ../../src/lib/bpf/bpf_validate.c:2537
#4 0x2b19b33 in rte_bpf_load ../../src/lib/bpf/bpf_load.c:113
#5 0x2b47d6d in bpf_load_elf ../../src/lib/bpf/bpf_load_elf.c:288
#6 0x2b47fe8 in rte_bpf_elf_load ../../src/lib/bpf/bpf_load_elf.c:320
#7 0x66b4b8 in test_bpf_elf_load ../../src/app/test/test_bpf.c:3382
On Fri, 7 Nov 2025 17:33:08 +0000
Marat Khalili <marat.khalili@huawei.com> wrote:
> > +/*
> > + * Test BPF function that will be loaded from ELF
> > + * This function:
> > + * 1. Reads values from input structure
> > + * 2. Performs some computations
> > + * 3. Writes results to output structure
> > + * 4. Returns sum of values
> > + */
> > +__attribute__((section("func"), used))
>
> Do we need `used` here and in other section attributes? I do not thing `clang
> -c` will eliminate non-static/inline functions from the object file, or maybe I
> don't understand the purpose (perhaps needs a comment).
>
> > +uint64_t
> > +test_func(struct dummy_vect8 *arg)
The BPF loader in DPDK uses section names as the way to find functions.
The original version of this code was autogenerated off of examples used
in kernel BPF.
On Fri, 7 Nov 2025 17:45:51 +0000
Marat Khalili <marat.khalili@huawei.com> wrote:
> P.S. When running with UB sanitizer:
>
> RTE>>bpf_elf_load_autotest
> BPF: resolve_xsym(40): EBPF_PSEUDO_CALL to external function: dummy_func1
> ../../src/lib/bpf/bpf_validate.c:247:24: runtime error: signed integer overflow: 9223372036854775807 + 2147483647 cannot be represented in type 'long int'
> #0 0x2b1fdca in eval_add ../../src/lib/bpf/bpf_validate.c:247
> #1 0x2b2b9dc in eval_alu ../../src/lib/bpf/bpf_validate.c:691
> #2 0x2b3b187 in evaluate ../../src/lib/bpf/bpf_validate.c:2446
> #3 0x2b3bf0f in __rte_bpf_validate ../../src/lib/bpf/bpf_validate.c:2537
> #4 0x2b19b33 in rte_bpf_load ../../src/lib/bpf/bpf_load.c:113
> #5 0x2b47d6d in bpf_load_elf ../../src/lib/bpf/bpf_load_elf.c:288
> #6 0x2b47fe8 in rte_bpf_elf_load ../../src/lib/bpf/bpf_load_elf.c:320
> #7 0x66b4b8 in test_bpf_elf_load ../../src/app/test/test_bpf.c:3382
Hmm. looks like a pre-existing BPF library bug since this code was never
tested before.
> > RTE>>bpf_elf_load_autotest
> > BPF: resolve_xsym(40): EBPF_PSEUDO_CALL to external function: dummy_func1
> > ../../src/lib/bpf/bpf_validate.c:247:24: runtime error: signed integer overflow: 9223372036854775807
> + 2147483647 cannot be represented in type 'long int'
> > #0 0x2b1fdca in eval_add ../../src/lib/bpf/bpf_validate.c:247
> > #1 0x2b2b9dc in eval_alu ../../src/lib/bpf/bpf_validate.c:691
> > #2 0x2b3b187 in evaluate ../../src/lib/bpf/bpf_validate.c:2446
> > #3 0x2b3bf0f in __rte_bpf_validate ../../src/lib/bpf/bpf_validate.c:2537
> > #4 0x2b19b33 in rte_bpf_load ../../src/lib/bpf/bpf_load.c:113
> > #5 0x2b47d6d in bpf_load_elf ../../src/lib/bpf/bpf_load_elf.c:288
> > #6 0x2b47fe8 in rte_bpf_elf_load ../../src/lib/bpf/bpf_load_elf.c:320
> > #7 0x66b4b8 in test_bpf_elf_load ../../src/app/test/test_bpf.c:3382
>
> Hmm. looks like a pre-existing BPF library bug since this code was never
> tested before.
Yes, that's correct. Sent some fixes for this and some other issues to the mailing list just now, maybe we should treat these problems separately.
new file mode 100644
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * BPF program for testing rte_bpf_elf_load
+ */
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+typedef unsigned long uint64_t;
+
+/* Match the structures from test_bpf.c */
+struct dummy_offset {
+ uint64_t u64;
+ uint32_t u32;
+ uint16_t u16;
+ uint8_t u8;
+} __attribute__((packed));
+
+struct dummy_vect8 {
+ struct dummy_offset in[8];
+ struct dummy_offset out[8];
+};
+
+/* External function declaration - provided by test via xsym */
+extern void dummy_func1(const void *p, uint32_t *v32, uint64_t *v64);
+
+/*
+ * Test BPF function that will be loaded from ELF
+ * This function:
+ * 1. Reads values from input structure
+ * 2. Performs some computations
+ * 3. Writes results to output structure
+ * 4. Returns sum of values
+ */
+__attribute__((section("func"), used))
+uint64_t
+test_func(struct dummy_vect8 *arg)
+{
+ uint64_t sum = 0;
+ uint32_t v32;
+ uint64_t v64;
+
+ /* Load input values */
+ v32 = arg->in[0].u32;
+ v64 = arg->in[0].u64;
+
+ /* Call external function */
+ dummy_func1(arg, &v32, &v64);
+
+ /* Store results */
+ arg->out[0].u32 = v32;
+ arg->out[0].u64 = v64;
+
+ /* Calculate sum */
+ sum = arg->in[0].u64;
+ sum += arg->in[0].u32;
+ sum += arg->in[0].u16;
+ sum += arg->in[0].u8;
+ sum += v32;
+ sum += v64;
+
+ return sum;
+}
new file mode 100644
@@ -0,0 +1,52 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright 2025 Stephen Hemminger <stephen@networkplumber.org>
+
+bpf_test_hdrs = [ ]
+
+# use clang to compile to bpf
+clang_supports_bpf = false
+clang = find_program('clang', required: false)
+if clang.found()
+ clang_supports_bpf = run_command(clang, '-target', 'bpf', '--print-supported-cpus',
+ check: false).returncode() == 0
+endif
+
+if not clang_supports_bpf
+ message('app/test_bpf: no BPF load tests missing clang BPF support')
+ subdir_done()
+
+endif
+
+xxd = find_program('xxd', required: false)
+if not xxd.found()
+ message('app/test_bpf: missing xxd required to convert object to array')
+ subdir_done()
+endif
+
+# BPF compiler flags
+bpf_cflags = [ '-O2', '-target', 'bpf', '-g', '-c']
+
+# Enable test in test_bpf.c
+cflags += '-DTEST_BPF_ELF_LOAD'
+
+# BPF sources to compile
+bpf_progs = {
+ 'load' : 'test_bpf_load',
+}
+
+foreach bpf_src, bpf_hdr : bpf_progs
+ # Compile BPF C source to object file
+ bpf_obj = custom_target(bpf_src + '_o',
+ input: bpf_src + '.c',
+ output: bpf_src + '.o',
+ command: [ clang, bpf_cflags, '@INPUT@', '-o', '@OUTPUT@'])
+
+ # Convert object file to C header using xxd
+ bpf_test_h = custom_target(bpf_src + '_h',
+ input: bpf_obj,
+ output: bpf_hdr + '.h',
+ command: [ xxd, '-i', '@INPUT@', '@OUTPUT@'])
+
+ resources += bpf_test_h
+
+endforeach
@@ -281,6 +281,8 @@ if not is_windows
install: false)
endif
+subdir('bpf')
+
subdir('test_cfgfiles')
resources += test_cfgfile_h
@@ -6,6 +6,7 @@
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
+#include <unistd.h>
#include <rte_memory.h>
#include <rte_debug.h>
@@ -14,6 +15,8 @@
#include <rte_random.h>
#include <rte_byteorder.h>
#include <rte_errno.h>
+#include <rte_bpf.h>
+
#include "test.h"
#if !defined(RTE_LIB_BPF)
@@ -3278,6 +3281,150 @@ test_bpf(void)
REGISTER_FAST_TEST(bpf_autotest, true, true, test_bpf);
+#ifdef TEST_BPF_ELF_LOAD
+
+/*
+ * Helper function to write BPF object data to temporary file.
+ * Returns temp file path on success, NULL on failure.
+ * Caller must free the returned path and unlink the file.
+ */
+static char *
+create_temp_bpf_file(const uint8_t *data, size_t size, const char *suffix)
+{
+ char *tmpfile = NULL;
+ int fd;
+ ssize_t written;
+
+ if (asprintf(&tmpfile, "/tmp/dpdk_bpf_%s_XXXXXX", suffix) < 0) {
+ printf("%s@%d: asprintf failed: %s\n",
+ __func__, __LINE__, strerror(errno));
+ return NULL;
+ }
+
+ /* Create and open temp file */
+ fd = mkstemp(tmpfile);
+ if (fd < 0) {
+ printf("%s@%d: mkstemp(%s) failed: %s\n",
+ __func__, __LINE__, tmpfile, strerror(errno));
+ free(tmpfile);
+ return NULL;
+ }
+
+ /* Write BPF object data */
+ written = write(fd, data, size);
+ close(fd);
+
+ if (written != (ssize_t)size) {
+ printf("%s@%d: write failed: %s\n",
+ __func__, __LINE__, strerror(errno));
+ unlink(tmpfile);
+ free(tmpfile);
+ return NULL;
+ }
+
+ return tmpfile;
+}
+
+#include "test_bpf_load.h"
+
+static int
+test_bpf_elf_load(void)
+{
+ uint8_t tbuf[sizeof(struct dummy_vect8)];
+ const struct rte_bpf_xsym xsym[] = {
+ {
+ .name = RTE_STR(dummy_func1),
+ .type = RTE_BPF_XTYPE_FUNC,
+ .func = {
+ .val = (void *)dummy_func1,
+ .nb_args = 3,
+ .args = {
+ [0] = {
+ .type = RTE_BPF_ARG_PTR,
+ .size = sizeof(struct dummy_offset),
+ },
+ [1] = {
+ .type = RTE_BPF_ARG_PTR,
+ .size = sizeof(uint32_t),
+ },
+ [2] = {
+ .type = RTE_BPF_ARG_PTR,
+ .size = sizeof(uint64_t),
+ },
+ },
+ },
+ },
+ };
+ int ret;
+
+ /* Create temp file from embedded BPF object */
+ char *tmpfile = create_temp_bpf_file(app_test_bpf_load_o,
+ app_test_bpf_load_o_len,
+ "load");
+ if (tmpfile == NULL)
+ return -1;
+
+ /* Try to load BPF program from temp file */
+ const struct rte_bpf_prm prm = {
+ .xsym = xsym,
+ .nb_xsym = RTE_DIM(xsym),
+ .prog_arg = {
+ .type = RTE_BPF_ARG_PTR,
+ .size = sizeof(tbuf),
+ },
+ };
+ struct rte_bpf *bpf = rte_bpf_elf_load(&prm, tmpfile, "func");
+ TEST_ASSERT(bpf != NULL, "failed to load BPF from %s %d:%s",
+ tmpfile, rte_errno, strerror(rte_errno));
+
+ /* Prepare test data */
+ struct dummy_vect8 *dv = (struct dummy_vect8 *)tbuf;
+ memset(dv, 0, sizeof(*dv));
+ dv->in[0].u64 = (int32_t)TEST_FILL_1;
+ dv->in[0].u32 = dv->in[0].u64;
+ dv->in[0].u16 = dv->in[0].u64;
+ dv->in[0].u8 = dv->in[0].u64;
+
+ /* Execute loaded BPF program */
+ uint64_t sum = rte_bpf_exec(bpf, tbuf);
+ TEST_ASSERT(sum != 0, "BPF execution returned: %" PRIu64, sum);
+
+ /* Test JIT if available */
+ struct rte_bpf_jit jit;
+ ret = rte_bpf_get_jit(bpf, &jit);
+ TEST_ASSERT(ret == 0, "rte_bpf_get_jit failed: %d", ret);
+
+ if (jit.func != NULL) {
+ memset(dv, 0, sizeof(*dv));
+ dv->in[0].u64 = (int32_t)TEST_FILL_1;
+ dv->in[0].u32 = dv->in[0].u64;
+ dv->in[0].u16 = dv->in[0].u64;
+ dv->in[0].u8 = dv->in[0].u64;
+
+ uint64_t jsum = jit.func(tbuf);
+ TEST_ASSERT_EQUAL(sum, jsum, "BPF JIT execution difference");
+ }
+
+ rte_bpf_destroy(bpf);
+ unlink(tmpfile);
+ free(tmpfile);
+
+ printf("%s: ELF load test passed\n", __func__);
+ return TEST_SUCCESS;
+}
+#else
+
+static int
+test_bpf_elf_load(void)
+{
+ printf("BPF compile not supported, skipping test\n");
+ return TEST_SKIPPED;
+}
+
+#endif /* !TEST_BPF_ELF_LOAD */
+
+REGISTER_FAST_TEST(bpf_elf_load_autotest, true, true, test_bpf_elf_load);
+
#ifndef RTE_HAS_LIBPCAP
static int