Index: lld/ELF/Arch/ARM.cpp =================================================================== --- lld/ELF/Arch/ARM.cpp +++ lld/ELF/Arch/ARM.cpp @@ -279,7 +279,7 @@ bool ARM::needsThunk(RelExpr expr, RelType type, const InputFile *file, uint64_t branchAddr, const Symbol &s, - int64_t /*a*/) const { + int64_t a) const { // If S is an undefined weak symbol and does not have a PLT entry then it // will be resolved as a branch to the next instruction. if (s.isUndefWeak() && !s.isInPlt()) @@ -298,7 +298,7 @@ LLVM_FALLTHROUGH; case R_ARM_CALL: { uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA(); - return !inBranchRange(type, branchAddr, dst); + return !inBranchRange(type, branchAddr, dst + a); } case R_ARM_THM_JUMP19: case R_ARM_THM_JUMP24: @@ -309,7 +309,7 @@ LLVM_FALLTHROUGH; case R_ARM_THM_CALL: { uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA(); - return !inBranchRange(type, branchAddr, dst); + return !inBranchRange(type, branchAddr, dst + a); } } return false; @@ -350,46 +350,30 @@ } bool ARM::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { - uint64_t range; - uint64_t instrSize; + if ((dst & 0x1) == 0) + // Destination is ARM, if ARM caller then Src is already 4-byte aligned. + // If Thumb Caller (BLX) the Src address has bottom 2 bits cleared to ensure + // destination will be 4 byte aligned. + src &= ~0x3; + else + // Bit 0 == 1 denotes Thumb state, it is not part of the range + dst &= ~0x1; + int64_t offset = dst - src; switch (type) { case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_JUMP24: case R_ARM_CALL: - range = 0x2000000; - instrSize = 4; - break; + return llvm::isInt<26>(offset); case R_ARM_THM_JUMP19: - range = 0x100000; - instrSize = 2; - break; + return llvm::isInt<21>(offset); case R_ARM_THM_JUMP24: case R_ARM_THM_CALL: - range = config->armJ1J2BranchEncoding ? 0x1000000 : 0x400000; - instrSize = 2; - break; + return config->armJ1J2BranchEncoding ? llvm::isInt<25>(offset) : llvm::isInt<23>(offset); default: return true; } - // PC at Src is 2 instructions ahead, immediate of branch is signed - if (src > dst) - range -= 2 * instrSize; - else - range += instrSize; - - if ((dst & 0x1) == 0) - // Destination is ARM, if ARM caller then Src is already 4-byte aligned. - // If Thumb Caller (BLX) the Src address has bottom 2 bits cleared to ensure - // destination will be 4 byte aligned. - src &= ~0x3; - else - // Bit 0 == 1 denotes Thumb state, it is not part of the range - dst &= ~0x1; - - uint64_t distance = (src > dst) ? src - dst : dst - src; - return distance <= range; } // Helper to produce message text when LLD detects that a CALL relocation to Index: lld/ELF/Relocations.h =================================================================== --- lld/ELF/Relocations.h +++ lld/ELF/Relocations.h @@ -148,7 +148,7 @@ void mergeThunks(ArrayRef outputSections); ThunkSection *getISDThunkSec(OutputSection *os, InputSection *isec, - InputSectionDescription *isd, uint32_t type, + InputSectionDescription *isd, const Relocation &rel, uint64_t src); ThunkSection *getISThunkSec(InputSection *isec); Index: lld/ELF/Relocations.cpp =================================================================== --- lld/ELF/Relocations.cpp +++ lld/ELF/Relocations.cpp @@ -1765,12 +1765,12 @@ // linker script section pattern such as { .text .text.* }. ThunkSection *ThunkCreator::getISDThunkSec(OutputSection *os, InputSection *isec, InputSectionDescription *isd, - uint32_t type, uint64_t src) { + const Relocation& rel, uint64_t src) { for (std::pair tp : isd->thunkSections) { ThunkSection *ts = tp.first; - uint64_t tsBase = os->addr + ts->outSecOff; - uint64_t tsLimit = tsBase + ts->getSize(); - if (target->inBranchRange(type, src, (src > tsLimit) ? tsBase : tsLimit)) + uint64_t tsBase = os->addr + ts->outSecOff + rel.addend; + uint64_t tsLimit = tsBase + ts->getSize() + rel.addend; + if (target->inBranchRange(rel.type, src, (src > tsLimit) ? tsBase : tsLimit)) return ts; } @@ -1780,9 +1780,9 @@ // possible. Error if InputSection is so large we cannot place ThunkSection // anywhere in Range. uint64_t thunkSecOff = isec->outSecOff; - if (!target->inBranchRange(type, src, os->addr + thunkSecOff)) { + if (!target->inBranchRange(rel.type, src, os->addr + thunkSecOff + rel.addend)) { thunkSecOff = isec->outSecOff + isec->getSize(); - if (!target->inBranchRange(type, src, os->addr + thunkSecOff)) + if (!target->inBranchRange(rel.type, src, os->addr + thunkSecOff + rel.addend)) fatal("InputSection too large for range extension thunk " + isec->getObjMsg(src - (os->addr + isec->outSecOff))); } @@ -1933,7 +1933,8 @@ std::pair ThunkCreator::getThunk(InputSection *isec, Relocation &rel, uint64_t src) { std::vector *thunkVec = nullptr; - int64_t addend = rel.addend + getPCBias(rel.type); + // Compensate for the different ARM/Thumb PC bias so we can reuse more thunks. + int64_t keyAddend = rel.addend + getPCBias(rel.type); // We use a ((section, offset), addend) pair to find the thunk position if // possible so that we create only one thunk for aliased symbols or ICFed @@ -1943,17 +1944,16 @@ if (auto *d = dyn_cast(rel.sym)) if (!d->isInPlt() && d->section) thunkVec = &thunkedSymbolsBySectionAndAddend[{ - {d->section->repl, d->value}, addend}]; + {d->section->repl, d->value}, keyAddend}]; if (!thunkVec) - thunkVec = &thunkedSymbols[{rel.sym, addend}]; + thunkVec = &thunkedSymbols[{rel.sym, keyAddend}]; // Check existing Thunks for Sym to see if they can be reused for (Thunk *t : *thunkVec) if (isThunkSectionCompatible(isec, t->getThunkTargetSym()->section) && t->isCompatibleWith(*isec, rel) && target->inBranchRange(rel.type, src, - t->getThunkTargetSym()->getVA(rel.addend) + - getPCBias(rel.type))) + t->getThunkTargetSym()->getVA(rel.addend))) return std::make_pair(t, false); // No existing compatible Thunk in range, create a new one @@ -1969,7 +1969,7 @@ bool ThunkCreator::normalizeExistingThunk(Relocation &rel, uint64_t src) { if (Thunk *t = thunks.lookup(rel.sym)) { if (target->inBranchRange(rel.type, src, - rel.sym->getVA(rel.addend) + getPCBias(rel.type))) + rel.sym->getVA(rel.addend))) return true; rel.sym = &t->destination; rel.addend = t->addend; @@ -2041,7 +2041,7 @@ if (auto *tis = t->getTargetInputSection()) ts = getISThunkSec(tis); else - ts = getISDThunkSec(os, isec, isd, rel.type, src); + ts = getISDThunkSec(os, isec, isd, rel, src); ts->addThunk(t); thunks[t->getThunkTargetSym()] = t; } Index: lld/ELF/Thunks.cpp =================================================================== --- lld/ELF/Thunks.cpp +++ lld/ELF/Thunks.cpp @@ -72,7 +72,7 @@ // if the target is in range, otherwise it creates a long thunk. class ARMThunk : public Thunk { public: - ARMThunk(Symbol &dest) : Thunk(dest, 0) {} + ARMThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {} bool getMayUseShortThunk(); uint32_t size() override { return getMayUseShortThunk() ? 4 : sizeLong(); } @@ -102,7 +102,7 @@ // which has a range of 16MB. class ThumbThunk : public Thunk { public: - ThumbThunk(Symbol &dest) : Thunk(dest, 0) { alignment = 2; } + ThumbThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) { alignment = 2; } bool getMayUseShortThunk(); uint32_t size() override { return getMayUseShortThunk() ? 4 : sizeLong(); } @@ -125,7 +125,7 @@ // Source State, TargetState, Target Requirement, ABS or PI, Range class ARMV7ABSLongThunk final : public ARMThunk { public: - ARMV7ABSLongThunk(Symbol &dest) : ARMThunk(dest) {} + ARMV7ABSLongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {} uint32_t sizeLong() override { return 12; } void writeLong(uint8_t *buf) override; @@ -134,7 +134,7 @@ class ARMV7PILongThunk final : public ARMThunk { public: - ARMV7PILongThunk(Symbol &dest) : ARMThunk(dest) {} + ARMV7PILongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {} uint32_t sizeLong() override { return 16; } void writeLong(uint8_t *buf) override; @@ -143,7 +143,7 @@ class ThumbV7ABSLongThunk final : public ThumbThunk { public: - ThumbV7ABSLongThunk(Symbol &dest) : ThumbThunk(dest) {} + ThumbV7ABSLongThunk(Symbol &dest, int64_t addend) : ThumbThunk(dest, addend) {} uint32_t sizeLong() override { return 10; } void writeLong(uint8_t *buf) override; @@ -152,7 +152,7 @@ class ThumbV7PILongThunk final : public ThumbThunk { public: - ThumbV7PILongThunk(Symbol &dest) : ThumbThunk(dest) {} + ThumbV7PILongThunk(Symbol &dest, int64_t addend) : ThumbThunk(dest, addend) {} uint32_t sizeLong() override { return 12; } void writeLong(uint8_t *buf) override; @@ -166,7 +166,7 @@ // can result in a thunk class ARMV5ABSLongThunk final : public ARMThunk { public: - ARMV5ABSLongThunk(Symbol &dest) : ARMThunk(dest) {} + ARMV5ABSLongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {} uint32_t sizeLong() override { return 8; } void writeLong(uint8_t *buf) override; @@ -177,7 +177,7 @@ class ARMV5PILongThunk final : public ARMThunk { public: - ARMV5PILongThunk(Symbol &dest) : ARMThunk(dest) {} + ARMV5PILongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {} uint32_t sizeLong() override { return 16; } void writeLong(uint8_t *buf) override; @@ -189,7 +189,7 @@ // Implementations of Thunks for Arm v6-M. Only Thumb instructions are permitted class ThumbV6MABSLongThunk final : public ThumbThunk { public: - ThumbV6MABSLongThunk(Symbol &dest) : ThumbThunk(dest) {} + ThumbV6MABSLongThunk(Symbol &dest, int64_t addend) : ThumbThunk(dest, addend) {} uint32_t sizeLong() override { return 12; } void writeLong(uint8_t *buf) override; @@ -198,7 +198,7 @@ class ThumbV6MPILongThunk final : public ThumbThunk { public: - ThumbV6MPILongThunk(Symbol &dest) : ThumbThunk(dest) {} + ThumbV6MPILongThunk(Symbol &dest, int64_t addend) : ThumbThunk(dest, addend) {} uint32_t sizeLong() override { return 16; } void writeLong(uint8_t *buf) override; @@ -1040,7 +1040,7 @@ // - MOVT and MOVW instructions cannot be used // - Only Thumb relocation that can generate a Thunk is a BL, this can always // be transformed into a BLX -static Thunk *addThunkPreArmv7(RelType reloc, Symbol &s) { +static Thunk *addThunkPreArmv7(RelType reloc, Symbol &s, int64_t a) { switch (reloc) { case R_ARM_PC24: case R_ARM_PLT32: @@ -1048,8 +1048,8 @@ case R_ARM_CALL: case R_ARM_THM_CALL: if (config->picThunk) - return make(s); - return make(s); + return make(s, a); + return make(s, a); } fatal("relocation " + toString(reloc) + " to " + toString(s) + " not supported for Armv5 or Armv6 targets"); @@ -1060,21 +1060,21 @@ // - MOVT and MOVW instructions cannot be used. // - Only a limited number of instructions can access registers r8 and above // - No interworking support is needed (all Thumb). -static Thunk *addThunkV6M(RelType reloc, Symbol &s) { +static Thunk *addThunkV6M(RelType reloc, Symbol &s, int64_t a) { switch (reloc) { case R_ARM_THM_JUMP19: case R_ARM_THM_JUMP24: case R_ARM_THM_CALL: if (config->isPic) - return make(s); - return make(s); + return make(s, a); + return make(s, a); } fatal("relocation " + toString(reloc) + " to " + toString(s) + " not supported for Armv6-M targets"); } // Creates a thunk for Thumb-ARM interworking or branch range extension. -static Thunk *addThunkArm(RelType reloc, Symbol &s) { +static Thunk *addThunkArm(RelType reloc, Symbol &s, int64_t a) { // Decide which Thunk is needed based on: // Available instruction set // - An Arm Thunk can only be used if Arm state is available. @@ -1093,8 +1093,8 @@ // architecture to flag. if (!config->armHasMovtMovw) { if (!config->armJ1J2BranchEncoding) - return addThunkPreArmv7(reloc, s); - return addThunkV6M(reloc, s); + return addThunkPreArmv7(reloc, s, a); + return addThunkV6M(reloc, s, a); } switch (reloc) { @@ -1103,14 +1103,14 @@ case R_ARM_JUMP24: case R_ARM_CALL: if (config->picThunk) - return make(s); - return make(s); + return make(s, a); + return make(s, a); case R_ARM_THM_JUMP19: case R_ARM_THM_JUMP24: case R_ARM_THM_CALL: if (config->picThunk) - return make(s); - return make(s); + return make(s, a); + return make(s, a); } fatal("unrecognized relocation type"); } @@ -1166,7 +1166,7 @@ return addThunkAArch64(rel.type, s, a); if (config->emachine == EM_ARM) - return addThunkArm(rel.type, s); + return addThunkArm(rel.type, s, a); if (config->emachine == EM_MIPS) return addThunkMips(rel.type, s);