From: Ihor Solodrai <ihor.solodrai@linux.dev>
To: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: Alexei Starovoitov <ast@kernel.org>,
Andrii Nakryiko <andrii@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Eduard Zingerman <eddyz87@gmail.com>,
Kumar Kartikeya Dwivedi <memxor@gmail.com>,
Alan Maguire <alan.maguire@oracle.com>,
Jiri Olsa <jolsa@kernel.org>,
bpf@vger.kernel.org, linux-kbuild@vger.kernel.org
Subject: Re: [PATCH bpf-next v1 03/14] selftests/bpf: Fix resolve_btfids test reads of BTF ID sets in PIE builds
Date: Tue, 16 Jun 2026 13:15:31 -0700 [thread overview]
Message-ID: <2002c51b-4310-4825-abd1-0c69889eaf82@linux.dev> (raw)
In-Reply-To: <CAEf4Bzawb0qkNR3Cf5aQve5anGDvrfBX6g2B-B7RESdsvUVdvg@mail.gmail.com>
On 6/3/26 4:45 PM, Andrii Nakryiko wrote:
> On Mon, Jun 1, 2026 at 3:18 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>>
>> TL;DR
>>
>> On aarch64 with gcc toolchain, when test_progs is linked as a PIE,
>> reads of BTF ID array by C name return garbage because the GNU
>> assembler on aarch64 unconditionally folds .local symbol references
>> into section+addend form, and GOT slots cannot carry an addend per the
>> AArch64 ELF spec.
>>
>> Fix by marking the test's BTF ID objects with hidden visibility, which
>> makes gcc emit a direct access that bypasses the GOT entirely.
>>
>> Details below.
>>
>> The subsequent patches adding kfunc checks to resolve_btfids test may
>> cause test failures on aarch64 / gcc-15:
>>
>> test_resolve_btfids:FAIL:kfunc_set_flags actual 13 != expected 1
>> test_resolve_btfids:FAIL:kfunc_set_cnt actual 0 != expected 4
>>
>> The test defines its BTF ID sets with the same macros as the kernel
>> and reads them back directly by C name (in the same way as the kernel
>> code does).
>>
>> test_kfunc_set is a .local symbol emitted into .BTF_ids by inline asm
>> and declared to the compiler as a plain default-visibility
>> extern, that is:
>> extern struct btf_id_set8 test_kfunc_set;
>>
>> Depending on the build environment, test_progs may be linked as a
>> position-independent executable (for example, gcc defaults to -fpie
>> [1]). In a PIE, taking the address of a default-visibility extern is
>> routed through the GOT (Global Offset Table) [2].
>>
>> The GNU assembler's adjust_reloc_syms() pass (gas/write.c [3])
>> replaces references to local symbols with the corresponding section
>> symbol, folding the symbol's offset into the relocation addend. On
>> aarch64 this conversion is unconditional: tc_fix_adjustable() is
>> defined to 1 for all fixups (gas/config/tc-aarch64.h [4]), so even
>> GOT-generating relocations are subject to it. The resulting object
>> file therefore contains:
>>
>> R_AARCH64_ADR_GOT_PAGE .BTF_ids + 0x54
>> R_AARCH64_LD64_GOT_LO12_NC .BTF_ids + 0x54
>>
>> However, the AArch64 ELF specification mandates that GOT-generating
>> relocations must have a zero addend [5]. The +0x54 is therefore not
>> honored: the linker creates a GOT slot pointing at the .BTF_ids base,
>> and every access through that slot reads offset 0 instead of 0x54.
>>
>> This is purely a read-side problem, specific to the PIE test binary on
>> aarch64 with gcc toolchain. resolve_btfids patches the set header
>> correctly and the .BTF_ids bytes in test_progs are correct. vmlinux is
>> unaffected because it is built with -fno-PIE [6] and reaches .BTF_ids
>> with direct, addend-preserving relocations rather than the GOT. clang
>> is unaffected because LLVM's assembler retains the original symbol for
>> GOT relocations instead of converting to section+addend [7].
>>
>> To mitigate this issue, mark the test's BTF ID objects hidden with a
>> visibility pragma so that gcc treats them as non-interposable and
>> emits a direct access instead of a GOT load. This keeps the natural
>> by-name access, works in both PIE and non-PIE builds, and needs no
>> change to the BTF_ID macros or resolve_btfids.
>>
>> [1] https://gcc.gnu.org/onlinedocs/gnat_ugn/Position-Independent-Executable-PIE-Enabled-by-Default-on-Linux.html
>> [2] https://gcc.gnu.org/wiki/Visibility
>> [3] https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gas/write.c#l922
>> [4] https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gas/config/tc-aarch64.h#l279
>> [5] https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst#5733relocation-operations
>> [6] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Makefile?h=v7.1-rc6#n593
>> [7] https://github.com/llvm/llvm-project/blob/4b3bc46d1d794b8ed78b75ccd35a6cc30235bf31/llvm/lib/MC/ELFObjectWriter.cpp#L1213-L1224
>>
>
> fascinating! but you also missed adding your SOB
>
> Acked-by: Andrii Nakryiko <andrii@kernel.org>
>
>
>
>> ---
>> ---
>> .../selftests/bpf/prog_tests/resolve_btfids.c | 18 ++++++++++++++++++
>> 1 file changed, 18 insertions(+)
>>
>> diff --git a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
>> index d742ecb2ca8e..d0e51979d455 100644
>> --- a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
>> +++ b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
>> @@ -32,6 +32,23 @@ asm (
>> ".balign 4, 0; \n"
>> ".popsection; \n");
>>
>> +/*
>> + * The BTF ID arrays below are .local symbols placed in .BTF_ids by
>> + * inline asm, and are read here directly by C name. To the compiler
>> + * they are plain, default-visibility extern objects.
>> + *
>> + * When test_progs is linked as a position-independent executable (PIE),
>> + * taking the address of such an extern is routed through the GOT. The
>> + * GNU assembler on aarch64 unconditionally converts references to
>> + * .local symbols into section + addend form (".BTF_ids + <offset>"),
>> + * but a GOT slot cannot carry an addend (the AArch64 ELF spec mandates
>> + * zero), so the linker resolves it to the .BTF_ids base.
>> + *
>> + * Mark these symbols hidden so the compiler treats them as
>> + * non-interposable and emits a direct, addend-preserving PC-relative
>> + * access instead of a GOT load, in both PIE and non-PIE builds.
>> + */
>> +#pragma GCC visibility push(hidden)
>> BTF_ID_LIST(test_list_local)
>> BTF_ID_UNUSED
>> BTF_ID(typedef, S)
>> @@ -60,6 +77,7 @@ BTF_ID(union, U)
>> BTF_ID(func, func)
>> BTF_SET_END(test_set)
>>
>> +#pragma GCC visibility pop
>
> nit: empty line after pragma, no empty line before pragma so it "hugs"
> relevant pieces of code instead?
>
>
> but also, can't we just use BTF_ID_LIST_GLOBAL instead to avoid all this?
Most of the BTF_ID_SET/LIST usages in the kernel tree are through
BTF_KFUNCS_START, which are local. And from the point of view of the
resolve_btfids test, we want to test both global and local symbols
resolution and macros.
We'd have to migrate everything to global to completely sidestep this.
Also the problem is specific to how the test works, and pragma fixes
it, so I think we can just leave it at that.
>
>
>> static int
>> __resolve_symbol(struct btf *btf, int type_id)
>> {
>> --
>> 2.54.0
>>
next prev parent reply other threads:[~2026-06-16 20:15 UTC|newest]
Thread overview: 50+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-01 22:17 [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Ihor Solodrai
2026-06-01 22:17 ` [PATCH bpf-next v1 01/14] tools/bpf: Sync btf_ids.h to tools Ihor Solodrai
2026-06-16 6:28 ` Emil Tsalapatis
2026-06-01 22:17 ` [PATCH bpf-next v1 02/14] selftests/bpf: Modernize resolve_btfids test scaffolding Ihor Solodrai
2026-06-02 13:02 ` Jiri Olsa
2026-06-02 18:30 ` Ihor Solodrai
2026-06-16 6:33 ` Emil Tsalapatis
2026-06-01 22:17 ` [PATCH bpf-next v1 03/14] selftests/bpf: Fix resolve_btfids test reads of BTF ID sets in PIE builds Ihor Solodrai
2026-06-03 23:45 ` Andrii Nakryiko
2026-06-16 20:15 ` Ihor Solodrai [this message]
2026-06-16 6:53 ` Emil Tsalapatis
2026-06-01 22:17 ` [PATCH bpf-next v1 04/14] selftests/bpf: Add kfunc set test to resolve_btfids Ihor Solodrai
2026-06-02 13:02 ` Jiri Olsa
2026-06-03 23:45 ` Andrii Nakryiko
2026-06-16 7:07 ` Emil Tsalapatis
2026-06-16 18:33 ` Alexei Starovoitov
2026-06-16 21:52 ` Ihor Solodrai
2026-06-01 22:17 ` [PATCH bpf-next v1 05/14] resolve_btfids: Index BTF ID symbols by address Ihor Solodrai
2026-06-01 23:03 ` bot+bpf-ci
2026-06-02 13:01 ` Jiri Olsa
2026-06-02 18:28 ` Ihor Solodrai
2026-06-03 23:45 ` Andrii Nakryiko
2026-06-16 21:47 ` Ihor Solodrai
2026-06-16 18:45 ` Emil Tsalapatis
2026-06-16 21:53 ` Ihor Solodrai
2026-06-01 22:17 ` [PATCH bpf-next v1 06/14] resolve_btfids: Discover kfuncs from BTF ID sets Ihor Solodrai
2026-06-02 20:36 ` Jiri Olsa
2026-06-02 21:08 ` Ihor Solodrai
2026-06-03 23:45 ` Andrii Nakryiko
2026-06-03 23:45 ` Andrii Nakryiko
2026-06-16 21:49 ` Ihor Solodrai
2026-06-01 22:17 ` [PATCH bpf-next v1 07/14] resolve_btfids: Emit bpf_kfunc BTF decl tag for discovered kfuncs Ihor Solodrai
2026-06-03 23:45 ` Andrii Nakryiko
2026-06-01 22:17 ` [PATCH bpf-next v1 08/14] selftests/bpf: Verify bpf_kfunc decl tag emission in resolve_btfids Ihor Solodrai
2026-06-01 22:18 ` [PATCH bpf-next v1 09/14] resolve_btfids: Emit a decl tag for kfuncs with KF_FASTCALL Ihor Solodrai
2026-06-01 22:18 ` [PATCH bpf-next v1 10/14] selftests/bpf: Verify bpf_fastcall decl tags in resolve_btfids test Ihor Solodrai
2026-06-03 23:47 ` Andrii Nakryiko
2026-06-01 22:18 ` [PATCH bpf-next v1 11/14] resolve_btfids: Process KF_ARENA_* flags in resolve_btfids Ihor Solodrai
2026-06-03 23:47 ` Andrii Nakryiko
2026-06-16 19:51 ` Emil Tsalapatis
2026-06-16 20:36 ` Emil Tsalapatis
2026-06-16 21:58 ` Ihor Solodrai
2026-06-01 22:18 ` [PATCH bpf-next v1 12/14] selftests/bpf: Verify arena type tags in resolve_btfids test Ihor Solodrai
2026-06-03 23:46 ` Andrii Nakryiko
2026-06-01 22:18 ` [PATCH bpf-next v1 13/14] kbuild: Drop decl_tag_kfuncs and attributes from pahole flags Ihor Solodrai
2026-06-03 23:48 ` Andrii Nakryiko
2026-06-01 22:18 ` [PATCH bpf-next v1 14/14] docs, resolve_btfids: Document kfunc BTF annotation emission Ihor Solodrai
2026-06-16 19:54 ` Emil Tsalapatis
2026-06-03 23:45 ` [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Andrii Nakryiko
2026-06-16 20:10 ` Ihor Solodrai
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=2002c51b-4310-4825-abd1-0c69889eaf82@linux.dev \
--to=ihor.solodrai@linux.dev \
--cc=alan.maguire@oracle.com \
--cc=andrii.nakryiko@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=jolsa@kernel.org \
--cc=linux-kbuild@vger.kernel.org \
--cc=memxor@gmail.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