Recommit R274836 Add Thunk support framework for ARM and Mips
The TinyPtrVector of const Thunk<ELFT>* in InputSections.h can cause
build failures on certain compiler/library combinations when Thunk<ELFT>
is not a complete type or is an abstract class. Fixed by making Thunk<ELFT>
non Abstract.
type or is an abstract class
llvm-svn: 274863
diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt
index 1875a1e..a1b65ad 100644
--- a/lld/ELF/CMakeLists.txt
+++ b/lld/ELF/CMakeLists.txt
@@ -21,6 +21,7 @@
SymbolTable.cpp
Symbols.cpp
Target.cpp
+ Thunks.cpp
Writer.cpp
LINK_COMPONENTS
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index 3d3f34d..97c273b 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/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"
@@ -128,9 +129,9 @@
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> *T) {
+ Thunks.push_back(T);
}
template <class ELFT> uint64_t InputSection<ELFT>::getThunkOff() const {
@@ -138,7 +139,10 @@
}
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
@@ -183,8 +187,11 @@
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 Body.getThunkVA<ELFT>() + A;
+ case R_THUNK_PC:
+ case R_THUNK_PLT_PC:
+ return Body.getThunkVA<ELFT>() + A - P;
case R_PPC_TOC:
return getPPC64TocBase() + A;
case R_TLSGD:
@@ -404,9 +411,9 @@
// 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/lld/ELF/InputSection.h b/lld/ELF/InputSection.h
index 9c874d4..eabe5fb 100644
--- a/lld/ELF/InputSection.h
+++ b/lld/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"
@@ -208,8 +209,8 @@
// 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> *T);
// The offset of synthetic thunk code from beginning of this section.
uint64_t getThunkOff() const;
@@ -230,7 +231,7 @@
// 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/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index a54c449..e39eef7 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/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 @@
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 @@
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 @@
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 @@
if (Expr == R_GOT_PC)
Expr = Target->adjustRelaxExpr(Type, Data, Expr);
}
+ Expr = Target->getThunkExpr(Expr, Type, File, Body);
if (IsWrite || isStaticLinkTimeConstant<ELFT>(Expr, Type, Body))
return Expr;
@@ -556,7 +557,8 @@
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 @@
// 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);
+ addThunk<ELFT>(Type, Body, *Sec);
}
// At this point we are done with the relocated position. Some relocations
diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index bac5276..4c1c74e 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -52,7 +52,9 @@
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/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp
index 3e96b91..85c71e30 100644
--- a/lld/ELF/Symbols.cpp
+++ b/lld/ELF/Symbols.cpp
@@ -134,6 +134,14 @@
return true;
}
+template <class ELFT> bool SymbolBody::hasThunk() const {
+ if (auto *DR = dyn_cast<DefinedRegular<ELFT>>(this))
+ return DR->ThunkData != nullptr;
+ if (auto *S = dyn_cast<SharedSymbol<ELFT>>(this))
+ return S->ThunkData != nullptr;
+ return false;
+}
+
template <class ELFT> InputFile *SymbolBody::getSourceFile() {
if (auto *S = dyn_cast<DefinedRegular<ELFT>>(this))
return S->Section ? S->Section->getFile() : nullptr;
@@ -174,10 +182,11 @@
}
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;
+ if (const auto *DR = dyn_cast<DefinedRegular<ELFT>>(this))
+ return DR->ThunkData->getVA();
+ if (const auto *S = dyn_cast<SharedSymbol<ELFT>>(this))
+ return S->ThunkData->getVA();
+ fatal("getThunkVA() not supported for Symbol class\n");
}
template <class ELFT> typename ELFT::uint SymbolBody::getSize() const {
@@ -250,6 +259,10 @@
return (ExportDynamic && VersionId != VER_NDX_LOCAL) || body()->isShared() ||
(body()->isUndefined() && Config->Shared);
}
+template bool SymbolBody::hasThunk<ELF32LE>() const;
+template bool SymbolBody::hasThunk<ELF32BE>() const;
+template bool SymbolBody::hasThunk<ELF64LE>() const;
+template bool SymbolBody::hasThunk<ELF64BE>() const;
template InputFile *SymbolBody::template getSourceFile<ELF32LE>();
template InputFile *SymbolBody::template getSourceFile<ELF32BE>();
@@ -276,6 +289,11 @@
template uint64_t SymbolBody::template getGotPltVA<ELF64LE>() const;
template uint64_t SymbolBody::template getGotPltVA<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 uint32_t SymbolBody::template getGotPltOffset<ELF32LE>() const;
template uint32_t SymbolBody::template getGotPltOffset<ELF32BE>() const;
template uint64_t SymbolBody::template getGotPltOffset<ELF64LE>() const;
@@ -291,11 +309,6 @@
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/lld/ELF/Symbols.h b/lld/ELF/Symbols.h
index e429f03..ec97b01 100644
--- a/lld/ELF/Symbols.h
+++ b/lld/ELF/Symbols.h
@@ -84,11 +84,10 @@
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> bool hasThunk() const;
template <class ELFT>
typename ELFT::uint getVA(typename ELFT::uint Addend = 0) const;
@@ -229,6 +228,10 @@
// If this is null, the symbol is an absolute symbol.
InputSectionBase<ELFT> *&Section;
+ // If non-null the symbol has a Thunk that may be used as an alternative
+ // destination for callers of this Symbol.
+ std::unique_ptr<Thunk<ELFT>> ThunkData;
+
private:
static InputSectionBase<ELFT> *NullInputSection;
};
@@ -301,6 +304,9 @@
// OffsetInBss is significant only when needsCopy() is true.
uintX_t OffsetInBss = 0;
+ // If non-null the symbol has a Thunk that may be used as an alternative
+ // destination for callers of this Symbol.
+ std::unique_ptr<Thunk<ELFT>> ThunkData;
bool needsCopy() const { return this->NeedsCopyOrPltAddr && !this->isFunc(); }
};
diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp
index 58f3beb..51d18c3 100644
--- a/lld/ELF/Target.cpp
+++ b/lld/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"
@@ -181,6 +182,9 @@
void writePltHeader(uint8_t *Buf) const override;
void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const override;
+ RelExpr getThunkExpr(RelExpr Expr, uint32_t RelocType,
+ const InputFile &File,
+ const SymbolBody &S) const override;
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
};
@@ -197,8 +201,9 @@
void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const override;
void writeThunk(uint8_t *Buf, uint64_t S) const override;
- bool needsThunk(uint32_t Type, const InputFile &File,
- const SymbolBody &S) const override;
+ RelExpr getThunkExpr(RelExpr Expr, uint32_t RelocType,
+ const InputFile &File,
+ 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;
};
@@ -248,9 +253,10 @@
bool TargetInfo::usesOnlyLowPageBits(uint32_t Type) const { return false; }
-bool TargetInfo::needsThunk(uint32_t Type, const InputFile &File,
- const SymbolBody &S) const {
- return false;
+RelExpr TargetInfo::getThunkExpr(RelExpr Expr, uint32_t RelocType,
+ const InputFile &File,
+ const SymbolBody &S) const {
+ return Expr;
}
bool TargetInfo::isTlsInitialExecRel(uint32_t Type) const { return false; }
@@ -1483,8 +1489,12 @@
// 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;
}
}
@@ -1532,6 +1542,34 @@
write32le(Buf + 12, GotEntryAddr - L1 - 8);
}
+RelExpr ARMTargetInfo::getThunkExpr(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<ELF32LE>() & 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;
+ if ((S.getVA<ELF32LE>() & 1) == 0)
+ return R_THUNK_PC;
+ break;
+ }
+ return Expr;
+}
+
void ARMTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
uint64_t Val) const {
switch (Type) {
@@ -1615,17 +1653,20 @@
((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
@@ -1636,6 +1677,7 @@
((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
@@ -1682,8 +1724,8 @@
((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
@@ -1698,12 +1740,16 @@
// 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);
@@ -1720,7 +1766,6 @@
PageSize = 65536;
PltEntrySize = 16;
PltHeaderSize = 32;
- ThunkSize = 16;
CopyRel = R_MIPS_COPY;
PltRel = R_MIPS_JUMP_SLOT;
if (ELFT::Is64Bits) {
@@ -1887,28 +1932,31 @@
}
template <class ELFT>
-bool MipsTargetInfo<ELFT>::needsThunk(uint32_t Type, const InputFile &File,
- const SymbolBody &S) const {
+RelExpr MipsTargetInfo<ELFT>::getThunkExpr(RelExpr Expr, uint32_t Type,
+ const InputFile &File,
+ 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
// to save the target function address.
// See page 3-38 ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
if (Type != R_MIPS_26)
- return false;
+ return Expr;
auto *F = dyn_cast<ELFFileBase<ELFT>>(&File);
if (!F)
- return false;
+ return Expr;
// If current file has PIC code, LA25 stub is not required.
if (F->getObj().getHeader()->e_flags & EF_MIPS_PIC)
- return false;
+ return Expr;
auto *D = dyn_cast<DefinedRegular<ELFT>>(&S);
if (!D || !D->Section)
- return false;
+ return Expr;
// LA25 is required if target file has PIC code
// or target symbol is a PIC symbol.
- return (D->Section->getFile()->getObj().getHeader()->e_flags & EF_MIPS_PIC) ||
- (D->StOther & STO_MIPS_MIPS16) == STO_MIPS_PIC;
+ const ELFFile<ELFT> &DefFile = D->Section->getFile()->getObj();
+ bool PicFile = DefFile.getHeader()->e_flags & EF_MIPS_PIC;
+ bool PicSym = (D->StOther & STO_MIPS_MIPS16) == STO_MIPS_PIC;
+ return (PicFile || PicSym) ? R_THUNK_ABS : Expr;
}
template <class ELFT>
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 822d38a..7adbf97 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -48,11 +48,16 @@
// a dynamic relocation.
virtual bool usesOnlyLowPageBits(uint32_t Type) const;
- virtual bool needsThunk(uint32_t Type, const InputFile &File,
- const SymbolBody &S) const;
-
+ // Decide whether a Thunk is needed for the relocation from File
+ // targeting S. Returns one of:
+ // Expr if there is no Thunk required
+ // R_THUNK_ABS if thunk is required and expression is absolute
+ // R_THUNK_PC if thunk is required and expression is pc rel
+ // R_THUNK_PLT_PC if thunk is required to PLT entry and expression is pc rel
+ virtual RelExpr getThunkExpr(RelExpr Expr, uint32_t RelocType,
+ const InputFile &File,
+ const SymbolBody &S) const;
virtual void writeThunk(uint8_t *Buf, uint64_t S) const {}
-
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 +92,6 @@
// 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/lld/ELF/Thunks.cpp b/lld/ELF/Thunks.cpp
new file mode 100644
index 0000000..15d8c7e
--- /dev/null
+++ b/lld/ELF/Thunks.cpp
@@ -0,0 +1,251 @@
+//===- 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
+// classes
+//
+// A Thunk Object represents a single Thunk that will be written to an
+// InputSection when the InputSection contents are written. The InputSection
+// maintains a list of Thunks that it owns.
+//===---------------------------------------------------------------------===//
+
+#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() {}
+
+template <class ELFT>
+Thunk<ELFT>::Thunk(const SymbolBody &D, const InputSection<ELFT> &O)
+ : Destination(D), Owner(O), Offset(O.getThunkOff() + O.getThunksSize()) {}
+
+template <class ELFT> typename ELFT::uint Thunk<ELFT>::getVA() const {
+ return Owner.OutSec->getVA() + Owner.OutSecOff + Offset;
+}
+
+// ARM Target Thunks
+template <class ELFT> static uint64_t getARMThunkDestVA(const SymbolBody &S) {
+ return S.isInPlt() ? S.getPltVA<ELFT>() : S.getVA<ELFT>();
+}
+
+// Specific ARM Thunk implementations. The naming convention is:
+// Source State, TargetState, Target Requirement, ABS or PI, Range
+namespace {
+template <class ELFT>
+class ARMToThumbV7ABSLongThunk final : public Thunk<ELFT> {
+public:
+ uint32_t size() const override { return 12; }
+
+ void writeTo(uint8_t *Buf) 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
+ };
+ uint64_t S = getARMThunkDestVA<ELFT>(this->Destination);
+ memcpy(Buf, ATData, sizeof(ATData));
+ Target->relocateOne(Buf, R_ARM_MOVW_ABS_NC, S);
+ Target->relocateOne(Buf + 4, R_ARM_MOVT_ABS, S);
+ }
+
+ ARMToThumbV7ABSLongThunk(const SymbolBody &Destination,
+ const InputSection<ELFT> &Owner)
+ : Thunk<ELFT>(Destination, Owner) {}
+};
+
+template <class ELFT> class ARMToThumbV7PILongThunk final : public Thunk<ELFT> {
+public:
+ uint32_t size() const override { return 16; }
+
+ void writeTo(uint8_t *Buf) 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
+ };
+ uint64_t S = getARMThunkDestVA<ELFT>(this->Destination);
+ uint64_t P = this->getVA();
+ 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);
+ }
+
+ ARMToThumbV7PILongThunk(const SymbolBody &Destination,
+ const InputSection<ELFT> &Owner)
+ : Thunk<ELFT>(Destination, Owner) {}
+};
+
+template <class ELFT>
+class ThumbToARMV7ABSLongThunk final : public Thunk<ELFT> {
+public:
+ uint32_t size() const override { return 10; }
+
+ void writeTo(uint8_t *Buf) 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
+ };
+ uint64_t S = getARMThunkDestVA<ELFT>(this->Destination);
+ 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);
+ }
+
+ ThumbToARMV7ABSLongThunk(const SymbolBody &Destination,
+ const InputSection<ELFT> &Owner)
+ : Thunk<ELFT>(Destination, Owner) {}
+};
+
+template <class ELFT> class ThumbToARMV7PILongThunk final : public Thunk<ELFT> {
+public:
+ uint32_t size() const override { return 12; }
+
+ void writeTo(uint8_t *Buf) 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
+ };
+ uint64_t S = getARMThunkDestVA<ELFT>(this->Destination);
+ uint64_t P = this->getVA();
+ 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);
+ }
+
+ ThumbToARMV7PILongThunk(const SymbolBody &Destination,
+ const InputSection<ELFT> &Owner)
+ : Thunk<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 Thunk<ELFT> {
+public:
+ MipsThunk(const SymbolBody &Destination, const InputSection<ELFT> &Owner);
+ uint32_t size() const override;
+ void writeTo(uint8_t *Buf) const override;
+};
+
+template <class ELFT>
+MipsThunk<ELFT>::MipsThunk(const SymbolBody &Destination,
+ const InputSection<ELFT> &Owner)
+ : Thunk<ELFT>(Destination, Owner) {}
+
+template <class ELFT> uint32_t MipsThunk<ELFT>::size() const { return 16; }
+
+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>
+static void addThunkARM(uint32_t RelocType, SymbolBody &S,
+ InputSection<ELFT> &IS) {
+ if (S.hasThunk<ELFT>())
+ // 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 ARMToThumbV7PILongThunk<ELFT>(S, IS);
+ else
+ thunk = new ARMToThumbV7ABSLongThunk<ELFT>(S, IS);
+ break;
+ case R_ARM_THM_JUMP19:
+ case R_ARM_THM_JUMP24:
+ if (NeedsPI)
+ thunk = new ThumbToARMV7PILongThunk<ELFT>(S, IS);
+ else
+ thunk = new ThumbToARMV7ABSLongThunk<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);
+ if (DefinedRegular<ELFT> *DR = dyn_cast<DefinedRegular<ELFT>>(&S))
+ DR->ThunkData.reset(thunk);
+ else if (SharedSymbol<ELFT> *SH = dyn_cast<SharedSymbol<ELFT>>(&S))
+ SH->ThunkData.reset(thunk);
+ else
+ fatal("symbol not DefinedRegular or Shared\n");
+}
+
+template <class ELFT>
+static void addThunkMips(uint32_t RelocType, SymbolBody &S) {
+ if (S.hasThunk<ELFT>())
+ // 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 MipsThunk<ELFT>(S, *Sec);
+ Sec->addThunk(T);
+ R->ThunkData.reset(T);
+}
+
+template <class ELFT>
+void addThunk(uint32_t RelocType, SymbolBody &S, InputSection<ELFT> &IS) {
+ if (Config->EMachine == EM_ARM)
+ addThunkARM<ELFT>(RelocType, S, IS);
+ else if (Config->EMachine == EM_MIPS)
+ addThunkMips<ELFT>(RelocType, S);
+ else
+ llvm_unreachable("add Thunk only supported for ARM and Mips");
+}
+
+template void addThunk<ELF32LE>(uint32_t, SymbolBody &,
+ InputSection<ELF32LE> &);
+template void addThunk<ELF32BE>(uint32_t, SymbolBody &,
+ InputSection<ELF32BE> &);
+template void addThunk<ELF64LE>(uint32_t, SymbolBody &,
+ InputSection<ELF64LE> &);
+template void addThunk<ELF64BE>(uint32_t, SymbolBody &,
+ InputSection<ELF64BE> &);
+
+template uint32_t Thunk<ELF32LE>::getVA() const;
+template uint32_t Thunk<ELF32BE>::getVA() const;
+template uint64_t Thunk<ELF64LE>::getVA() const;
+template uint64_t Thunk<ELF64BE>::getVA() const;
+
+} // namespace elf
+} // namespace lld
diff --git a/lld/ELF/Thunks.h b/lld/ELF/Thunks.h
new file mode 100644
index 0000000..754bcad
--- /dev/null
+++ b/lld/ELF/Thunks.h
@@ -0,0 +1,55 @@
+//===- 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.
+// A Thunk is a code-sequence inserted by the linker in between a caller and
+// the callee. The relocation to the callee is redirected to the Thunk, which
+// after executing transfers control to the callee. Typical uses of Thunks
+// include transferring control from non-pi to pi and changing state on
+// targets like ARM.
+//
+// Thunks can be created for DefinedRegular and Shared Symbols. The Thunk
+// is stored in a field of the Symbol Destination.
+// Thunks to be written to an InputSection are recorded by the InputSection.
+template <class ELFT> class Thunk {
+public:
+ virtual uint32_t size() const { return 0; }
+ typename ELFT::uint getVA() const;
+ virtual void writeTo(uint8_t *Buf) const {};
+ Thunk(const SymbolBody &Destination, const InputSection<ELFT> &Owner);
+ virtual ~Thunk();
+
+protected:
+ const SymbolBody &Destination;
+ const InputSection<ELFT> &Owner;
+ uint64_t Offset;
+};
+
+// For a Relocation to symbol S from InputSection Src, create a Thunk and
+// update the fields of S and the InputSection that the Thunk body will be
+// written to. At present there are implementations for ARM and Mips Thunks.
+template <class ELFT>
+void addThunk(uint32_t RelocType, SymbolBody &S, InputSection<ELFT> &Src);
+
+} // namespace elf
+} // namespace lld
+
+#endif