aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Smith <peter.smith@arm.com>2016-06-16 11:23:51 +0100
committerPeter Smith <peter.smith@arm.com>2016-06-30 12:55:40 +0100
commitca73544faac911d9de33dbab4b8f3ad5c6e64f4f (patch)
tree3c2b9da392cb1a8bc10d8da133cfc63c1a8b80e6
parentd2e8398160f84fc5def02f315e0ca647b773a714 (diff)
Add Thunk support framework for ARM and Mipslinaro-local/InterworkVeneers
Generalise the Mips LA25 Thunk code and implement ARM and Thumb interworking Thunks. - Introduce a new module Thunks.cpp to store the mapping between SymbolBody and Thunk - SymbolBody now has no Thunk specific fields - A Target can more easily have more than one type of Thunk - Support PC-relative calls to Thunks - Support Thunks to PLT entries - Existing Mips LA25 Thunk code integrated - Support for ARMv7A interworking Thunks Limitations: - Only one Thunk per SymbolBody, this is sufficient for all currently implemented Thunks. - ARM thunks assume presence of V6T2 MOVT and MOVW instructions. Change-Id: I3d90a0303079ab12effc2821de347ee90ad70a07
-rw-r--r--ELF/CMakeLists.txt1
-rw-r--r--ELF/Driver.cpp3
-rw-r--r--ELF/InputSection.cpp25
-rw-r--r--ELF/InputSection.h7
-rw-r--r--ELF/Relocations.cpp26
-rw-r--r--ELF/Relocations.h4
-rw-r--r--ELF/Symbols.cpp12
-rw-r--r--ELF/Symbols.h3
-rw-r--r--ELF/Target.cpp32
-rw-r--r--ELF/Target.h4
-rw-r--r--ELF/Thunks.cpp357
-rw-r--r--ELF/Thunks.h58
-rw-r--r--ELF/Writer.cpp2
-rw-r--r--test/ELF/arm-mov-relocs.s37
-rw-r--r--test/ELF/arm-thumb-interwork-thunk.s375
15 files changed, 894 insertions, 52 deletions
diff --git a/ELF/CMakeLists.txt b/ELF/CMakeLists.txt
index 1875a1ebb..a1b65adc7 100644
--- a/ELF/CMakeLists.txt
+++ b/ELF/CMakeLists.txt
@@ -21,6 +21,7 @@ add_lld_library(lldELF
SymbolTable.cpp
Symbols.cpp
Target.cpp
+ Thunks.cpp
Writer.cpp
LINK_COMPONENTS
diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp
index c8041e0db..5f2f433cd 100644
--- a/ELF/Driver.cpp
+++ b/ELF/Driver.cpp
@@ -18,6 +18,7 @@
#include "SymbolListFile.h"
#include "SymbolTable.h"
#include "Target.h"
+#include "Thunks.h"
#include "Writer.h"
#include "lld/Driver/Driver.h"
#include "llvm/ADT/StringExtras.h"
@@ -509,6 +510,8 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
Target = TI.get();
LinkerScript<ELFT> LS;
Script<ELFT>::X = &LS;
+ std::unique_ptr<ThunkInfo<ELFT>> THI(createThunkInfo<ELFT>());
+ Thunks<ELFT>::X = THI.get();
Config->Rela = ELFT::Is64Bits;
Config->Mips64EL =
diff --git a/ELF/InputSection.cpp b/ELF/InputSection.cpp
index 32fed5504..db7a69042 100644
--- a/ELF/InputSection.cpp
+++ b/ELF/InputSection.cpp
@@ -14,6 +14,7 @@
#include "InputFiles.h"
#include "OutputSections.h"
#include "Target.h"
+#include "Thunks.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/Endian.h"
@@ -127,9 +128,9 @@ InputSectionBase<ELFT> *InputSection<ELFT>::getRelocatedSection() {
return Sections[this->Header->sh_info];
}
-template <class ELFT> void InputSection<ELFT>::addThunk(SymbolBody &Body) {
- Body.ThunkIndex = Thunks.size();
- Thunks.push_back(&Body);
+template <class ELFT> void
+InputSection<ELFT>::addThunk(const Thunk<ELFT>* thunk) {
+ Thunks.push_back(thunk);
}
template <class ELFT> uint64_t InputSection<ELFT>::getThunkOff() const {
@@ -137,7 +138,10 @@ template <class ELFT> uint64_t InputSection<ELFT>::getThunkOff() const {
}
template <class ELFT> uint64_t InputSection<ELFT>::getThunksSize() const {
- return Thunks.size() * Target->ThunkSize;
+ uint64_t total = 0;
+ for (const Thunk<ELFT> *T : Thunks)
+ total += T->size();
+ return total;
}
// This is used for -r. We can't use memcpy to copy relocations because we need
@@ -182,8 +186,11 @@ getSymVA(uint32_t Type, typename ELFT::uint A, typename ELFT::uint P,
Out<ELFT>::Got->getNumEntries() * sizeof(uintX_t);
case R_TLSLD_PC:
return Out<ELFT>::Got->getTlsIndexVA() + A - P;
- case R_THUNK:
- return Body.getThunkVA<ELFT>();
+ case R_THUNK_ABS:
+ return Thunks<ELFT>::X->getThunk(Body)->getVA();
+ case R_THUNK_PC:
+ case R_THUNK_PLT_PC:
+ return Thunks<ELFT>::X->getThunk(Body)->getVA() + A - P;
case R_PPC_TOC:
return getPPC64TocBase() + A;
case R_TLSGD:
@@ -403,9 +410,9 @@ template <class ELFT> void InputSection<ELFT>::writeTo(uint8_t *Buf) {
// jump istruction.
if (!Thunks.empty()) {
Buf += OutSecOff + getThunkOff();
- for (const SymbolBody *S : Thunks) {
- Target->writeThunk(Buf, S->getVA<ELFT>());
- Buf += Target->ThunkSize;
+ for (const Thunk<ELFT> * T : Thunks) {
+ T->writeTo(Buf);
+ Buf += T->size();
}
}
}
diff --git a/ELF/InputSection.h b/ELF/InputSection.h
index f9ea1bb6e..6b2e6a0c2 100644
--- a/ELF/InputSection.h
+++ b/ELF/InputSection.h
@@ -12,6 +12,7 @@
#include "Config.h"
#include "Relocations.h"
+#include "Thunks.h"
#include "lld/Core/LLVM.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/TinyPtrVector.h"
@@ -207,8 +208,8 @@ public:
// Register thunk related to the symbol. When the section is written
// to a mmap'ed file, target is requested to write an actual thunk code.
- // Now thunks is supported for MIPS target only.
- void addThunk(SymbolBody &Body);
+ // Now thunks is supported for MIPS and ARM target only.
+ void addThunk(const Thunk<ELFT> *thunk);
// The offset of synthetic thunk code from beginning of this section.
uint64_t getThunkOff() const;
@@ -229,7 +230,7 @@ private:
// Used by ICF.
uint64_t GroupId = 0;
- llvm::TinyPtrVector<const SymbolBody *> Thunks;
+ llvm::TinyPtrVector<const Thunk<ELFT> *> Thunks;
};
// MIPS .reginfo section provides information on the registers used by the code
diff --git a/ELF/Relocations.cpp b/ELF/Relocations.cpp
index a54c449c6..eafba88df 100644
--- a/ELF/Relocations.cpp
+++ b/ELF/Relocations.cpp
@@ -46,6 +46,7 @@
#include "OutputSections.h"
#include "SymbolTable.h"
#include "Target.h"
+#include "Thunks.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/raw_ostream.h"
@@ -276,14 +277,14 @@ template <class ELFT> static bool isAbsolute(const SymbolBody &Body) {
static bool needsPlt(RelExpr Expr) {
return Expr == R_PLT_PC || Expr == R_PPC_PLT_OPD || Expr == R_PLT ||
- Expr == R_PLT_PAGE_PC;
+ Expr == R_PLT_PAGE_PC || Expr == R_THUNK_PLT_PC;
}
// True if this expression is of the form Sym - X, where X is a position in the
// file (PC, or GOT for example).
static bool isRelExpr(RelExpr Expr) {
return Expr == R_PC || Expr == R_GOTREL || Expr == R_PAGE_PC ||
- Expr == R_RELAX_GOT_PC;
+ Expr == R_RELAX_GOT_PC || Expr == R_THUNK_PC || Expr == R_THUNK_PLT_PC;
}
template <class ELFT>
@@ -293,7 +294,8 @@ static bool isStaticLinkTimeConstant(RelExpr E, uint32_t Type,
if (E == R_SIZE || E == R_GOT_FROM_END || E == R_GOT_OFF ||
E == R_MIPS_GOT_LOCAL_PAGE || E == R_MIPS_GOT_OFF || E == R_MIPS_TLSGD ||
E == R_GOT_PAGE_PC || E == R_GOT_PC || E == R_PLT_PC || E == R_TLSGD_PC ||
- E == R_TLSGD || E == R_PPC_PLT_OPD || E == R_TLSDESC_PAGE || E == R_HINT)
+ E == R_TLSGD || E == R_PPC_PLT_OPD || E == R_TLSDESC_PAGE ||
+ E == R_HINT || E == R_THUNK_PC || E == R_THUNK_PLT_PC)
return true;
// These never do, except if the entire file is position dependent or if
@@ -402,8 +404,6 @@ template <class ELFT>
static RelExpr adjustExpr(const elf::ObjectFile<ELFT> &File, SymbolBody &Body,
bool IsWrite, RelExpr Expr, uint32_t Type,
const uint8_t *Data) {
- if (Target->needsThunk(Type, File, Body))
- return R_THUNK;
bool Preemptible = isPreemptible(Body, Type);
if (Body.isGnuIFunc()) {
Expr = toPlt(Expr);
@@ -413,6 +413,7 @@ static RelExpr adjustExpr(const elf::ObjectFile<ELFT> &File, SymbolBody &Body,
if (Expr == R_GOT_PC)
Expr = Target->adjustRelaxExpr(Type, Data, Expr);
}
+ Expr = Thunks<ELFT>::X->adjustExprForThunk(Expr, Type, File, Body);
if (IsWrite || isStaticLinkTimeConstant<ELFT>(Expr, Type, Body))
return Expr;
@@ -556,7 +557,8 @@ static void scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
if (Expr == R_HINT)
continue;
- if (needsPlt(Expr) || Expr == R_THUNK || refersToGotEntry(Expr) ||
+ if (needsPlt(Expr) || Expr == R_THUNK_ABS || Expr == R_THUNK_PC ||
+ Expr == R_THUNK_PLT_PC || refersToGotEntry(Expr) ||
!isPreemptible(Body, Type)) {
// If the relocation points to something in the file, we can process it.
bool Constant = isStaticLinkTimeConstant<ELFT>(Expr, Type, Body);
@@ -600,14 +602,10 @@ static void scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
// Some targets might require creation of thunks for relocations.
// Now we support only MIPS which requires LA25 thunk to call PIC
- // code from non-PIC one.
- if (Expr == R_THUNK) {
- if (!Body.hasThunk()) {
- auto *Sec = cast<InputSection<ELFT>>(
- cast<DefinedRegular<ELFT>>(&Body)->Section);
- Sec->addThunk(Body);
- }
- continue;
+ // code from non-PIC one, and ARM which requires interworking.
+ if (Expr == R_THUNK_ABS || Expr == R_THUNK_PC || Expr == R_THUNK_PLT_PC) {
+ auto *Sec = cast<InputSection<ELFT>>(&C);
+ Thunks<ELFT>::X->addThunk(Type, Body, *Sec);
}
// At this point we are done with the relocated position. Some relocations
diff --git a/ELF/Relocations.h b/ELF/Relocations.h
index 0ff02ef1c..f5e79abc6 100644
--- a/ELF/Relocations.h
+++ b/ELF/Relocations.h
@@ -52,7 +52,9 @@ enum RelExpr {
R_RELAX_TLS_IE_TO_LE,
R_RELAX_TLS_LD_TO_LE,
R_SIZE,
- R_THUNK,
+ R_THUNK_ABS,
+ R_THUNK_PC,
+ R_THUNK_PLT_PC,
R_TLS,
R_TLSDESC,
R_TLSDESC_PAGE,
diff --git a/ELF/Symbols.cpp b/ELF/Symbols.cpp
index 700c3bf80..f08fe7057 100644
--- a/ELF/Symbols.cpp
+++ b/ELF/Symbols.cpp
@@ -175,13 +175,6 @@ template <class ELFT> typename ELFT::uint SymbolBody::getPltVA() const {
PltIndex * Target->PltEntrySize;
}
-template <class ELFT> typename ELFT::uint SymbolBody::getThunkVA() const {
- auto *D = cast<DefinedRegular<ELFT>>(this);
- auto *S = cast<InputSection<ELFT>>(D->Section);
- return S->OutSec->getVA() + S->OutSecOff + S->getThunkOff() +
- ThunkIndex * Target->ThunkSize;
-}
-
template <class ELFT> typename ELFT::uint SymbolBody::getSize() const {
if (const auto *C = dyn_cast<DefinedCommon>(this))
return C->Size;
@@ -319,11 +312,6 @@ template uint32_t SymbolBody::template getSize<ELF32BE>() const;
template uint64_t SymbolBody::template getSize<ELF64LE>() const;
template uint64_t SymbolBody::template getSize<ELF64BE>() const;
-template uint32_t SymbolBody::template getThunkVA<ELF32LE>() const;
-template uint32_t SymbolBody::template getThunkVA<ELF32BE>() const;
-template uint64_t SymbolBody::template getThunkVA<ELF64LE>() const;
-template uint64_t SymbolBody::template getThunkVA<ELF64BE>() const;
-
template class elf::DefinedSynthetic<ELF32LE>;
template class elf::DefinedSynthetic<ELF32BE>;
template class elf::DefinedSynthetic<ELF64LE>;
diff --git a/ELF/Symbols.h b/ELF/Symbols.h
index 3a9baa7fb..8bdc59e8e 100644
--- a/ELF/Symbols.h
+++ b/ELF/Symbols.h
@@ -89,11 +89,9 @@ public:
uint32_t GotIndex = -1;
uint32_t GotPltIndex = -1;
uint32_t PltIndex = -1;
- uint32_t ThunkIndex = -1;
uint32_t GlobalDynIndex = -1;
bool isInGot() const { return GotIndex != -1U; }
bool isInPlt() const { return PltIndex != -1U; }
- bool hasThunk() const { return ThunkIndex != -1U; }
template <class ELFT>
typename ELFT::uint getVA(typename ELFT::uint Addend = 0) const;
@@ -103,7 +101,6 @@ public:
template <class ELFT> typename ELFT::uint getGotPltOffset() const;
template <class ELFT> typename ELFT::uint getGotPltVA() const;
template <class ELFT> typename ELFT::uint getPltVA() const;
- template <class ELFT> typename ELFT::uint getThunkVA() const;
template <class ELFT> typename ELFT::uint getSize() const;
// Returns the file from which the symbol was created.
diff --git a/ELF/Target.cpp b/ELF/Target.cpp
index 2f5727fed..ceeeee825 100644
--- a/ELF/Target.cpp
+++ b/ELF/Target.cpp
@@ -29,6 +29,7 @@
#include "InputFiles.h"
#include "OutputSections.h"
#include "Symbols.h"
+#include "Thunks.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Object/ELF.h"
@@ -199,6 +200,7 @@ public:
void writeThunk(uint8_t *Buf, uint64_t S) const override;
bool needsThunk(uint32_t Type, const InputFile &File,
const SymbolBody &S) const override;
+ uint32_t thunkSize() const override;
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
bool usesOnlyLowPageBits(uint32_t Type) const override;
};
@@ -1468,8 +1470,12 @@ RelExpr ARMTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
// FIXME: currently B(S) assumed to be .got, this may not hold for all
// platforms.
return R_GOTONLY_PC;
+ case R_ARM_MOVW_PREL_NC:
+ case R_ARM_MOVT_PREL:
case R_ARM_PREL31:
case R_ARM_REL32:
+ case R_ARM_THM_MOVW_PREL_NC:
+ case R_ARM_THM_MOVT_PREL:
return R_PC;
}
}
@@ -1600,17 +1606,20 @@ void ARMTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
((Val >> 1) & 0x07ff)); // imm11
break;
case R_ARM_MOVW_ABS_NC:
+ case R_ARM_MOVW_PREL_NC:
write32le(Loc, (read32le(Loc) & ~0x000f0fff) | ((Val & 0xf000) << 4) |
(Val & 0x0fff));
break;
case R_ARM_MOVT_ABS:
- checkUInt<32>(Val, Type);
+ case R_ARM_MOVT_PREL:
+ checkInt<32>(Val, Type);
write32le(Loc, (read32le(Loc) & ~0x000f0fff) |
(((Val >> 16) & 0xf000) << 4) | ((Val >> 16) & 0xfff));
break;
case R_ARM_THM_MOVT_ABS:
+ case R_ARM_THM_MOVT_PREL:
// Encoding T1: A = imm4:i:imm3:imm8
- checkUInt<32>(Val, Type);
+ checkInt<32>(Val, Type);
write16le(Loc,
0xf2c0 | // opcode
((Val >> 17) & 0x0400) | // i
@@ -1621,6 +1630,7 @@ void ARMTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
((Val >> 16) & 0x00ff)); // imm8
break;
case R_ARM_THM_MOVW_ABS_NC:
+ case R_ARM_THM_MOVW_PREL_NC:
// Encoding T3: A = imm4:i:imm3:imm8
write16le(Loc,
0xf240 | // opcode
@@ -1667,8 +1677,8 @@ uint64_t ARMTargetInfo::getImplicitAddend(const uint8_t *Buf,
((Hi & 0x003f) << 12) | // imm6
((Lo & 0x07ff) << 1)); // imm11:0
}
- case R_ARM_THM_JUMP24:
- case R_ARM_THM_CALL: {
+ case R_ARM_THM_CALL:
+ case R_ARM_THM_JUMP24: {
// Encoding B T4, BL T1, BLX T2: A = S:I1:I2:imm10:imm11:0
// I1 = NOT(J1 EOR S), I2 = NOT(J2 EOR S)
// FIXME: I1 and I2 require v6T2ops
@@ -1683,12 +1693,16 @@ uint64_t ARMTargetInfo::getImplicitAddend(const uint8_t *Buf,
// ELF for the ARM Architecture 4.6.1.1 the implicit addend for MOVW and
// MOVT is in the range -32768 <= A < 32768
case R_ARM_MOVW_ABS_NC:
- case R_ARM_MOVT_ABS: {
+ case R_ARM_MOVT_ABS:
+ case R_ARM_MOVW_PREL_NC:
+ case R_ARM_MOVT_PREL: {
uint64_t Val = read32le(Buf) & 0x000f0fff;
return SignExtend64<16>(((Val & 0x000f0000) >> 4) | (Val & 0x00fff));
}
case R_ARM_THM_MOVW_ABS_NC:
- case R_ARM_THM_MOVT_ABS: {
+ case R_ARM_THM_MOVT_ABS:
+ case R_ARM_THM_MOVW_PREL_NC:
+ case R_ARM_THM_MOVT_PREL: {
// Encoding T3: A = imm4:i:imm3:imm8
uint16_t Hi = read16le(Buf);
uint16_t Lo = read16le(Buf + 2);
@@ -1705,7 +1719,6 @@ template <class ELFT> MipsTargetInfo<ELFT>::MipsTargetInfo() {
PageSize = 65536;
PltEntrySize = 16;
PltHeaderSize = 32;
- ThunkSize = 16;
CopyRel = R_MIPS_COPY;
PltRel = R_MIPS_JUMP_SLOT;
if (ELFT::Is64Bits) {
@@ -1897,6 +1910,11 @@ bool MipsTargetInfo<ELFT>::needsThunk(uint32_t Type, const InputFile &File,
}
template <class ELFT>
+uint32_t MipsTargetInfo<ELFT>::thunkSize() const {
+ return 16;
+}
+
+template <class ELFT>
uint64_t MipsTargetInfo<ELFT>::getImplicitAddend(const uint8_t *Buf,
uint32_t Type) const {
const endianness E = ELFT::TargetEndianness;
diff --git a/ELF/Target.h b/ELF/Target.h
index 822d38ae1..58e3c7a5b 100644
--- a/ELF/Target.h
+++ b/ELF/Target.h
@@ -52,7 +52,7 @@ public:
const SymbolBody &S) const;
virtual void writeThunk(uint8_t *Buf, uint64_t S) const {}
-
+ virtual uint32_t thunkSize() const { return 0; }
virtual RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const = 0;
virtual void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const = 0;
virtual ~TargetInfo();
@@ -87,8 +87,6 @@ public:
// Set to 0 for variant 2
unsigned TcbSize = 0;
- uint32_t ThunkSize = 0;
-
virtual RelExpr adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
RelExpr Expr) const;
virtual void relaxGot(uint8_t *Loc, uint64_t Val) const;
diff --git a/ELF/Thunks.cpp b/ELF/Thunks.cpp
new file mode 100644
index 000000000..71c3e6264
--- /dev/null
+++ b/ELF/Thunks.cpp
@@ -0,0 +1,357 @@
+//===- Thunks.cpp --------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains both the Target independent and Target specific Thunk
+// and ThunkInfo classes.
+//
+// A Thunk Object represents a single Thunk that will be written to an
+// InputSection. The InputSection maintains a list of Thunks that it owns.
+//
+// The ThunkInfo class:
+// - Decides when Thunks are needed
+// - Creates the correct type of Thunk for the context
+// - Assigns the Thunk to a Target Section
+// - Finds the Thunks for a SymbolBody
+//===----------------------------------------------------------------------===//
+
+#include "Thunks.h"
+#include "Error.h"
+#include "InputFiles.h"
+#include "InputSection.h"
+#include "OutputSections.h"
+#include "Symbols.h"
+#include "Target.h"
+
+#include "llvm/Object/ELF.h"
+#include "llvm/Support/ELF.h"
+#include "llvm/Support/Endian.h"
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace llvm::support::endian;
+using namespace llvm::ELF;
+
+namespace lld {
+namespace elf {
+
+template <class ELFT> Thunk<ELFT>::~Thunk() {}
+// The InputSectionThunk is a suitable base class for Thunks that are written
+// to InputSections.
+template <class ELFT> class InputSectionThunk : public Thunk<ELFT> {
+public:
+ InputSectionThunk(const SymbolBody &Destination,
+ const InputSection<ELFT> &Owner);
+ virtual typename ELFT::uint getVA() const override;
+ const SymbolBody &Destination;
+ const InputSection<ELFT> &Owner;
+ uint64_t Offset;
+};
+
+template <class ELFT>
+InputSectionThunk<ELFT>::InputSectionThunk(const SymbolBody &D,
+ const InputSection<ELFT> &O)
+ : Destination(D), Owner(O), Offset(O.getThunkOff() + O.getThunksSize()) {}
+
+template <class ELFT>
+typename ELFT::uint InputSectionThunk<ELFT>::getVA() const {
+ return Owner.OutSec->getVA() + Owner.OutSecOff + Offset;
+}
+
+// ARM Target Thunks
+
+// Base class for ARM Target Thunks that handles Thunks to PLT entries
+// and position independent Thunks
+template <class ELFT> class ARMThunk : public InputSectionThunk<ELFT> {
+public:
+ virtual void writeTo(uint8_t *Buf) const override {
+ const SymbolBody &D = this->Destination;
+ uint64_t S = D.isInPlt() ? D.getPltVA<ELFT>() : D.getVA<ELFT>();
+ writeTo(Buf, S, this->getVA());
+ }
+ virtual void writeTo(uint8_t *Buf, uint64_t S, uint64_t P) const = 0;
+ ARMThunk(const SymbolBody &Destination, const InputSection<ELFT> &Owner)
+ : InputSectionThunk<ELFT>(Destination, Owner) {}
+};
+
+// Specific ARM Thunk implementations. The naming convention is:
+// Source State, TargetState, Target Requirement, ABS or PI, Range
+
+template <class ELFT>
+class ARMThumbV7ABSLongThunk final : public ARMThunk<ELFT> {
+public:
+ virtual uint32_t size() const override { return 12; }
+ virtual void writeTo(uint8_t *Buf, uint64_t S, uint64_t P) const override {
+ const uint8_t ATData[] = {
+ 0x00, 0xc0, 0x00, 0xe3, // movw ip,:lower16:S
+ 0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S
+ 0x1c, 0xff, 0x2f, 0xe1, // bx ip
+ };
+ memcpy(Buf, ATData, sizeof(ATData));
+ Target->relocateOne(Buf, R_ARM_MOVW_ABS_NC, S);
+ Target->relocateOne(Buf + 4, R_ARM_MOVT_ABS, S);
+ }
+ ARMThumbV7ABSLongThunk(const SymbolBody &Destination,
+ const InputSection<ELFT> &Owner)
+ : ARMThunk<ELFT>(Destination, Owner) {}
+};
+
+template <class ELFT>
+class ARMThumbV7PILongThunk final : public ARMThunk<ELFT> {
+public:
+ virtual uint32_t size() const override { return 16; }
+ virtual void writeTo(uint8_t *Buf, uint64_t S, uint64_t P) const override {
+ const uint8_t ATData[] = {
+ 0xf0, 0xcf, 0x0f, 0xe3, // P: movw ip,:lower16:S - (P + (L1-P) +8)
+ 0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S - (P + (L1-P+4) +8)
+ 0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc
+ 0x1c, 0xff, 0x2f, 0xe1, // bx r12
+ };
+ memcpy(Buf, ATData, sizeof(ATData));
+ Target->relocateOne(Buf, R_ARM_MOVW_PREL_NC, S - P - 16);
+ Target->relocateOne(Buf + 4, R_ARM_MOVT_PREL, S - P - 12);
+ }
+ ARMThumbV7PILongThunk(const SymbolBody &Destination,
+ const InputSection<ELFT> &Owner)
+ : ARMThunk<ELFT>(Destination, Owner) {}
+};
+
+template <class ELFT>
+class ThumbARMV7ABSLongThunk final : public ARMThunk<ELFT> {
+public:
+ virtual uint32_t size() const override { return 10; }
+ virtual void writeTo(uint8_t *Buf, uint64_t S, uint64_t P) const override {
+ const uint8_t TAData[] = {
+ 0x40, 0xf2, 0x00, 0x0c, // movw ip, :lower16:S
+ 0xc0, 0xf2, 0x00, 0x0c, // movt ip, :upper16:S
+ 0x60, 0x47, // bx ip
+ };
+ memcpy(Buf, TAData, sizeof(TAData));
+ Target->relocateOne(Buf, R_ARM_THM_MOVW_ABS_NC, S);
+ Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_ABS, S);
+ }
+ ThumbARMV7ABSLongThunk(const SymbolBody &Destination,
+ const InputSection<ELFT> &Owner)
+ : ARMThunk<ELFT>(Destination, Owner) {}
+};
+
+template <class ELFT>
+class ThumbARMV7PILongThunk final : public ARMThunk<ELFT> {
+public:
+ virtual uint32_t size() const override { return 12; }
+ virtual void writeTo(uint8_t *Buf, uint64_t S, uint64_t P) const override {
+ const uint8_t TAData[] = {
+ 0x4f, 0xf6, 0xf4, 0x7c, // P: movw ip,:lower16:S - (P + (L1-P) + 4)
+ 0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P+4) + 4)
+ 0xfc, 0x44, // L1: add r12, pc
+ 0x60, 0x47, // bx r12
+ };
+ memcpy(Buf, TAData, sizeof(TAData));
+ Target->relocateOne(Buf, R_ARM_THM_MOVW_PREL_NC, S - P - 12);
+ Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_PREL, S - P - 8);
+ }
+ ThumbARMV7PILongThunk(const SymbolBody &Destination,
+ const InputSection<ELFT> &Owner)
+ : ARMThunk<ELFT>(Destination, Owner) {}
+};
+
+// Mips Thunks
+// Only the MIPS LA25 Thunk is supported, the implementation is delegated
+// to the MipsTargetInfo class in Target.cpp
+template <class ELFT> class MipsThunk : public InputSectionThunk<ELFT> {
+public:
+ MipsThunk(const SymbolBody &Destination, const InputSection<ELFT> &Owner);
+ virtual uint32_t size() const override;
+ virtual void writeTo(uint8_t *Buf) const override;
+};
+
+template <class ELFT>
+MipsThunk<ELFT>::MipsThunk(const SymbolBody &Destination,
+ const InputSection<ELFT> &Owner)
+ : InputSectionThunk<ELFT>(Destination, Owner) {}
+
+template <class ELFT> uint32_t MipsThunk<ELFT>::size() const {
+ return Target->thunkSize();
+}
+
+template <class ELFT>
+void MipsThunk<ELFT>::writeTo(uint8_t *Buf) const {
+ const SymbolBody &D = this->Destination;
+ uint64_t S = D.getVA<ELFT>();
+ Target->writeThunk(Buf, S);
+}
+
+template <class ELFT> ThunkInfo<ELFT>::~ThunkInfo() {}
+
+// Base class for ThunkInfo that implements a mapping from SymbolBody to
+// a ThunkInstance. The Base class also provides default implementations of
+// the member functions for Targets that don't use Thunks.
+template <class ELFT> class ThunkInfoBase : public ThunkInfo<ELFT> {
+public:
+ virtual void addThunk(uint32_t RelocType, const SymbolBody &S,
+ InputSection<ELFT> &Src) override {
+ fatal("addThunk not implemented for Target");
+ }
+ virtual RelExpr adjustExprForThunk(RelExpr Expr, uint32_t Type,
+ const InputFile &File,
+ const SymbolBody &S) const override {
+ return Expr;
+ }
+
+ virtual const Thunk<ELFT> *getThunk(const SymbolBody &S) const override;
+
+protected:
+ void registerThunk(const SymbolBody &S, const Thunk<ELFT> *T);
+ llvm::BumpPtrAllocator Alloc;
+
+private:
+ llvm::DenseMap<const SymbolBody *, const Thunk<ELFT> *> ThunkMap;
+};
+
+template <class ELFT>
+const Thunk<ELFT> *ThunkInfoBase<ELFT>::getThunk(const SymbolBody &S) const {
+ auto iter = ThunkMap.find(&S);
+ return (iter != ThunkMap.end()) ? iter->second : nullptr;
+}
+
+template <class ELFT>
+void ThunkInfoBase<ELFT>::registerThunk(const SymbolBody &S,
+ const Thunk<ELFT> *T) {
+ this->ThunkMap[&S] = T;
+}
+
+template <class ELFT> class ARMThunkInfo final : public ThunkInfoBase<ELFT> {
+public:
+ void addThunk(uint32_t RelocType, const SymbolBody &S,
+ InputSection<ELFT> &IS) override;
+ RelExpr adjustExprForThunk(RelExpr Expr, uint32_t RelocType,
+ const InputFile &File,
+ const SymbolBody &S) const override;
+};
+
+template <class ELFT>
+void ARMThunkInfo<ELFT>::addThunk(uint32_t RelocType, const SymbolBody &S,
+ InputSection<ELFT> &IS) {
+ if (this->getThunk(S))
+ // only one Thunk supported per symbol
+ return;
+
+ bool NeedsPI = Config->Pic || Config->Pie || Config->Shared;
+ Thunk<ELFT> *thunk;
+ // ARM relocations need ARM to Thumb interworking Thunks, Thumb relocations
+ // need Thumb to ARM relocations. Use position independent Thunks if we
+ // require position independent code.
+ switch (RelocType) {
+ case R_ARM_PC24:
+ case R_ARM_PLT32:
+ case R_ARM_JUMP24:
+ if (NeedsPI)
+ thunk = new (this->Alloc) ARMThumbV7PILongThunk<ELFT>(S, IS);
+ else
+ thunk = new (this->Alloc) ARMThumbV7ABSLongThunk<ELFT>(S, IS);
+ break;
+ case R_ARM_THM_JUMP19:
+ case R_ARM_THM_JUMP24:
+ if (NeedsPI)
+ thunk = new (this->Alloc) ThumbARMV7PILongThunk<ELFT>(S, IS);
+ else
+ thunk = new (this->Alloc) ThumbARMV7ABSLongThunk<ELFT>(S, IS);
+ break;
+ default:
+ fatal("Unrecognised Relocation type\n");
+ }
+ // ARM Thunks are added to the same InputSection as the relocation. This
+ // isn't strictly necessary but it makes it more likely that a limited range
+ // branch can reach the Thunk, and it makes Thunks to the PLT section easier
+ IS.addThunk(thunk);
+ this->registerThunk(S, thunk);
+}
+
+template <class ELFT>
+RelExpr ARMThunkInfo<ELFT>::adjustExprForThunk(RelExpr Expr,
+ uint32_t RelocType,
+ const InputFile &File,
+ const SymbolBody &S) const {
+ // A state change from ARM to Thumb and vice versa must go through an
+ // interworking thunk if the relocation type is not R_ARM_CALL or
+ // R_ARM_THM_CALL.
+ switch (RelocType) {
+ case R_ARM_PC24:
+ 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<ELFT>() & 1) == 1))
+ return R_THUNK_PC;
+ break;
+ 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)
+ return R_THUNK_PLT_PC;
+ else if ((S.getVA<ELFT>() & 1) == 0)
+ return R_THUNK_PC;
+ break;
+ }
+ return Expr;
+}
+
+template <class ELFT> class MipsThunkInfo final : public ThunkInfoBase<ELFT> {
+public:
+ void addThunk(uint32_t RelocType, const SymbolBody &S,
+ InputSection<ELFT> &IS) override;
+ RelExpr adjustExprForThunk(RelExpr Expr,
+ uint32_t RelocType, const InputFile &File,
+ const SymbolBody &S) const override;
+};
+
+template <class ELFT>
+void MipsThunkInfo<ELFT>::addThunk(uint32_t RelocType, const SymbolBody &S,
+ InputSection<ELFT> &) {
+ if (this->getThunk(S))
+ // only one Thunk supported per symbol
+ return;
+ // Mips Thunks are added to the InputSection defining S
+ auto *R = cast<DefinedRegular<ELFT>>(&S);
+ auto *Sec = cast<InputSection<ELFT>>(R->Section);
+ auto *T = new (this->Alloc) MipsThunk<ELFT>(S, *Sec);
+ Sec->addThunk(T);
+ this->registerThunk(S, T);
+}
+
+template <class ELFT>
+RelExpr MipsThunkInfo<ELFT>::adjustExprForThunk(RelExpr Expr,
+ uint32_t RelocType,
+ const InputFile &File,
+ const SymbolBody &S) const {
+ return Target->needsThunk(RelocType, File, S) ? R_THUNK_ABS : Expr;
+}
+
+template <class ELFT> ThunkInfo<ELFT> *createThunkInfo() {
+ switch (Config->EMachine) {
+ case EM_ARM:
+ return new ARMThunkInfo<ELFT>();
+ case EM_MIPS:
+ return new MipsThunkInfo<ELFT>();
+ default:
+ return new ThunkInfoBase<ELFT>();
+ }
+}
+
+template class ThunkInfo<ELF32LE>;
+template ThunkInfo<ELF32LE> *createThunkInfo<ELF32LE>();
+template class ThunkInfo<ELF32BE>;
+template ThunkInfo<ELF32BE> *createThunkInfo<ELF32BE>();
+template class ThunkInfo<ELF64LE>;
+template ThunkInfo<ELF64LE> *createThunkInfo<ELF64LE>();
+template class ThunkInfo<ELF64BE>;
+template ThunkInfo<ELF64BE> *createThunkInfo<ELF64BE>();
+
+} // namespace elf
+} // namespace lld
diff --git a/ELF/Thunks.h b/ELF/Thunks.h
new file mode 100644
index 000000000..bbfd28b05
--- /dev/null
+++ b/ELF/Thunks.h
@@ -0,0 +1,58 @@
+//===- Thunks.h --------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_ELF_THUNKS_H
+#define LLD_ELF_THUNKS_H
+
+#include "Relocations.h"
+
+namespace lld {
+namespace elf {
+class SymbolBody;
+class InputFile;
+template <class ELFT> class InputSection;
+template <class ELFT> class InputSectionBase;
+
+// Class to describe an instance of a Thunk
+template <class ELFT> class Thunk {
+public:
+ virtual uint32_t size() const = 0;
+ virtual typename ELFT::uint getVA() const = 0;
+ virtual void writeTo(uint8_t *Buf) const = 0;
+ virtual ~Thunk();
+};
+
+// Class to decide when Thunks are necessary and maintain the mapping
+// between SymbolBodies and the Thunks that target them.
+template <class ELFT> class ThunkInfo {
+public:
+ // Add a Thunk for Symbol S to InputSection Src for relocation type
+ // RelocType. If there is already a Thunk for S then do nothing.
+ virtual void addThunk(uint32_t RelocType, const SymbolBody &S,
+ InputSection<ELFT> &Src) = 0;
+ // Return the appropriate Expr if a Thunk is needed. Returns Expr
+ // unchanged otherwise.
+ virtual RelExpr adjustExprForThunk(RelExpr Expr, uint32_t RelocType,
+ const InputFile &File,
+ const SymbolBody &S) const = 0;
+ // Return the Thunk for S if it exists, otherwise return nullptr
+ virtual const Thunk<ELFT> *getThunk(const SymbolBody &S) const = 0;
+ virtual ~ThunkInfo();
+};
+
+// Thunks<ELFT>::X points to the Target specific implementation of ThunkInfo
+template <class ELFT> struct Thunks { static ThunkInfo<ELFT> *X; };
+template <class ELFT> ThunkInfo<ELFT> *Thunks<ELFT>::X;
+
+template <class ELFT> ThunkInfo<ELFT> *createThunkInfo();
+
+} // namespace elf
+} // namespace lld
+
+#endif
diff --git a/ELF/Writer.cpp b/ELF/Writer.cpp
index 28036d3db..cbae1c70f 100644
--- a/ELF/Writer.cpp
+++ b/ELF/Writer.cpp
@@ -22,6 +22,8 @@
#include "llvm/Support/StringSaver.h"
#include "llvm/Support/raw_ostream.h"
+#include <functional>
+
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
diff --git a/test/ELF/arm-mov-relocs.s b/test/ELF/arm-mov-relocs.s
index 6a254e8e0..31ccba4cc 100644
--- a/test/ELF/arm-mov-relocs.s
+++ b/test/ELF/arm-mov-relocs.s
@@ -41,16 +41,53 @@ _start:
// CHECK: movt r3, #2
// CHECK: movt r4, #3
+.section .R_ARM_MOVW_PREL_NC, "ax",%progbits
+ movw r0, :lower16:label - .
+ movw r1, :lower16:label1 - .
+ movw r2, :lower16:label2 + 4 - .
+ movw r3, :lower16:label3 - .
+ movw r4, :lower16:label3 + 0x103c - .
+// 0x20000 - 0x11028 = :lower16:0xefd8 (61400)
+// CHECK: 11028: {{.*}} movw r0, #61400
+// 0x20004 = 0x1102c = :lower16:0xefd8 (61400)
+// CHECK: 1102c: {{.*}} movw r1, #61400
+// 0x20008 - 0x11030 + 4 = :lower16:0xefdc (61404)
+// CHECK: 11030: {{.*}} movw r2, #61404
+// 0x2fffc - 0x11034 = :lower16:0x1efc8 (61384)
+// CHECK: 11034: {{.*}} movw r3, #61384
+// 0x2fffc - 0x11038 +0x103c :lower16:0x20000 (0)
+// CHECK: 11038: {{.*}} movw r4, #0
+
+.section .R_ARM_MOVT_PREL, "ax",%progbits
+ movt r0, :upper16:label - .
+ movt r1, :upper16:label1 - .
+ movt r2, :upper16:label2 + 0x4 - .
+ movt r3, :upper16:label3 - .
+ movt r4, :upper16:label3 + 0x1050 - .
+// 0x20000 - 0x1103c = :upper16:0xefc4 = 0
+// CHECK: 1103c: {{.*}} movt r0, #0
+// 0x20004 - 0x11040 = :upper16:0xefc0 = 0
+// CHECK: 11040: {{.*}} movt r1, #0
+// 0x20008 - 0x11044 + 4 = :upper16:0xefc8 = 0
+// CHECK: 11044: {{.*}} movt r2, #0
+// 0x2fffc - 0x11048 = :upper16:0x1efb4 = 1
+// CHECK: 11048: {{.*}} movt r3, #1
+// 0x2fffc - 0x1104c + 0x1050 = :upper16:0x20000 = 2
+// CHECK: 1104c: {{.*}} movt r4, #2
.section .destination, "aw",%progbits
.balign 65536
+// 0x20000
label:
.word 0
+// 0x20004
label1:
.word 1
+// 0x20008
label2:
.word 2
// Test label3 is immediately below 2^16 alignment boundary
.space 65536 - 16
+// 0x2fffc
label3:
.word 3
// label3 + 4 is on a 2^16 alignment boundary
diff --git a/test/ELF/arm-thumb-interwork-thunk.s b/test/ELF/arm-thumb-interwork-thunk.s
new file mode 100644
index 000000000..27b5e5f68
--- /dev/null
+++ b/test/ELF/arm-thumb-interwork-thunk.s
@@ -0,0 +1,375 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: echo "SECTIONS { \
+// RUN: .R_ARM_JUMP24_callee_1 : { *(.R_ARM_JUMP24_callee_low) } \
+// RUN: .R_ARM_THM_JUMP_callee_1 : { *(.R_ARM_THM_JUMP_callee_low)} \
+// RUN: .text : { *(.text) } \
+// RUN: .arm_caller : { *(.arm_caller) } \
+// RUN: .thumb_caller : { *(.thumb_caller) } \
+// RUN: .R_ARM_JUMP24_callee_2 : { *(.R_ARM_JUMP24_callee_high) } \
+// RUN: .R_ARM_THM_JUMP_callee_2 : { *(.R_ARM_THM_JUMP_callee_high) } } " > %t.script
+// RUN: ld.lld --script %t.script %t -o %t2 2>&1
+// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi %t2 | FileCheck -check-prefix=CHECK-THUMB -check-prefix=CHECK-ABS-THUMB %s
+// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t2 | FileCheck -check-prefix=CHECK-ARM -check-prefix=CHECK-ABS-ARM %s
+// RUN: ld.lld --script %t.script %t -pie -o %t3 2>&1
+// RUN: ld.lld --script %t.script %t --shared -o %t4 2>&1
+// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi %t3 | FileCheck -check-prefix=CHECK-THUMB -check-prefix=CHECK-PI-THUMB %s
+// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t3 | FileCheck -check-prefix=CHECK-ARM -check-prefix=CHECK-PI-ARM %s
+// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi %t4 | FileCheck -check-prefix=CHECK-THUMB -check-prefix=CHECK-PI-PLT-THUMB %s
+// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t4 | FileCheck -check-prefix=CHECK-ARM -check-prefix=CHECK-PI-PLT-ARM %s
+// RUN: llvm-readobj -s -r %t4 | FileCheck -check-prefix=CHECK-DSO-REL %s
+// REQUIRES: arm
+
+// Test ARM Thumb Interworking
+// The file is linked and checked 3 times to check the following contexts
+// - Absolute executables, absolute Thunks are used.
+// - Position independent executables, position independent Thunks are used.
+// - Shared object, position independent Thunks to PLT entries are used.
+
+ .syntax unified
+
+// Target Sections for thunks at a lower address than the callers.
+.section .R_ARM_JUMP24_callee_low, "ax", %progbits
+ .thumb
+ .balign 0x1000
+ .globl thumb_callee1
+ .type thumb_callee1, %function
+thumb_callee1:
+ bx lr
+
+// CHECK-THUMB: Disassembly of section .R_ARM_JUMP24_callee_1:
+// CHECK-THUMB: thumb_callee1:
+// CHECK-THUMB: 1000: 70 47 bx
+ .section .R_ARM_THM_JUMP_callee_low, "ax", %progbits
+ .arm
+ .balign 0x100
+ .globl arm_callee1
+ .type arm_callee1, %function
+arm_callee1:
+ bx lr
+// Disassembly of section .R_ARM_THM_JUMP_callee_1:
+// CHECK-ARM: arm_callee1:
+// CHECK-ARM-NEXT: 1100: 1e ff 2f e1 bx lr
+
+ // Calling sections
+ // At present ARM and Thumb interworking thunks are always added to the calling
+ // section.
+ .section .arm_caller, "ax", %progbits
+ .arm
+ .balign 0x100
+ .globl arm_caller
+ .type arm_caller, %function
+arm_caller:
+ // If target supports BLX and target is in range we don't need an
+ // interworking thunk for a BL or BLX instruction.
+ bl thumb_callee1
+ blx thumb_callee1
+ // A B instruction can't be transformed into a BLX and needs an interworking
+ // thunk
+ b thumb_callee1
+ // As long as the thunk is in range it can be reused
+ b thumb_callee1
+ // There can be more than one thunk associated with a section
+ b thumb_callee2
+ b thumb_callee3
+ // In range ARM targets do not require interworking thunks
+ b arm_callee1
+ beq arm_callee2
+ bne arm_callee3
+ bx lr
+// CHECK-ABS-ARM: Disassembly of section .arm_caller:
+// CHECK-ABS-ARM-NEXT: arm_caller:
+// CHECK-ABS-ARM-NEXT: 1300: 3e ff ff fa blx #-776 <thumb_callee1>
+// CHECK-ABS-ARM-NEXT: 1304: 3d ff ff fa blx #-780 <thumb_callee1>
+// CHECK-ABS-ARM-NEXT: 1308: 06 00 00 ea b #24 <arm_caller+0x28>
+// CHECK-ABS-ARM-NEXT: 130c: 05 00 00 ea b #20 <arm_caller+0x28>
+// CHECK-ABS-ARM-NEXT: 1310: 07 00 00 ea b #28 <arm_caller+0x34>
+// CHECK-ABS-ARM-NEXT: 1314: 09 00 00 ea b #36 <arm_caller+0x40>
+// CHECK-ABS-ARM-NEXT: 1318: 78 ff ff ea b #-544 <arm_callee1>
+// CHECK-ABS-ARM-NEXT: 131c: b7 00 00 0a beq #732 <arm_callee2>
+// CHECK-ABS-ARM-NEXT: 1320: b7 00 00 1a bne #732 <arm_callee3>
+// CHECK-ABS-ARM-NEXT: 1324: 1e ff 2f e1 bx lr
+// 0x1001 = thumb_callee1
+// CHECK-ABS-ARM-NEXT: 1328: 01 c0 01 e3 movw r12, #4097
+// CHECK-ABS-ARM-NEXT: 132c: 00 c0 40 e3 movt r12, #0
+// CHECK-ABS-ARM-NEXT: 1330: 1c ff 2f e1 bx r12
+// 0x1501 = thumb_callee2
+// CHECK-ABS-ARM-NEXT: 1334: 01 c5 01 e3 movw r12, #5377
+// CHECK-ABS-ARM-NEXT: 1338: 00 c0 40 e3 movt r12, #0
+// CHECK-ABS-ARM-NEXT: 133c: 1c ff 2f e1 bx r12
+// 0x1503 = thumb_callee3
+// CHECK-ABS-ARM-NEXT: 1340: 03 c5 01 e3 movw r12, #5379
+// CHECK-ABS-ARM-NEXT: 1344: 00 c0 40 e3 movt r12, #0
+// CHECK-ABS-ARM-NEXT: 1348: 1c ff 2f e1 bx r12
+
+// CHECK-PI-ARM: Disassembly of section .arm_caller:
+// CHECK-PI-ARM-NEXT: arm_caller:
+// CHECK-PI-ARM-NEXT: 1300: 3e ff ff fa blx #-776 <thumb_callee1>
+// CHECK-PI-ARM-NEXT: 1304: 3d ff ff fa blx #-780 <thumb_callee1>
+// 0x1308 + 8 + 0x18 = 0x1328
+// CHECK-PI-ARM-NEXT: 1308: 06 00 00 ea b #24 <arm_caller+0x28>
+// 0x130c + 8 + 0x14 = 0x1328
+// CHECK-PI-ARM-NEXT: 130c: 05 00 00 ea b #20 <arm_caller+0x28>
+// 0x1310 + 8 + 0x20 = 0x1338
+// CHECK-PI-ARM-NEXT: 1310: 08 00 00 ea b #32 <arm_caller+0x38>
+// 0x1314 + 8 + 0x2c = 0x1348
+// CHECK-PI-ARM-NEXT: 1314: 0b 00 00 ea b #44 <arm_caller+0x48>
+// CHECK-PI-ARM-NEXT: 1318: 78 ff ff ea b #-544 <arm_callee1>
+// CHECK-PI-ARM-NEXT: 131c: b7 00 00 0a beq #732 <arm_callee2>
+// CHECK-PI-ARM-NEXT: 1320: b7 00 00 1a bne #732 <arm_callee3>
+// CHECK-PI-ARM-NEXT: 1324: 1e ff 2f e1 bx lr
+// 0x1330 + 8 - 0x337 = 0x1001 = thumb_callee1
+// CHECK-PI-ARM-NEXT: 1328: c9 cc 0f e3 movw r12, #64713
+// CHECK-PI-ARM-NEXT: 132c: ff cf 4f e3 movt r12, #65535
+// CHECK-PI-ARM-NEXT: 1330: 0f c0 8c e0 add r12, r12, pc
+// CHECK-PI-ARM-NEXT: 1334: 1c ff 2f e1 bx r12
+// 0x1340 + 8 + 0x1b9 = 0x1501
+// CHECK-PI-ARM-NEXT: 1338: b9 c1 00 e3 movw r12, #441
+// CHECK-PI-ARM-NEXT: 133c: 00 c0 40 e3 movt r12, #0
+// CHECK-PI-ARM-NEXT: 1340: 0f c0 8c e0 add r12, r12, pc
+// CHECK-PI-ARM-NEXT: 1344: 1c ff 2f e1 bx r12
+// 1350 + 8 + 0x1ab = 0x1503
+// CHECK-PI-ARM-NEXT: 1348: ab c1 00 e3 movw r12, #427
+// CHECK-PI-ARM-NEXT: 134c: 00 c0 40 e3 movt r12, #0
+// CHECK-PI-ARM-NEXT: 1350: 0f c0 8c e0 add r12, r12, pc
+// CHECK-PI-ARM-NEXT: 1354: 1c ff 2f e1 bx r12
+
+// All PLT entries are ARM, no need for interworking thunks
+// CHECK-PI-ARM-PLT: Disassembly of section .arm_caller:
+// CHECK-PI-ARM-PLT-NEXT: arm_caller:
+// 0x17e4 PLT(thumb_callee1)
+// CHECK-PI-ARM-PLT-NEXT: 1300: 37 01 00 eb bl #1244
+// 0x17e4 PLT(thumb_callee1)
+// CHECK-PI-ARM-PLT-NEXT: 1304: 36 01 00 eb bl #1240
+// 0x17e4 PLT(thumb_callee1)
+// CHECK-PI-ARM-PLT-NEXT: 1308: 35 01 00 ea b #1236
+// 0x17e4 PLT(thumb_callee1)
+// CHECK-PI-ARM-PLT-NEXT: 130c: 34 01 00 ea b #1232
+// 0x17f4 PLT(thumb_callee2)
+// CHECK-PI-ARM-PLT-NEXT: 1310: 37 01 00 ea b #1244
+// 0x1804 PLT(thumb_callee3)
+// CHECK-PI-ARM-PLT-NEXT: 1314: 3a 01 00 ea b #1256
+// 0x1814 PLT(arm_callee1)
+// CHECK-PI-ARM-PLT-NEXT: 1318: 3d 01 00 ea b #1268
+// 0x1824 PLT(arm_callee2)
+// CHECK-PI-ARM-PLT-NEXT: 131c: 40 01 00 0a beq #1280
+// 0x1834 PLT(arm_callee3)
+// CHECK-PI-ARM-PLT-NEXT: 1320: 43 01 00 1a bne #1292
+// CHECK-PI-ARM-PLT-NEXT: 1324: 1e ff 2f e1 bx lr
+
+ .section .thumb_caller, "ax", %progbits
+ .balign 0x100
+ .thumb
+ .globl thumb_caller
+ .type thumb_caller, %function
+thumb_caller:
+ // If target supports BLX and target is in range we don't need an
+ // interworking thunk for a BL or BLX instruction.
+ bl arm_callee1
+ blx arm_callee1
+ // A B instruction can't be transformed into a BLX and needs an interworking
+ // thunk
+ b.w arm_callee1
+ // As long as the thunk is in range it can be reused
+ b.w arm_callee2
+ // There can be more than one thunk associated with a section
+ b.w arm_callee3
+ // Conditional branches also require interworking thunks, they can use the
+ // same interworking thunks.
+ beq.w arm_callee1
+ beq.w arm_callee2
+ bne.w arm_callee3
+// CHECK-ABS-THUMB: Disassembly of section .thumb_caller:
+// CHECK-ABS-THUMB-NEXT: thumb_caller:
+// 0x1400 + 4 - 0x304 = 0x1100 = arm_callee1
+// CHECK-ABS-THUMB-NEXT: 1400: ff f7 7e ee blx #-772
+// 0x1404 + 4 - 0x308 = 0x1100 = arm_callee1
+// CHECK-ABS-THUMB-NEXT: 1404: ff f7 7c ee blx #-776
+// 0x1408 + 4 + 0x14 = 0x520
+// CHECK-ABS-THUMB-NEXT: 1408: 00 f0 0a b8 b.w #20
+// 0x140c + 4 + 0x1a = 0x52a
+// CHECK-ABS-THUMB-NEXT: 140c: 00 f0 0d b8 b.w #26
+// 0x1410 + 4 + 0x20 = 0x534
+// CHECK-ABS-THUMB-NEXT: 1410: 00 f0 10 b8 b.w #32
+// 0x1414 + 4 + 8 = 0x520
+// CHECK-ABS-THUMB-NEXT: 1414: 00 f0 04 80 beq.w #8
+// 0x1418 + 4 + 0xe = 0x52a
+// CHECK-ABS-THUMB-NEXT: 1418: 00 f0 07 80 beq.w #14
+// 0x141c + 4 + 0x14 = 0x534
+// CHECK-ABS-THUMB-NEXT: 141c: 40 f0 0a 80 bne.w #20
+// 0x1100 = arm_callee1
+// CHECK-ABS-THUMB-NEXT: 1420: 41 f2 00 1c movw r12, #4352
+// CHECK-ABS-THUMB-NEXT: 1424: c0 f2 00 0c movt r12, #0
+// CHECK-ABS-THUMB-NEXT: 1428: 60 47 bx r12
+// 0x1600 = arm_callee2
+// CHECK-ABS-THUMB-NEXT: 142a: 41 f2 00 6c movw r12, #5632
+// CHECK-ABS-THUMB-NEXT: 142e: c0 f2 00 0c movt r12, #0
+// CHECK-ABS-THUMB-NEXT: 1432: 60 47 bx r12
+// 0x1604 = arm_callee3
+// CHECK-ABS-THUMB-NEXT: 1434: 41 f2 04 6c movw r12, #5636
+// CHECK-ABS-THUMB-NEXT: 1438: c0 f2 00 0c movt r12, #0
+// CHECK-ABS-THUMB-NEXT: 143c: 60 47 bx r12
+
+// CHECK-PI-THUMB: Disassembly of section .thumb_caller:
+// CHECK-PI-THUMB-NEXT: thumb_caller:
+// CHECK-PI-THUMB-NEXT: 1400: ff f7 7e ee blx #-772
+// CHECK-PI-THUMB-NEXT: 1404: ff f7 7c ee blx #-776
+// CHECK-PI-THUMB-NEXT: 1408: 00 f0 0a b8 b.w #20
+// CHECK-PI-THUMB-NEXT: 140c: 00 f0 0e b8 b.w #28
+// CHECK-PI-THUMB-NEXT: 1410: 00 f0 12 b8 b.w #36
+// CHECK-PI-THUMB-NEXT: 1414: 00 f0 04 80 beq.w #8
+// CHECK-PI-THUMB-NEXT: 1418: 00 f0 08 80 beq.w #16
+// CHECK-PI-THUMB-NEXT: 141c: 40 f0 0c 80 bne.w #24
+// 0x1428 + 4 - 0x32c = 0x1100 = arm_callee1
+// CHECK-PI-THUMB-NEXT: 1420: 4f f6 d4 4c movw r12, #64724
+// CHECK-PI-THUMB-NEXT: 1424: cf f6 ff 7c movt r12, #65535
+// CHECK-PI-THUMB-NEXT: 1428: fc 44 add r12, pc
+// CHECK-PI-THUMB-NEXT: 142a: 60 47 bx r12
+// 0x1434 + 4 + 0x1c8 = 0x1600 = arm_callee2
+// CHECK-PI-THUMB-NEXT: 142c: 40 f2 c8 1c movw r12, #456
+// CHECK-PI-THUMB-NEXT: 1430: c0 f2 00 0c movt r12, #0
+// CHECK-PI-THUMB-NEXT: 1434: fc 44 add r12, pc
+// CHECK-PI-THUMB-NEXT: 1436: 60 47 bx r12
+// 0x1440 + 4 + 0x1c0 = 0x1604 = arm_callee3
+// CHECK-PI-THUMB-NEXT: 1438: 40 f2 c0 1c movw r12, #448
+// CHECK-PI-THUMB-NEXT: 143c: c0 f2 00 0c movt r12, #0
+// CHECK-PI-THUMB-NEXT: 1440: fc 44 add r12, pc
+// CHECK-PI-THUMB-NEXT: 1442: 60 47 bx r12
+
+// CHECK-PI-THUMB-PLT: Disassembly of section .arm_caller:
+// CHECK-PI-THUMB-PLT-NEXT: thumb_caller:
+// 0x1400 + 4 + 0x410 = 0x1814 = PLT(arm_callee1)
+// CHECK-PI-THUMB-PLT-NEXT: 1400: 00 f0 08 ea blx #1040
+// 0x1404 + 4 + 0x40c = 0x1814 = PLT(arm_callee1)
+// CHECK-PI-THUMB-PLT-NEXT: 1404: 00 f0 06 ea blx #1036
+// 0x1408 + 4 + 0x14 = 0x1420 = IWV(PLT(arm_callee1)
+// CHECK-PI-THUMB-PLT-NEXT: 1408: 00 f0 0a b8 b.w #20
+// 0x140c + 4 + 0x1c = 0x142c = IWV(PLT(arm_callee2)
+// CHECK-PI-THUMB-PLT-NEXT: 140c: 00 f0 0e b8 b.w #28
+// 0x1410 + 4 + 0x24 = 0x1438 = IWV(PLT(arm_callee3)
+// CHECK-PI-THUMB-PLT-NEXT: 1410: 00 f0 12 b8 b.w #36
+// 0x1414 + 4 + 8 = 0x1420 = IWV(PLT(arm_callee1)
+// CHECK-PI-THUMB-PLT-NEXT: 1414: 00 f0 04 80 beq.w #8
+// 0x1418 + 4 + 0x10 = 0x142c = IWV(PLT(arm_callee2)
+// CHECK-PI-THUMB-PLT-NEXT: 1418: 00 f0 08 80 beq.w #16
+// 0x141c + 4 + 0x18 = 0x1438 = IWV(PLT(arm_callee3)
+// CHECK-PI-THUMB-PLT-NEXT: 141c: 40 f0 0c 80 bne.w #24
+// 0x1428 + 4 + 0x3e8 = 0x1814 = PLT(arm_callee1)
+// CHECK-PI-THUMB-PLT-NEXT: 1420: 40 f2 e8 3c movw r12, #1000
+// CHECK-PI-THUMB-PLT-NEXT: 1424: c0 f2 00 0c movt r12, #0
+// CHECK-PI-THUMB-PLT-NEXT: 1428: fc 44 add r12, pc
+// CHECK-PI-THUMB-PLT-NEXT: 142a: 60 47 bx r12
+// 0x1434 + 4 + 0x3ec = 0x1824 = PLT(arm_callee2)
+// CHECK-PI-THUMB-PLT-NEXT: 142c: 40 f2 ec 3c movw r12, #1004
+// CHECK-PI-THUMB-PLT-NEXT: 1430: c0 f2 00 0c movt r12, #0
+// CHECK-PI-THUMB-PLT-NEXT: 1434: fc 44 add r12, pc
+// CHECK-PI-THUMB-PLT-NEXT: 1436: 60 47 bx r12
+// 0x1440 + 4 + 0x3f0 = 0x1834 = PLT(arm_callee3)
+// CHECK-PI-THUMB-PLT-NEXT: 1438: 40 f2 f0 3c movw r12, #1008
+// CHECK-PI-THUMB-PLT-NEXT: 143c: c0 f2 00 0c movt r12, #0
+// CHECK-PI-THUMB-PLT-NEXT: 1440: fc 44 add r12, pc
+// CHECK-PI-THUMB-PLT-NEXT: 1442: 60 47 bx r12
+
+// Target Sections for thunks at a higher address than the callers.
+.section .R_ARM_JUMP24_callee_high, "ax", %progbits
+ .thumb
+ .balign 0x100
+ .globl thumb_callee2
+ .type thumb_callee2, %function
+thumb_callee2:
+ bx lr
+
+ .globl thumb_callee3
+ .type thumb_callee3, %function
+thumb_callee3:
+ bx lr
+// CHECK-THUMB: Disassembly of section .R_ARM_JUMP24_callee_2:
+// CHECK-THUMB-NEXT: thumb_callee2:
+// CHECK-THUMB-NEXT: 1500: 70 47 bx lr
+// CHECK-THUMB: thumb_callee3:
+// CHECK-THUMB-NEXT: 1502: 70 47 bx lr
+
+ .section .R_ARM_THM_JUMP_callee_high, "ax", %progbits
+ .arm
+ .balign 0x100
+ .globl arm_callee2
+ .type arm_callee2, %function
+arm_callee2:
+ bx lr
+ .globl arm_callee3
+ .type arm_callee3, %function
+arm_callee3:
+ bx lr
+// CHECK-ARM: Disassembly of section .R_ARM_THM_JUMP_callee_2:
+// CHECK-ARM-NEXT: arm_callee2:
+// CHECK-ARM-NEXT: 1600: 1e ff 2f e1 bx lr
+// CHECK-ARM: arm_callee3:
+// CHECK-ARM-NEXT: 1604: 1e ff 2f e1 bx lr
+
+// _start section just calls the arm and thumb calling sections
+ .text
+ .arm
+ .globl _start
+ .balign 0x100
+ .type _start, %function
+_start:
+ bl arm_caller
+ bl thumb_caller
+ bx lr
+
+
+// CHECK-PI-ARM-PLT: Disassembly of section .plt:
+// CHECK-PI-ARM-PLT-NEXT: .plt:
+// CHECK-PI-ARM-PLT-NEXT: 17b0: 04 e0 2d e5 str lr, [sp, #-4]!
+// CHECK-PI-ARM-PLT-NEXT: 17b4: 04 e0 9f e5 ldr lr, [pc, #4]
+// CHECK-PI-ARM-PLT-NEXT: 17b8: 0e e0 8f e0 add lr, pc, lr
+// CHECK-PI-ARM-PLT-NEXT: 17bc: 08 f0 be e5 ldr pc, [lr, #8]!
+// CHECK-PI-ARM-PLT-NEXT: 17c0: d4 00 00 00
+// 0x17c8 + 8 + 0xd0 = 0x18a0 arm_caller
+// CHECK-PI-ARM-PLT-NEXT: 17c4: 04 c0 9f e5 ldr r12, [pc, #4]
+// CHECK-PI-ARM-PLT-NEXT: 17c8: 0f c0 8c e0 add r12, r12, pc
+// CHECK-PI-ARM-PLT-NEXT: 17cc: 00 f0 9c e5 ldr pc, [r12]
+// CHECK-PI-ARM-PLT-NEXT: 17d0: d0 00 00 00
+// 0x17d8 + 8 + 0xc4 = 0x18a4 thumb_caller
+// CHECK-PI-ARM-PLT-NEXT: 17d4: 04 c0 9f e5 ldr r12, [pc, #4]
+// CHECK-PI-ARM-PLT-NEXT: 17d8: 0f c0 8c e0 add r12, r12, pc
+// CHECK-PI-ARM-PLT-NEXT: 17dc: 00 f0 9c e5 ldr pc, [r12]
+// CHECK-PI-ARM-PLT-NEXT: 17e0: c4 00 00 00
+// 0x17e8 + 8 + 0xb8 = 0x18a8 thumb_callee1
+// CHECK-PI-ARM-PLT-NEXT: 17e4: 04 c0 9f e5 ldr r12, [pc, #4]
+// CHECK-PI-ARM-PLT-NEXT: 17e8: 0f c0 8c e0 add r12, r12, pc
+// CHECK-PI-ARM-PLT-NEXT: 17ec: 00 f0 9c e5 ldr pc, [r12]
+// CHECK-PI-ARM-PLT-NEXT: 17f0: b8 00 00 00
+// 0x17f8 + 8 + 0xac = 0x18ac thumb_callee2
+// CHECK-PI-ARM-PLT-NEXT: 17f4: 04 c0 9f e5 ldr r12, [pc, #4]
+// CHECK-PI-ARM-PLT-NEXT: 17f8: 0f c0 8c e0 add r12, r12, pc
+// CHECK-PI-ARM-PLT-NEXT: 17fc: 00 f0 9c e5 ldr pc, [r12]
+// CHECK-PI-ARM-PLT-NEXT: 1800: ac 00 00 00
+// 0x1808 + 8 + 0xa0 = 0x18b0 thumb_callee3
+// CHECK-PI-ARM-PLT-NEXT: 1804: 04 c0 9f e5 ldr r12, [pc, #4]
+// CHECK-PI-ARM-PLT-NEXT: 1808: 0f c0 8c e0 add r12, r12, pc
+// CHECK-PI-ARM-PLT-NEXT: 180c: 00 f0 9c e5 ldr pc, [r12]
+// CHECK-PI-ARM-PLT-NEXT: 1810: a0 00 00 00
+// 0x1818 + 8 + 0x94 = 0x18b4 arm_callee1
+// CHECK-PI-ARM-PLT-NEXT: 1814: 04 c0 9f e5 ldr r12, [pc, #4]
+// CHECK-PI-ARM-PLT-NEXT: 1818: 0f c0 8c e0 add r12, r12, pc
+// CHECK-PI-ARM-PLT-NEXT: 181c: 00 f0 9c e5 ldr pc, [r12]
+// CHECK-PI-ARM-PLT-NEXT: 1820: 94 00 00 00
+// 0x1828 + 8 + 0x88 = 0x18b8 arm_callee2
+// CHECK-PI-ARM-PLT-NEXT: 1824: 04 c0 9f e5 ldr r12, [pc, #4]
+// CHECK-PI-ARM-PLT-NEXT: 1828: 0f c0 8c e0 add r12, r12, pc
+// CHECK-PI-ARM-PLT-NEXT: 182c: 00 f0 9c e5 ldr pc, [r12]
+// CHECK-PI-ARM-PLT-NEXT: 1830: 88 00 00 00
+// 0x1838 + 8 + 0x7c = 0x18bc arm_callee3
+// CHECK-PI-ARM-PLT-NEXT: 1834: 04 c0 9f e5 ldr r12, [pc, #4]
+// CHECK-PI-ARM-PLT-NEXT: 1838: 0f c0 8c e0 add r12, r12, pc
+// CHECK-PI-ARM-PLT-NEXT: 183c: 00 f0 9c e5 ldr pc, [r12]
+// CHECK-PI-ARM-PLT-NEXT: 1840: 7c 00 00 00
+
+// CHECK-DSO-REL: 0x18A0 R_ARM_JUMP_SLOT arm_caller
+// CHECK-DSO-REL-NEXT: 0x18A4 R_ARM_JUMP_SLOT thumb_caller
+// CHECK-DSO-REL-NEXT: 0x18A8 R_ARM_JUMP_SLOT thumb_callee1
+// CHECK-DSO-REL-NEXT: 0x18AC R_ARM_JUMP_SLOT thumb_callee2
+// CHECK-DSO-REL-NEXT: 0x18B0 R_ARM_JUMP_SLOT thumb_callee3
+// CHECK-DSO-REL-NEXT: 0x18B4 R_ARM_JUMP_SLOT arm_callee1
+// CHECK-DSO-REL-NEXT: 0x18B8 R_ARM_JUMP_SLOT arm_callee2
+// CHECK-DSO-REL-NEXT: 0x18BC R_ARM_JUMP_SLOT arm_callee3