> -----Original Message----- > From: Stephen Hemminger <[email protected]> > Sent: Sunday 9 November 2025 20:08 > To: [email protected] > Cc: Stephen Hemminger <[email protected]>; Konstantin Ananyev > <[email protected]> > Subject: [PATCH v5 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 <[email protected]>
Don't know what's going on with the test without libelf, perhaps someone else needs to try. Apart from this, Acked-by: Marat Khalili <[email protected]> > --- > app/test/bpf/load.c | 51 +++++++++++++ > app/test/bpf/meson.build | 52 +++++++++++++ > app/test/meson.build | 2 + > app/test/test_bpf.c | 156 +++++++++++++++++++++++++++++++++++++++ > 4 files changed, 261 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..a4d3d61d7a > --- /dev/null > +++ b/app/test/bpf/load.c > @@ -0,0 +1,51 @@ > +/* 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 is compiled version of code used in test_call1 > + */ > +__attribute__((section("call1"), used)) > +uint64_t > +test_call1(struct dummy_vect8 *arg) > +{ > + 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; > + > + v64 += v32; > + return v64; > +} > diff --git a/app/test/bpf/meson.build b/app/test/bpf/meson.build > new file mode 100644 > index 0000000000..b4f54aa976 > --- /dev/null > +++ b/app/test/bpf/meson.build > @@ -0,0 +1,52 @@ > +# SPDX-License-Identifier: BSD-3-Clause > +# Copyright 2025 Stephen Hemminger <[email protected]> > + > +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 > diff --git a/app/test/meson.build b/app/test/meson.build > index 8df8d3edd1..efec42a6bf 100644 > --- a/app/test/meson.build > +++ b/app/test/meson.build > @@ -281,6 +281,8 @@ if not is_windows > install: false) > endif > > +subdir('bpf') > + > subdir('test_cfgfiles') > > resources += test_cfgfile_h > diff --git a/app/test/test_bpf.c b/app/test/test_bpf.c > index 90e10d7d2c..a80f41a543 100644 > --- 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,159 @@ 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 *name) > +{ > + char *tmpfile = NULL; > + int fd; > + ssize_t written; > + > + if (asprintf(&tmpfile, "/tmp/dpdk_bpf_%s_XXXXXX.o", name) < 0) { > + printf("%s@%d: asprintf failed: %s\n", > + __func__, __LINE__, strerror(errno)); > + return NULL; > + } > + > + /* Create and open temp file */ > + fd = mkstemps(tmpfile, strlen(".o")); > + if (fd < 0) { > + printf("%s@%d: mkstemps(%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" > + > +/* > + * Test loading BPF program from an object file. > + * This test uses same arguments as previous test_call1 example. > + */ > +static int > +test_bpf_elf_load(void) > +{ > + static const char test_section[] = "call1"; > + 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, test_section); > + unlink(tmpfile); > + free(tmpfile); > + > + TEST_ASSERT(bpf != NULL, "failed to load BPF %d:%s", 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 rc = rte_bpf_exec(bpf, tbuf); > + ret = test_call1_check(rc, tbuf); > + TEST_ASSERT(ret == 0, "test_call1_check failed: %d", ret); > + > + /* 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; > + > + rc = jit.func(tbuf); > + ret = test_call1_check(rc, tbuf); > + TEST_ASSERT(ret == 0, "jit test_call1_check failed: %d", ret); > + } > + > + rte_bpf_destroy(bpf); > + > + 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

