Skip to content

Commit

Permalink
[LLD][ELF][ARM] Do not insert interworking thunks for non STT_FUNC sy…
Browse files Browse the repository at this point in the history
…mbols

ELF for the ARM architecture requires linkers to provide
interworking for symbols that are of type STT_FUNC. Interworking for
other symbols must be encoded directly in the object file. LLD was always
providing interworking, regardless of the symbol type, this breaks some
programs that have branches from Thumb state targeting STT_NOTYPE symbols
that have bit 0 clear, but they are in fact internal labels in a Thumb
function. LLD treats these symbols as ARM and inserts a transition to Arm.

This fixes the problem for in range branches, R_ARM_JUMP24,
R_ARM_THM_JUMP24 and R_ARM_THM_JUMP19. This is expected to be the vast
majority of problem cases as branching to an internal label close to the
function.

There is at least one follow up patch required.
- R_ARM_CALL and R_ARM_THM_CALL may do interworking via BL/BLX
  substitution.

In theory range-extension thunks can be altered to not change state when
the symbol type is not STT_FUNC. I will need to check with ld.bfd to see if
this is the case in practice.

Fixes (part of) ClangBuiltLinux/linux#773

Differential Revision: https://reviews.llvm.org/D73474

(cherry picked from commit 4f38ab2)
  • Loading branch information
