From: Vineet Gupta <gvineet@meta.com>
To: Andrii Nakryiko <andrii@kernel.org>, bpf@vger.kernel.org
Cc: kernel-team@meta.com, Ihor Solodrai <ihor.solodrai@linux.dev>
Subject: Re: [PATCH v2 bpf-next 2/2] selftests/bpf: Add linked_externs test for externs in allocated sections
Date: Mon, 28 Sep 2026 14:52:59 -0700 [thread overview]
Message-ID: <6cadba40-10bb-4acf-9a32-d22a270ada8d@linux.dev> (raw)
In-Reply-To: <20260926000243.2830819-2-andrii@kernel.org>
Hi Andrii,
On 9/25/26 5:02 PM, Andrii Nakryiko wrote:
> Add two objects that define an arena global each and declare the
> other one's as extern, linked into a single skeleton with the
> extern-declaring object first.
>
> The skeleton build relinks the linked object twice more, which fixes
> up DATASEC offsets from the ELF symbol table, so the test also drives
> the libbpf linker directly: it links linked_arena1.bpf.o on its own,
> links the result with linked_arena2.bpf.o, checks that no variable in
> the .addr_space.1 DATASEC is left extern, and loads the object. The
> same is done with linked_maps1/2.bpf.o, which have an extern map in
> .maps.
>
> Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
> ---
> tools/testing/selftests/bpf/Makefile.skel | 3 +-
> .../selftests/bpf/prog_tests/linked_externs.c | 109 ++++++++++++++++++
> .../selftests/bpf/progs/linked_arena1.c | 22 ++++
> .../selftests/bpf/progs/linked_arena2.c | 18 +++
> 4 files changed, 151 insertions(+), 1 deletion(-)
> create mode 100644 tools/testing/selftests/bpf/prog_tests/linked_externs.c
> create mode 100644 tools/testing/selftests/bpf/progs/linked_arena1.c
> create mode 100644 tools/testing/selftests/bpf/progs/linked_arena2.c
This is tripping up CI, fails with BPF_GCC [1] and some more (sched-ext)
Fix for gcc build failure is posted here [2]
[1] https://github.com/kernel-patches/bpf/actions/runs/36243003457
[2]
https://lore.kernel.org/bpf/20260928215110.3966357-1-vineet.gupta@linux.dev/T/#u
Thx,
-Vineet
>
> diff --git a/tools/testing/selftests/bpf/Makefile.skel b/tools/testing/selftests/bpf/Makefile.skel
> index 3d92cdca62ed..2e22bb901bf3 100644
> --- a/tools/testing/selftests/bpf/Makefile.skel
> +++ b/tools/testing/selftests/bpf/Makefile.skel
> @@ -23,7 +23,7 @@ BPF_OBJS := $(patsubst %.c,$(RDIR)/%.bpf.o,$(BPF_SRCS))
> SKEL_BLACKLIST := btf__% test_pinning_invalid.c test_sk_assign.c
>
> LINKED_SKELS := test_static_linked.skel.h linked_funcs.skel.h \
> - linked_vars.skel.h linked_maps.skel.h \
> + linked_vars.skel.h linked_maps.skel.h linked_arena.skel.h \
> test_subskeleton.skel.h test_subskeleton_lib.skel.h \
> test_usdt.skel.h tracing_multi.skel.h \
> tracing_multi_module.skel.h \
> @@ -46,6 +46,7 @@ test_static_linked.skel.h-deps := test_static_linked1.bpf.o test_static_linked2.
> linked_funcs.skel.h-deps := linked_funcs1.bpf.o linked_funcs2.bpf.o
> linked_vars.skel.h-deps := linked_vars1.bpf.o linked_vars2.bpf.o
> linked_maps.skel.h-deps := linked_maps1.bpf.o linked_maps2.bpf.o
> +linked_arena.skel.h-deps := linked_arena1.bpf.o linked_arena2.bpf.o
> # In the subskeleton case, we want the test_subskeleton_lib.subskel.h file
> # but that's created as a side-effect of the skel.h generation.
> test_subskeleton.skel.h-deps := test_subskeleton_lib2.bpf.o test_subskeleton_lib.bpf.o test_subskeleton.bpf.o
> diff --git a/tools/testing/selftests/bpf/prog_tests/linked_externs.c b/tools/testing/selftests/bpf/prog_tests/linked_externs.c
> new file mode 100644
> index 000000000000..29358c82dc35
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/linked_externs.c
> @@ -0,0 +1,109 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
> +#include <test_progs.h>
> +#include <bpf/btf.h>
> +#include "linked_arena.skel.h"
> +
> +static void test_skel(void)
> +{
> + LIBBPF_OPTS(bpf_test_run_opts, opts);
> + struct linked_arena *skel;
> + int err;
> +
> + skel = linked_arena__open_and_load();
> + if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
> + return;
> +
> + ASSERT_EQ(skel->arena->a_val, 1, "a_val_init");
> + ASSERT_EQ(skel->arena->b_val, 2, "b_val_init");
> +
> + err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.sum1), &opts);
> + ASSERT_OK(err, "sum1_run");
> + ASSERT_EQ(opts.retval, 1 + 2, "sum1_retval");
> +
> + err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.bump2), &opts);
> + ASSERT_OK(err, "bump2_run");
> + ASSERT_EQ(opts.retval, 11 + 22, "bump2_retval");
> +
> + ASSERT_EQ(skel->arena->a_val, 11, "a_val");
> + ASSERT_EQ(skel->arena->b_val, 22, "b_val");
> +
> + linked_arena__destroy(skel);
> +}
> +
> +static int link_objs(const char *out, const char *in1, const char *in2)
> +{
> + struct bpf_linker *linker;
> + int err;
> +
> + linker = bpf_linker__new(out, NULL);
> + if (!ASSERT_OK_PTR(linker, "linker_new"))
> + return -EINVAL;
> +
> + err = bpf_linker__add_file(linker, in1, NULL);
> + if (!ASSERT_OK(err, in1))
> + goto cleanup;
> + if (in2) {
> + err = bpf_linker__add_file(linker, in2, NULL);
> + if (!ASSERT_OK(err, in2))
> + goto cleanup;
> + }
> + err = bpf_linker__finalize(linker);
> + ASSERT_OK(err, "finalize");
> +
> +cleanup:
> + bpf_linker__free(linker);
> + return err;
> +}
> +
> +/* link in1 on its own first, then link the result with in2 */
> +static void test_relink(const char *in1, const char *in2, const char *sec_name)
> +{
> + char out1[] = "/tmp/linked_externs.XXXXXX", out2[] = "/tmp/linked_externs.XXXXXX";
> + const struct btf_var_secinfo *vi;
> + struct bpf_object *obj = NULL;
> + const struct btf_type *t;
> + struct btf *btf;
> + int i, id, n;
> +
> + close(mkstemp(out1));
> + close(mkstemp(out2));
> +
> + if (!ASSERT_OK(link_objs(out1, in1, NULL), "link_stage1") ||
> + !ASSERT_OK(link_objs(out2, out1, in2), "link_stage2"))
> + goto cleanup;
> +
> + obj = bpf_object__open_file(out2, NULL);
> + if (!ASSERT_OK_PTR(obj, "obj_open"))
> + goto cleanup;
> +
> + btf = bpf_object__btf(obj);
> + id = btf__find_by_name_kind(btf, sec_name, BTF_KIND_DATASEC);
> + if (!ASSERT_GT(id, 0, "find_datasec"))
> + goto cleanup;
> +
> + t = btf__type_by_id(btf, id);
> + vi = btf_var_secinfos(t);
> + n = btf_vlen(t);
> + for (i = 0; i < n; i++) {
> + t = btf__type_by_id(btf, vi[i].type);
> + ASSERT_NEQ(btf_var(t)->linkage, BTF_VAR_GLOBAL_EXTERN, "var_resolved");
> + }
> +
> + ASSERT_OK(bpf_object__load(obj), "obj_load");
> +
> +cleanup:
> + bpf_object__close(obj);
> + unlink(out1);
> + unlink(out2);
> +}
> +
> +void test_linked_externs(void)
> +{
> + if (test__start_subtest("skel_arena"))
> + test_skel();
> + if (test__start_subtest("relink_arena"))
> + test_relink("linked_arena1.bpf.o", "linked_arena2.bpf.o", ".addr_space.1");
> + if (test__start_subtest("relink_maps"))
> + test_relink("linked_maps1.bpf.o", "linked_maps2.bpf.o", ".maps");
> +}
> diff --git a/tools/testing/selftests/bpf/progs/linked_arena1.c b/tools/testing/selftests/bpf/progs/linked_arena1.c
> new file mode 100644
> index 000000000000..3e8788dec1d9
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/linked_arena1.c
> @@ -0,0 +1,22 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
> +#include <vmlinux.h>
> +#include <bpf/bpf_helpers.h>
> +#include "bpf_arena_common.h"
> +
> +struct {
> + __uint(type, BPF_MAP_TYPE_ARENA);
> + __uint(map_flags, BPF_F_MMAPABLE);
> + __uint(max_entries, 1); /* number of pages */
> +} arena SEC(".maps");
> +
> +long __arena a_val = 1;
> +extern long __arena b_val; /* defined in linked_arena2.c */
> +
> +SEC("syscall")
> +int sum1(void *ctx)
> +{
> + return a_val + b_val;
> +}
> +
> +char _license[] SEC("license") = "GPL";
> diff --git a/tools/testing/selftests/bpf/progs/linked_arena2.c b/tools/testing/selftests/bpf/progs/linked_arena2.c
> new file mode 100644
> index 000000000000..d0bcdf18a4b6
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/linked_arena2.c
> @@ -0,0 +1,18 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
> +#include <vmlinux.h>
> +#include <bpf/bpf_helpers.h>
> +#include "bpf_arena_common.h"
> +
> +long __arena b_val = 2;
> +extern long __arena a_val; /* defined in linked_arena1.c */
> +
> +SEC("syscall")
> +int bump2(void *ctx)
> +{
> + a_val += 10;
> + b_val += 20;
> + return a_val + b_val;
> +}
> +
> +char _license[] SEC("license") = "GPL";
WARNING: multiple messages have this Message-ID (diff)
From: Vineet Gupta <vineet.gupta@linux.dev>
To: bpf@vger.kernel.org
Cc: kernel-team@meta.com, Ihor Solodrai <ihor.solodrai@linux.dev>
Subject: Re: [PATCH v2 bpf-next 2/2] selftests/bpf: Add linked_externs test for externs in allocated sections
Date: Mon, 28 Sep 2026 14:52:59 -0700 [thread overview]
Message-ID: <6cadba40-10bb-4acf-9a32-d22a270ada8d@linux.dev> (raw)
Message-ID: <20260928215259.79HZdS5cr3XmU_Wrc6t2XMLDh4PKme5_iz3tc8_xFpo@z> (raw)
In-Reply-To: <20260926000243.2830819-2-andrii@kernel.org>
Hi Andrii,
On 9/25/26 5:02 PM, Andrii Nakryiko wrote:
> Add two objects that define an arena global each and declare the
> other one's as extern, linked into a single skeleton with the
> extern-declaring object first.
>
> The skeleton build relinks the linked object twice more, which fixes
> up DATASEC offsets from the ELF symbol table, so the test also drives
> the libbpf linker directly: it links linked_arena1.bpf.o on its own,
> links the result with linked_arena2.bpf.o, checks that no variable in
> the .addr_space.1 DATASEC is left extern, and loads the object. The
> same is done with linked_maps1/2.bpf.o, which have an extern map in
> .maps.
>
> Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
> ---
> tools/testing/selftests/bpf/Makefile.skel | 3 +-
> .../selftests/bpf/prog_tests/linked_externs.c | 109 ++++++++++++++++++
> .../selftests/bpf/progs/linked_arena1.c | 22 ++++
> .../selftests/bpf/progs/linked_arena2.c | 18 +++
> 4 files changed, 151 insertions(+), 1 deletion(-)
> create mode 100644 tools/testing/selftests/bpf/prog_tests/linked_externs.c
> create mode 100644 tools/testing/selftests/bpf/progs/linked_arena1.c
> create mode 100644 tools/testing/selftests/bpf/progs/linked_arena2.c
This is tripping up CI, fails with BPF_GCC [1] and some more (sched-ext)
Fix for gcc build failure is posted here [2]
[1] https://github.com/kernel-patches/bpf/actions/runs/36243003457
[2]
https://lore.kernel.org/bpf/20260928215110.3966357-1-vineet.gupta@linux.dev/T/#u
Thx,
-Vineet
>
> diff --git a/tools/testing/selftests/bpf/Makefile.skel b/tools/testing/selftests/bpf/Makefile.skel
> index 3d92cdca62ed..2e22bb901bf3 100644
> --- a/tools/testing/selftests/bpf/Makefile.skel
> +++ b/tools/testing/selftests/bpf/Makefile.skel
> @@ -23,7 +23,7 @@ BPF_OBJS := $(patsubst %.c,$(RDIR)/%.bpf.o,$(BPF_SRCS))
> SKEL_BLACKLIST := btf__% test_pinning_invalid.c test_sk_assign.c
>
> LINKED_SKELS := test_static_linked.skel.h linked_funcs.skel.h \
> - linked_vars.skel.h linked_maps.skel.h \
> + linked_vars.skel.h linked_maps.skel.h linked_arena.skel.h \
> test_subskeleton.skel.h test_subskeleton_lib.skel.h \
> test_usdt.skel.h tracing_multi.skel.h \
> tracing_multi_module.skel.h \
> @@ -46,6 +46,7 @@ test_static_linked.skel.h-deps := test_static_linked1.bpf.o test_static_linked2.
> linked_funcs.skel.h-deps := linked_funcs1.bpf.o linked_funcs2.bpf.o
> linked_vars.skel.h-deps := linked_vars1.bpf.o linked_vars2.bpf.o
> linked_maps.skel.h-deps := linked_maps1.bpf.o linked_maps2.bpf.o
> +linked_arena.skel.h-deps := linked_arena1.bpf.o linked_arena2.bpf.o
> # In the subskeleton case, we want the test_subskeleton_lib.subskel.h file
> # but that's created as a side-effect of the skel.h generation.
> test_subskeleton.skel.h-deps := test_subskeleton_lib2.bpf.o test_subskeleton_lib.bpf.o test_subskeleton.bpf.o
> diff --git a/tools/testing/selftests/bpf/prog_tests/linked_externs.c b/tools/testing/selftests/bpf/prog_tests/linked_externs.c
> new file mode 100644
> index 000000000000..29358c82dc35
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/linked_externs.c
> @@ -0,0 +1,109 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
> +#include <test_progs.h>
> +#include <bpf/btf.h>
> +#include "linked_arena.skel.h"
> +
> +static void test_skel(void)
> +{
> + LIBBPF_OPTS(bpf_test_run_opts, opts);
> + struct linked_arena *skel;
> + int err;
> +
> + skel = linked_arena__open_and_load();
> + if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
> + return;
> +
> + ASSERT_EQ(skel->arena->a_val, 1, "a_val_init");
> + ASSERT_EQ(skel->arena->b_val, 2, "b_val_init");
> +
> + err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.sum1), &opts);
> + ASSERT_OK(err, "sum1_run");
> + ASSERT_EQ(opts.retval, 1 + 2, "sum1_retval");
> +
> + err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.bump2), &opts);
> + ASSERT_OK(err, "bump2_run");
> + ASSERT_EQ(opts.retval, 11 + 22, "bump2_retval");
> +
> + ASSERT_EQ(skel->arena->a_val, 11, "a_val");
> + ASSERT_EQ(skel->arena->b_val, 22, "b_val");
> +
> + linked_arena__destroy(skel);
> +}
> +
> +static int link_objs(const char *out, const char *in1, const char *in2)
> +{
> + struct bpf_linker *linker;
> + int err;
> +
> + linker = bpf_linker__new(out, NULL);
> + if (!ASSERT_OK_PTR(linker, "linker_new"))
> + return -EINVAL;
> +
> + err = bpf_linker__add_file(linker, in1, NULL);
> + if (!ASSERT_OK(err, in1))
> + goto cleanup;
> + if (in2) {
> + err = bpf_linker__add_file(linker, in2, NULL);
> + if (!ASSERT_OK(err, in2))
> + goto cleanup;
> + }
> + err = bpf_linker__finalize(linker);
> + ASSERT_OK(err, "finalize");
> +
> +cleanup:
> + bpf_linker__free(linker);
> + return err;
> +}
> +
> +/* link in1 on its own first, then link the result with in2 */
> +static void test_relink(const char *in1, const char *in2, const char *sec_name)
> +{
> + char out1[] = "/tmp/linked_externs.XXXXXX", out2[] = "/tmp/linked_externs.XXXXXX";
> + const struct btf_var_secinfo *vi;
> + struct bpf_object *obj = NULL;
> + const struct btf_type *t;
> + struct btf *btf;
> + int i, id, n;
> +
> + close(mkstemp(out1));
> + close(mkstemp(out2));
> +
> + if (!ASSERT_OK(link_objs(out1, in1, NULL), "link_stage1") ||
> + !ASSERT_OK(link_objs(out2, out1, in2), "link_stage2"))
> + goto cleanup;
> +
> + obj = bpf_object__open_file(out2, NULL);
> + if (!ASSERT_OK_PTR(obj, "obj_open"))
> + goto cleanup;
> +
> + btf = bpf_object__btf(obj);
> + id = btf__find_by_name_kind(btf, sec_name, BTF_KIND_DATASEC);
> + if (!ASSERT_GT(id, 0, "find_datasec"))
> + goto cleanup;
> +
> + t = btf__type_by_id(btf, id);
> + vi = btf_var_secinfos(t);
> + n = btf_vlen(t);
> + for (i = 0; i < n; i++) {
> + t = btf__type_by_id(btf, vi[i].type);
> + ASSERT_NEQ(btf_var(t)->linkage, BTF_VAR_GLOBAL_EXTERN, "var_resolved");
> + }
> +
> + ASSERT_OK(bpf_object__load(obj), "obj_load");
> +
> +cleanup:
> + bpf_object__close(obj);
> + unlink(out1);
> + unlink(out2);
> +}
> +
> +void test_linked_externs(void)
> +{
> + if (test__start_subtest("skel_arena"))
> + test_skel();
> + if (test__start_subtest("relink_arena"))
> + test_relink("linked_arena1.bpf.o", "linked_arena2.bpf.o", ".addr_space.1");
> + if (test__start_subtest("relink_maps"))
> + test_relink("linked_maps1.bpf.o", "linked_maps2.bpf.o", ".maps");
> +}
> diff --git a/tools/testing/selftests/bpf/progs/linked_arena1.c b/tools/testing/selftests/bpf/progs/linked_arena1.c
> new file mode 100644
> index 000000000000..3e8788dec1d9
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/linked_arena1.c
> @@ -0,0 +1,22 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
> +#include <vmlinux.h>
> +#include <bpf/bpf_helpers.h>
> +#include "bpf_arena_common.h"
> +
> +struct {
> + __uint(type, BPF_MAP_TYPE_ARENA);
> + __uint(map_flags, BPF_F_MMAPABLE);
> + __uint(max_entries, 1); /* number of pages */
> +} arena SEC(".maps");
> +
> +long __arena a_val = 1;
> +extern long __arena b_val; /* defined in linked_arena2.c */
> +
> +SEC("syscall")
> +int sum1(void *ctx)
> +{
> + return a_val + b_val;
> +}
> +
> +char _license[] SEC("license") = "GPL";
> diff --git a/tools/testing/selftests/bpf/progs/linked_arena2.c b/tools/testing/selftests/bpf/progs/linked_arena2.c
> new file mode 100644
> index 000000000000..d0bcdf18a4b6
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/linked_arena2.c
> @@ -0,0 +1,18 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
> +#include <vmlinux.h>
> +#include <bpf/bpf_helpers.h>
> +#include "bpf_arena_common.h"
> +
> +long __arena b_val = 2;
> +extern long __arena a_val; /* defined in linked_arena1.c */
> +
> +SEC("syscall")
> +int bump2(void *ctx)
> +{
> + a_val += 10;
> + b_val += 20;
> + return a_val + b_val;
> +}
> +
> +char _license[] SEC("license") = "GPL";
next prev parent reply other threads:[~2026-09-28 21:53 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-26 0:02 [PATCH v2 bpf-next 1/2] libbpf: Fix static linking of externs placed in allocated sections Andrii Nakryiko
2026-09-26 0:02 ` [PATCH v2 bpf-next 2/2] selftests/bpf: Add linked_externs test for externs " Andrii Nakryiko
2026-09-26 0:08 ` sashiko-bot
2026-09-26 0:50 ` bot+bpf-ci
2026-09-28 21:52 ` Vineet Gupta [this message]
2026-09-28 21:52 ` Vineet Gupta
2026-09-26 0:50 ` [PATCH v2 bpf-next 1/2] libbpf: Fix static linking of externs placed " bot+bpf-ci
2026-09-26 8:51 ` Alexei Starovoitov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=6cadba40-10bb-4acf-9a32-d22a270ada8d@linux.dev \
--to=gvineet@meta.com \
--cc=andrii@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=ihor.solodrai@linux.dev \
--cc=kernel-team@meta.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox