diff mbox series

arm64: Force position-independent veneers

Message ID 20240927101838.3061054-1-mark.rutland@arm.com (mailing list archive)
State New, archived
Headers show
Series arm64: Force position-independent veneers | expand

Commit Message

Mark Rutland Sept. 27, 2024, 10:18 a.m. UTC
Certain portions of code always need to be position-independent
regardless of CONFIG_RELOCATABLE, including code which is executed in an
idmap or which is executed before relocations are applied. In some
kernel configurations the LLD linker generates position-dependent
veneers for such code, and when executed these result in early boot-time
failures.

Marc Zyngier encountered a boot failure resulting from this when
building a (particularly cursed) configuration with LLVM, as he reported
to the list:

  https://lore.kernel.org/linux-arm-kernel/86wmjwvatn.wl-maz@kernel.org/

In Marc's kernel configuration, the .head.text and .rodata.text sections
end up more than 128MiB apart, requiring a veneer to branch between the
two:

| [mark@lakrids:~/src/linux]% usekorg 14.1.0 aarch64-linux-objdump -t vmlinux | grep -w _text
| ffff800080000000 g       .head.text     0000000000000000 _text
| [mark@lakrids:~/src/linux]% usekorg 14.1.0 aarch64-linux-objdump -t vmlinux | grep -w primary_entry
| ffff8000889df0e0 g       .rodata.text   000000000000006c primary_entry,

... consequently, LLD inserts a position-dependent veneer for the branch
from _stext (in .head.text) to primary_entry (in .rodata.text):

| ffff800080000000 <_text>:
| ffff800080000000:       fa405a4d        ccmp    x18, #0x0, #0xd, pl     // pl = nfrst
| ffff800080000004:       14003fff        b       ffff800080010000 <__AArch64AbsLongThunk_primary_entry>
...
| ffff800080010000 <__AArch64AbsLongThunk_primary_entry>:
| ffff800080010000:       58000050        ldr     x16, ffff800080010008 <__AArch64AbsLongThunk_primary_entry+0x8>
| ffff800080010004:       d61f0200        br      x16
| ffff800080010008:       889df0e0        .word   0x889df0e0
| ffff80008001000c:       ffff8000        .word   0xffff8000

... and as this is executed early in boot before the kernel is mapped in
TTBR1 this results in a silent boot failure.

Fix this by passing '--pic-veneer' to the linker, which will cause the
linker to use position-independent veneers, e.g.

| ffff800080000000 <_text>:
| ffff800080000000:       fa405a4d        ccmp    x18, #0x0, #0xd, pl     // pl = nfrst
| ffff800080000004:       14003fff        b       ffff800080010000 <__AArch64ADRPThunk_primary_entry>
...
| ffff800080010000 <__AArch64ADRPThunk_primary_entry>:
| ffff800080010000:       f004e3f0        adrp    x16, ffff800089c8f000 <__idmap_text_start>
| ffff800080010004:       91038210        add     x16, x16, #0xe0
| ffff800080010008:       d61f0200        br      x16

I've opted to pass '--pic-veneer' unconditionally, as:

* In addition to solving the boot failure, these sequences are generally
  nicer as they require fewer instructions and don't need to perform
  data accesses.

* While the position-independent veneer sequences have a limited +/-2GiB
  range, this is not a new restriction. Even kernels built with
  CONFIG_RELOCATABLE=n are limited to 2GiB in size as we have several
  structues using 32-bit relative offsets and PPREL32 relocations, which
  are similarly limited to +/-2GiB in range. These include extable
  entries, jump table entries, and alt_instr entries.

* GNU LD defaults to using position-independent veneers, and supports
  the same '--pic-veneer' option, so this change is not expected to
  adversely affect GNU LD.

I've tested with GNU LD 2.30 to 2.42 inclusive and LLVM 13.0.1 to 19.1.0
inclusive, using the kernel.org binaries from:

* https://mirrors.edge.kernel.org/pub/tools/crosstool/
* https://mirrors.edge.kernel.org/pub/tools/llvm/

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Reported-by: Marc Zyngier <maz@kernel.org>
Cc: Ard Biesheuvel <ardb@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: Nick Desaulniers <ndesaulniers@google.com>
Cc: Will Deacon <will@kernel.org>
---
 arch/arm64/Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Comments

