Index: ELF/Arch/ARM.cpp =================================================================== --- ELF/Arch/ARM.cpp +++ ELF/Arch/ARM.cpp @@ -39,7 +39,7 @@ void addPltSymbols(InputSectionBase *IS, uint64_t Off) const override; void addPltHeaderSymbols(InputSectionBase *ISD) const override; bool needsThunk(RelExpr Expr, uint32_t RelocType, const InputFile *File, - const SymbolBody &S) const override; + uint64_t BranchAddr, const SymbolBody &S) const override; bool inBranchRange(uint32_t RelocType, uint64_t Src, uint64_t Dst) const override; void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override; @@ -206,7 +206,7 @@ } bool ARM::needsThunk(RelExpr Expr, uint32_t RelocType, const InputFile *File, - const SymbolBody &S) const { + uint64_t BranchAddr, const SymbolBody &S) const { // If S is an undefined weak symbol in an executable we don't need a Thunk. // In a DSO calls to undefined symbols, including weak ones get PLT entries // which may need a thunk. @@ -224,14 +224,22 @@ // Otherwise we need to interwork if Symbol has bit 0 set (Thumb). if (Expr == R_PC && ((S.getVA() & 1) == 1)) return true; - break; + // Fall through + case R_ARM_CALL: { + uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA(); + return !inBranchRange(RelocType, BranchAddr, Dst); + } 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)) return true; - break; + // Fall through + case R_ARM_THM_CALL: { + uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA(); + return !inBranchRange(RelocType, BranchAddr, Dst); + } } return false; } Index: ELF/Arch/Mips.cpp =================================================================== --- ELF/Arch/Mips.cpp +++ ELF/Arch/Mips.cpp @@ -38,7 +38,7 @@ void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; bool needsThunk(RelExpr Expr, uint32_t RelocType, const InputFile *File, - const SymbolBody &S) const override; + uint64_t BranchAddr, const SymbolBody &S) const override; void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override; bool usesOnlyLowPageBits(uint32_t Type) const override; }; @@ -229,7 +229,7 @@ template bool MIPS::needsThunk(RelExpr Expr, uint32_t Type, const InputFile *File, - const SymbolBody &S) const { + uint64_t BranchAddr, const SymbolBody &S) const { // Any MIPS PIC code function is invoked with its address in register $t9. // So if we have a branch instruction from non-PIC code to the PIC one // we cannot make the jump directly and need to create a small stubs Index: ELF/Relocations.h =================================================================== --- ELF/Relocations.h +++ ELF/Relocations.h @@ -133,15 +133,17 @@ private: void mergeThunks(); - ThunkSection *getISRThunkSec(OutputSection *OS, - std::vector *ISR); + ThunkSection *getISRThunkSec(OutputSection *OS, InputSection *IS, + std::vector *ISR, uint32_t Type, + uint64_t Src); ThunkSection *getISThunkSec(InputSection *IS, OutputSection *OS); void createInitialThunkSections(ArrayRef OutputSections); void forEachExecInputSectionRange( ArrayRef OutputSections, std::function *)> Fn); - std::pair getThunk(SymbolBody &Body, uint32_t Type); + std::pair getThunk(SymbolBody &Body, uint32_t Type, + uint64_t Src); ThunkSection *addThunkSection(OutputSection *OS, std::vector *, uint64_t Off); // Record all the available Thunks for a Symbol Index: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ ELF/Relocations.cpp @@ -1011,19 +1011,22 @@ // Find or create a ThunkSection within the InputSectionRange (ISR) that is in // range of Src. An ISR maps to a range of InputSections described by a // linker script section pattern such as { .text .text.* }. -// FIXME: At present we assume that all ThunkSections are in range so we always -// return the first pre-created ThunkSection. -ThunkSection *ThunkCreator::getISRThunkSec(OutputSection *OS, - std::vector *ISR) { - // FIXME: When range extension thunks are supported we will need to check - // that the ThunkSection is in range of the caller - if (!ThunkSections[ISR].empty()) - return ThunkSections[ISR].front(); - - // FIXME: When range extension thunks are supported we must handle the case - // where no pre-created ThunkSections are in range by creating a new one in - // range for now it is unreachable - llvm_unreachable("Must have created at least one ThunkSection per ISR"); +ThunkSection *ThunkCreator::getISRThunkSec(OutputSection *OS, InputSection *IS, + std::vector *ISR, + uint32_t Type, uint64_t Src) { + // Use the simple heuristic of inserting the Thunk in the first ThunkSection + // in range of the caller. + for (ThunkSection *TS : ThunkSections[ISR]) { + uint64_t TSBase = OS->Addr + TS->OutSecOff; + uint64_t TSLimit = TSBase + TS->getSize(); + if (Target->inBranchRange(Type, Src, (Src > TSLimit) ? TSBase : TSLimit)) + return TS; + } + + // No suitable ThunkSection exists. This can happen when there is a branch + // with lower range than the ThunkSection spacing or when there are too + // many Thunks. Create a new ThunkSection. + return addThunkSection(OS, ISR, IS->OutSecOff); } ThunkSection *ThunkCreator::getISThunkSec(InputSection *IS, OutputSection *OS) { @@ -1104,13 +1107,14 @@ return TS; } -std::pair ThunkCreator::getThunk(SymbolBody &Body, - uint32_t Type) { +std::pair ThunkCreator::getThunk(SymbolBody &Body, uint32_t Type, + uint64_t Src) { auto Res = ThunkedSymbols.insert({&Body, std::vector()}); if (!Res.second) { // Check existing Thunks for Body to see if they can be reused for (Thunk *ET : Res.first->second) - if (ET->isCompatibleWith(Type)) + if (ET->isCompatibleWith(Type) && + Target->inBranchRange(Type, Src, ET->ThunkSym->getVA())) return std::make_pair(ET, false); } // No existing compatible Thunk in range, create a new one @@ -1143,8 +1147,7 @@ // finalized. If any Thunks are added to an OutputSection the output section // offsets of the InputSections will change. // -// FIXME: All Thunks are assumed to be in range of the relocation. Range -// extension Thunks are not yet supported. +// FIXME: Initial support for RangeThunks, only one pass supported bool ThunkCreator::createThunks( ArrayRef OutputSections) { if (Pass > 0) @@ -1163,19 +1166,20 @@ for (InputSection *IS : *ISR) for (Relocation &Rel : IS->Relocations) { SymbolBody &Body = *Rel.Sym; + uint64_t Src = OS->Addr + IS->OutSecOff + Rel.Offset; if (Thunks.find(&Body) != Thunks.end() || - !Target->needsThunk(Rel.Expr, Rel.Type, IS->File, Body)) + !Target->needsThunk(Rel.Expr, Rel.Type, IS->File, Src, Body)) continue; Thunk *T; bool IsNew; - std::tie(T, IsNew) = getThunk(Body, Rel.Type); + std::tie(T, IsNew) = getThunk(Body, Rel.Type, Src); if (IsNew) { // Find or create a ThunkSection for the new Thunk ThunkSection *TS; if (auto *TIS = T->getTargetInputSection()) TS = getISThunkSec(TIS, OS); else - TS = getISRThunkSec(OS, ISR); + TS = getISRThunkSec(OS, IS, ISR, Rel.Type, Src); TS->addThunk(T); Thunks[T->ThunkSym] = T; } Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -50,7 +50,8 @@ // Decide whether a Thunk is needed for the relocation from File // targeting S. virtual bool needsThunk(RelExpr Expr, uint32_t RelocType, - const InputFile *File, const SymbolBody &S) const; + const InputFile *File, uint64_t BranchAddr, + const SymbolBody &S) const; // Return true if we can reach Dst from Src with Relocation RelocType virtual bool inBranchRange(uint32_t RelocType, uint64_t Src, uint64_t Dst) const; Index: ELF/Target.cpp =================================================================== --- ELF/Target.cpp +++ ELF/Target.cpp @@ -124,7 +124,8 @@ bool TargetInfo::usesOnlyLowPageBits(uint32_t Type) const { return false; } bool TargetInfo::needsThunk(RelExpr Expr, uint32_t RelocType, - const InputFile *File, const SymbolBody &S) const { + const InputFile *File, uint64_t BranchAddr, + const SymbolBody &S) const { return false; } Index: ELF/Thunks.cpp =================================================================== --- ELF/Thunks.cpp +++ ELF/Thunks.cpp @@ -250,11 +250,13 @@ case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_JUMP24: + case R_ARM_CALL: if (Config->Pic) return make(S); return make(S); case R_ARM_THM_JUMP19: case R_ARM_THM_JUMP24: + case R_ARM_THM_CALL: if (Config->Pic) return make(S); return make(S); Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -1311,6 +1311,7 @@ if (TC.createThunks(OutputSectionCommands)) { applySynthetic({InX::MipsGot}, [](SyntheticSection *SS) { SS->updateAllocSize(); }); + Script->assignAddresses(); if (TC.createThunks(OutputSectionCommands)) fatal("All non-range thunks should be created in first call"); } Index: test/ELF/arm-branch-error.s =================================================================== --- test/ELF/arm-branch-error.s +++ /dev/null @@ -1,19 +0,0 @@ -// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t -// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %S/Inputs/far-arm-abs.s -o %tfar -// RUN: not ld.lld %t %tfar -o %t2 2>&1 | FileCheck %s -// REQUIRES: arm - .syntax unified - .section .text, "ax",%progbits - .globl _start - .balign 0x10000 - .type _start,%function -_start: - // address of too_far symbols are just out of range of ARM branch with - // 26-bit immediate field and an addend of -8 - bl too_far1 - b too_far2 - beq too_far3 - -// CHECK: R_ARM_CALL out of range -// CHECK-NEXT: R_ARM_JUMP24 out of range -// CHECK-NEXT: R_ARM_JUMP24 out of range Index: test/ELF/arm-branch-rangethunk.s =================================================================== --- /dev/null +++ test/ELF/arm-branch-rangethunk.s @@ -0,0 +1,34 @@ +// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t +// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %S/Inputs/far-arm-abs.s -o %tfar +// RUN: ld.lld %t %tfar -o %t2 2>&1 +// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t2 | FileCheck %s +// REQUIRES: arm + .syntax unified + .section .text, "ax",%progbits + .globl _start + .balign 0x10000 + .type _start,%function +_start: + // address of too_far symbols are just out of range of ARM branch with + // 26-bit immediate field and an addend of -8 + bl too_far1 + b too_far2 + beq too_far3 + +// CHECK: Disassembly of section .text: +// CHECK-NEXT: _start: +// CHECK-NEXT: 20000: 01 00 00 eb bl #4 <__ARMv7ABSLongThunk_too_far1> +// CHECK-NEXT: 20004: 03 00 00 ea b #12 <__ARMv7ABSLongThunk_too_far2> +// CHECK-NEXT: 20008: 05 00 00 0a beq #20 <__ARMv7ABSLongThunk_too_far3> +// CHECK: __ARMv7ABSLongThunk_too_far1: +// CHECK-NEXT: 2000c: 08 c0 00 e3 movw r12, #8 +// CHECK-NEXT: 20010: 02 c2 40 e3 movt r12, #514 +// CHECK-NEXT: 20014: 1c ff 2f e1 bx r12 +// CHECK: __ARMv7ABSLongThunk_too_far2: +// CHECK-NEXT: 20018: 0c c0 00 e3 movw r12, #12 +// CHECK-NEXT: 2001c: 02 c2 40 e3 movt r12, #514 +// CHECK-NEXT: 20020: 1c ff 2f e1 bx r12 +// CHECK: __ARMv7ABSLongThunk_too_far3: +// CHECK-NEXT: 20024: 10 c0 00 e3 movw r12, #16 +// CHECK-NEXT: 20028: 02 c2 40 e3 movt r12, #514 +// CHECK-NEXT: 2002c: 1c ff 2f e1 bx r12 Index: test/ELF/arm-thumb-branch-error.s =================================================================== --- test/ELF/arm-thumb-branch-error.s +++ /dev/null @@ -1,19 +0,0 @@ -// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t -// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %S/Inputs/far-arm-thumb-abs.s -o %tfar -// RUN: not ld.lld %t %tfar -o %t2 2>&1 | FileCheck %s -// REQUIRES: arm - .syntax unified - .section .text, "ax",%progbits - .globl _start - .balign 0x10000 - .type _start,%function -_start: - // address of too_far symbols are just out of range of ARM branch with - // 26-bit immediate field and an addend of -8 - bl too_far1 - b too_far2 - beq.w too_far3 - -// CHECK: R_ARM_THM_CALL out of range -// CHECK-NEXT: R_ARM_THM_JUMP24 out of range -// CHECK-NEXT: R_ARM_THM_JUMP19 out of range Index: test/ELF/arm-thumb-branch-rangethunk.s =================================================================== --- /dev/null +++ test/ELF/arm-thumb-branch-rangethunk.s @@ -0,0 +1,36 @@ +// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t +// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %S/Inputs/far-arm-thumb-abs.s -o %tfar +// RUN: ld.lld %t %tfar -o %t2 2>&1 +// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi %t2 +// REQUIRES: arm + .syntax unified + .thumb + .section .text, "ax",%progbits + .globl _start + .balign 0x10000 + .type _start,%function +_start: + // address of too_far symbols are just out of range of ARM branch with + // 26-bit immediate field and an addend of -8 + bl too_far1 + b too_far2 + beq.w too_far3 + +// CHECK: Disassembly of section .text: +// CHECK-NEXT: _start: +// CHECK-NEXT: 20000: 00 f0 04 f8 bl #8 +// CHECK-NEXT: 20004: 00 f0 07 b8 b.w #14 <__Thumbv7ABSLongThunk_too_far2> +// CHECK-NEXT: 20008: 00 f0 0a 80 beq.w #20 <__Thumbv7ABSLongThunk_too_far3> +// CHECK: __Thumbv7ABSLongThunk_too_far1: +// CHECK-NEXT: 2000c: 40 f2 05 0c movw r12, #5 +// CHECK-NEXT: 20010: c0 f2 02 1c movt r12, #258 +// CHECK-NEXT: 20014: 60 47 bx r12 +// CHECK: __Thumbv7ABSLongThunk_too_far2: +// CHECK-NEXT: 20016: 40 f2 09 0c movw r12, #9 +// CHECK-NEXT: 2001a: c0 f2 02 1c movt r12, #258 +// CHECK-NEXT: 2001e: 60 47 bx r12 +// CHECK: __Thumbv7ABSLongThunk_too_far3: +// CHECK-NEXT: 20020: 40 f2 0d 0c movw r12, #13 +// CHECK-NEXT: 20024: c0 f2 12 0c movt r12, #18 +// CHECK-NEXT: 20028: 60 47 bx r12 +// CHECK-NEXT: 2002a: 00 00 movs r0, r0