smithp35 authored and zmodem committed Jan 29, 2020
1 parent 5777899 commit 81d73c6
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 4 deletions.
8 changes: 4 additions & 4 deletions lld/ELF/Arch/ARM.cpp
Expand Up @@ -275,8 +275,8 @@ bool ARM::needsThunk(RelExpr expr, RelType type, const InputFile *file,
case R_ARM_PLT32:
case R_ARM_JUMP24:
// Source is ARM, all PLT entries are ARM so no interworking required.
// Otherwise we need to interwork if Symbol has bit 0 set (Thumb).
if (expr == R_PC && ((s.getVA() & 1) == 1))
// Otherwise we need to interwork if STT_FUNC Symbol has bit 0 set (Thumb).
if (s.isFunc() && expr == R_PC && (s.getVA() & 1))
return true;
LLVM_FALLTHROUGH;
case R_ARM_CALL: {
Expand All @@ -286,8 +286,8 @@ bool ARM::needsThunk(RelExpr expr, RelType type, const InputFile *file,
case R_ARM_THM_JUMP19:
case R_ARM_THM_JUMP24:
// Source is Thumb, all PLT entries are ARM so interworking is required.
// Otherwise we need to interwork if Symbol has bit 0 clear (ARM).
if (expr == R_PLT_PC || ((s.getVA() & 1) == 0))
// Otherwise we need to interwork if STT_FUNC Symbol has bit 0 clear (ARM).
if (expr == R_PLT_PC || (s.isFunc() && (s.getVA() & 1) == 0))
return true;
LLVM_FALLTHROUGH;
case R_ARM_THM_CALL: {
Expand Down
52 changes: 52 additions & 0 deletions lld/test/ELF/arm-thumb-interwork-ifunc.s
@@ -0,0 +1,52 @@
// REQUIRES: arm
// RUN: llvm-mc --triple=armv7a-linux-gnueabihf -arm-add-build-attributes -filetype=obj -o %t.o %s
// RUN: ld.lld %t.o -o %t
// RUN: llvm-objdump -triple armv7a-none-linux-gnueabi -d --no-show-raw-insn %t

/// Non-preemptible ifuncs are called via a PLT entry which is always Arm
/// state, expect the ARM callers to go direct to the PLT entry, Thumb
/// branches are indirected via state change thunks, the bl is changed to blx.

.syntax unified
.text
.balign 0x1000
.type foo STT_GNU_IFUNC
.globl foo
foo:
bx lr

.section .text.1, "ax", %progbits
.arm
.global _start
_start:
b foo
bl foo

.section .text.2, "ax", %progbits
.thumb
.global thumb_caller
thumb_caller:
b foo
b.w foo
bl foo

// CHECK: 00012004 _start:
// CHECK-NEXT: b #36
// CHECK-NEXT: bl #32

// CHECK: 0001200c thumb_caller:
// CHECK-NEXT: b.w #8
// CHECK-NEXT: b.w #4
// CHECK-NEXT: blx #24

// CHECK: 00012018 __Thumbv7ABSLongThunk_foo:
// CHECK-NEXT: movw r12, #8240
// CHECK-NEXT: movt r12, #1
// CHECK-NEXT: bx r12

// CHECK: Disassembly of section .iplt:

// CHECK: 00012030 $a:
// CHECK-NEXT: add r12, pc, #0, #12
// CHECK-NEXT: add r12, r12, #4096
// CHECK-NEXT: ldr pc, [r12, #8]!
73 changes: 73 additions & 0 deletions lld/test/ELF/arm-thumb-interwork-notfunc.s
@@ -0,0 +1,73 @@
// REQUIRES: arm
// RUN: llvm-mc --triple=armv7a-linux-gnueabihf -arm-add-build-attributes -filetype=obj -o %t.o %s
// RUN: ld.lld %t.o -o %t
// RUN: llvm-objdump --no-show-raw-insn -d %t | FileCheck %s

.syntax unified
.section .arm_target, "ax", %progbits
.balign 0x1000
.arm
arm_func_with_notype:
.type arm_func_with_explicit_notype, %notype
arm_func_with_explicit_notype:
bx lr

.section .thumb_target, "ax", %progbits
.balign 4
.thumb
thumb_func_with_notype:
.type thumb_func_with_explicit_notype, %notype
thumb_func_with_explicit_notype:
bx lr

/// All the symbols that are targets of the branch relocations do not have
/// type STT_FUNC. LLD should not insert interworking thunks as non STT_FUNC
/// symbols have no state information, the ABI assumes the user has manually
/// done the interworking.
.section .arm_caller, "ax", %progbits
.balign 4
.arm
.global _start
_start:
b .arm_target
b arm_func_with_notype
b arm_func_with_explicit_notype
b .thumb_target
b thumb_func_with_notype
b thumb_func_with_explicit_notype

.section .thumb_caller, "ax", %progbits
.thumb
.balign 4
b.w .arm_target
b.w arm_func_with_notype
b.w arm_func_with_explicit_notype
b.w .thumb_target
b.w thumb_func_with_notype
b.w thumb_func_with_explicit_notype
beq.w .arm_target
beq.w arm_func_with_notype
beq.w arm_func_with_explicit_notype
beq.w .thumb_target
beq.w thumb_func_with_notype
beq.w thumb_func_with_explicit_notype

// CHECK: 00012008 _start:
// CHECK-NEXT: 12008: b #-16
// CHECK-NEXT: 1200c: b #-20
// CHECK-NEXT: 12010: b #-24
// CHECK-NEXT: 12014: b #-24
// CHECK-NEXT: 12018: b #-28
// CHECK-NEXT: 1201c: b #-32
// CHECK: 12020: b.w #-36
// CHECK-NEXT: 12024: b.w #-40
// CHECK-NEXT: 12028: b.w #-44
// CHECK-NEXT: 1202c: b.w #-44
// CHECK-NEXT: 12030: b.w #-48
// CHECK-NEXT: 12034: b.w #-52
// CHECK-NEXT: 12038: beq.w #-60
// CHECK-NEXT: 1203c: beq.w #-64
// CHECK-NEXT: 12040: beq.w #-68
// CHECK-NEXT: 12044: beq.w #-68
// CHECK-NEXT: 12048: beq.w #-72
// CHECK-NEXT: 1204c: beq.w #-76
1 change: 1 addition & 0 deletions lld/test/ELF/arm-thunk-edgecase.s
Expand Up @@ -18,6 +18,7 @@
.arm
.section .text_armfunc, "ax", %progbits
.globl armfunc
.type armfunc, %function
armfunc:
b thumbfunc

Expand Down

0 comments on commit 81d73c6

Please sign in to comment.