Re: [PATCH 03/12] arm64/bti: Fix BTI linker failures with long branches into .idmap.text

From: Ard Biesheuvel

Date: Mon Aug 17 2026 - 07:06:10 EST


Hi Josh,

On Sat, 15 Aug 2026, at 07:45, Josh Poimboeuf wrote:
> On a kernel whose text exceeds the +/128MB direct branch range, the
> linker inserts veneers. With BTI enabled, the veneers' indirect branch
> targets need a BTI landing pad, which not all functions have starting
> with Clang 21 (and for all versions of GCC).
>
> In such cases the linker can emit a second veneer close to the target
> which has the landing pad along with a direct branch to the target. But
> a long branch to .idmap.text never gets one because it's missing the
> executable section flag.
>
> With the LLVM linker, it's a silent failure, presumably only discovered
> by a BTI exception at runtime. With the GNU linker it's even worse, as
> it dereferences the missing stub/veneer group entry and seg faults (this
> was how I discovered it).
>
> Make sure the section is executable by adding the "x" flag to all the
> creators of the input section.
>
> Also manually add "bti c" to primary_entry() and enter_vhe(), otherwise
> the linker-generated veneer page pushes the .idmap.text past its
> asserted 4KB size:
>
> ld.bfd: ID map text too big or misaligned
>
> Link: https://sourceware.org/bugzilla/show_bug.cgi?id=34525
> Signed-off-by: Josh Poimboeuf <jpoimboe@xxxxxxxxxx>
> ---
> arch/arm64/kernel/cpu-reset.S | 2 +-
> arch/arm64/kernel/head.S | 7 ++++---
> arch/arm64/kernel/hyp-stub.S | 1 +
> arch/arm64/kernel/sleep.S | 2 +-
> arch/arm64/mm/proc.S | 8 ++++----
> 5 files changed, 11 insertions(+), 9 deletions(-)
>
> diff --git a/arch/arm64/kernel/cpu-reset.S b/arch/arm64/kernel/cpu-reset.S
> index c87445dde6745..9943a7c70f6b0 100644
> --- a/arch/arm64/kernel/cpu-reset.S
> +++ b/arch/arm64/kernel/cpu-reset.S
> @@ -14,7 +14,7 @@
> #include <asm/virt.h>
>
> .text
> -.pushsection .idmap.text, "a"
> +.pushsection .idmap.text, "ax"
>

We've had issues in the past with this change.

The problem is that the ID map is restricted to a single page, and
[some versions of] the GNU linker concatenate generated veneers onto
whichever executable input section came last in the input, which was
this one at that point.

That resulted in the ID map start/end boundaries being placed further
apart than the linker asserts would tolerate, even though the generated
code in question would never be called via the 1:1 mapping.

Not sure whether that problem has simply disappeared by now, or older
versions of the linker may still trigger it. But it is something to be
aware of.