Ard Biesheuvel Sept. 27, 2024, 10:42 a.m. UTC | #1
On Fri, 27 Sept 2024 at 12:18, Mark Rutland <mark.rutland@arm.com> wrote:
>
> Certain portions of code always need to be position-independent
> regardless of CONFIG_RELOCATABLE, including code which is executed in an
> idmap or which is executed before relocations are applied. In some
> kernel configurations the LLD linker generates position-dependent
> veneers for such code, and when executed these result in early boot-time
> failures.
>
...
>
> I've opted to pass '--pic-veneer' unconditionally, as:
>
> * In addition to solving the boot failure, these sequences are generally
>   nicer as they require fewer instructions and don't need to perform
>   data accesses.
>

Yeah, and accessing literal pools interspersed with the code. I find
it rather bizarre that --pic-veneer is not the default when using the
small code model.

...
>
> Signed-off-by: Mark Rutland <mark.rutland@arm.com>
> Reported-by: Marc Zyngier <maz@kernel.org>
> Cc: Ard Biesheuvel <ardb@kernel.org>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Nathan Chancellor <nathan@kernel.org>
> Cc: Nick Desaulniers <ndesaulniers@google.com>
> Cc: Will Deacon <will@kernel.org>
> ---
>  arch/arm64/Makefile | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>

Acked-by: Ard Biesheuvel <ardb@kernel.org>
Nathan Chancellor Sept. 27, 2024, 1:49 p.m. UTC | #2
On Fri, Sep 27, 2024 at 11:18:38AM +0100, Mark Rutland wrote:
> Certain portions of code always need to be position-independent
> regardless of CONFIG_RELOCATABLE, including code which is executed in an
> idmap or which is executed before relocations are applied. In some
> kernel configurations the LLD linker generates position-dependent
> veneers for such code, and when executed these result in early boot-time
> failures.
> 
> Marc Zyngier encountered a boot failure resulting from this when
> building a (particularly cursed) configuration with LLVM, as he reported
> to the list:

Heh, I admire the "particularly cursed" bit.

> I've tested with GNU LD 2.30 to 2.42 inclusive and LLVM 13.0.1 to 19.1.0
> inclusive, using the kernel.org binaries from:
> 
> * https://mirrors.edge.kernel.org/pub/tools/crosstool/
> * https://mirrors.edge.kernel.org/pub/tools/llvm/
> 
> Signed-off-by: Mark Rutland <mark.rutland@arm.com>
> Reported-by: Marc Zyngier <maz@kernel.org>
> Cc: Ard Biesheuvel <ardb@kernel.org>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Nathan Chancellor <nathan@kernel.org>
> Cc: Nick Desaulniers <ndesaulniers@google.com>
> Cc: Will Deacon <will@kernel.org>

Thanks for the patch!

Reviewed-by: Nathan Chancellor <nathan@kernel.org>

> ---
>  arch/arm64/Makefile | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
> index f6bc3da1ef110..c8e237b20ef29 100644
> --- a/arch/arm64/Makefile
> +++ b/arch/arm64/Makefile
> @@ -10,7 +10,7 @@
>  #
>  # Copyright (C) 1995-2001 by Russell King
>  
> -LDFLAGS_vmlinux	:=--no-undefined -X
> +LDFLAGS_vmlinux	:=--no-undefined -X --pic-veneer
>  
>  ifeq ($(CONFIG_RELOCATABLE), y)
>  # Pass --no-apply-dynamic-relocs to restore pre-binutils-2.27 behaviour
> -- 
> 2.30.2
>
Catalin Marinas Oct. 1, 2024, 1:03 p.m. UTC | #3
On Fri, 27 Sep 2024 11:18:38 +0100, Mark Rutland wrote:
> Certain portions of code always need to be position-independent
> regardless of CONFIG_RELOCATABLE, including code which is executed in an
> idmap or which is executed before relocations are applied. In some
> kernel configurations the LLD linker generates position-dependent
> veneers for such code, and when executed these result in early boot-time
> failures.
> 
> [...]

Applied to arm64 (for-next/fixes), thanks!

[1/1] arm64: Force position-independent veneers
      https://git.kernel.org/arm64/c/9abe390e689f
diff mbox series

Patch

diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
index f6bc3da1ef110..c8e237b20ef29 100644
--- a/arch/arm64/Makefile
+++ b/arch/arm64/Makefile
@@ -10,7 +10,7 @@ 
 #
 # Copyright (C) 1995-2001 by Russell King
 
-LDFLAGS_vmlinux	:=--no-undefined -X
+LDFLAGS_vmlinux	:=--no-undefined -X --pic-veneer
 
 ifeq ($(CONFIG_RELOCATABLE), y)
 # Pass --no-apply-dynamic-relocs to restore pre-binutils-2.27 behaviour