Index: ELF/CMakeLists.txt =================================================================== --- ELF/CMakeLists.txt +++ ELF/CMakeLists.txt @@ -21,6 +21,7 @@ SymbolTable.cpp Symbols.cpp Target.cpp + Thunks.cpp Writer.cpp LINK_COMPONENTS Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ 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 @@ Target = TI.get(); LinkerScript LS; Script::X = &LS; + std::unique_ptr> THI(createThunkInfo()); + Thunks::X = THI.get(); Config->Rela = ELFT::Is64Bits; Config->Mips64EL = Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ 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 @@ // 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 *thunk); // The offset of synthetic thunk code from beginning of this section. uint64_t getThunkOff() const; @@ -229,7 +230,7 @@ // Used by ICF. uint64_t GroupId = 0; - llvm::TinyPtrVector Thunks; + llvm::TinyPtrVector *> Thunks; }; // MIPS .reginfo section provides information on the registers used by the code Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ 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 @@ return Sections[this->Header->sh_info]; } -template void InputSection::addThunk(SymbolBody &Body) { - Body.ThunkIndex = Thunks.size(); - Thunks.push_back(&Body); +template void +InputSection::addThunk(const Thunk* thunk) { + Thunks.push_back(thunk); } template uint64_t InputSection::getThunkOff() const { @@ -137,7 +138,10 @@ } template uint64_t InputSection::getThunksSize() const { - return Thunks.size() * Target->ThunkSize; + uint64_t Total = 0; + for (const Thunk *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 @@ Out::Got->getNumEntries() * sizeof(uintX_t); case R_TLSLD_PC: return Out::Got->getTlsIndexVA() + A - P; - case R_THUNK: - return Body.getThunkVA(); + case R_THUNK_ABS: + return Thunks::X->getThunkVA(Body); + case R_THUNK_PC: + case R_THUNK_PLT_PC: + return Thunks::X->getThunkVA(Body) + A - P; case R_PPC_TOC: return getPPC64TocBase() + A; case R_TLSGD: @@ -403,9 +410,9 @@ // jump istruction. if (!Thunks.empty()) { Buf += OutSecOff + getThunkOff(); - for (const SymbolBody *S : Thunks) { - Target->writeThunk(Buf, S->getVA()); - Buf += Target->ThunkSize; + for (const Thunk * T : Thunks) { + T->writeTo(Buf); + Buf += T->size(); } } } Index: ELF/Relocations.h =================================================================== --- ELF/Relocations.h +++ 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, Index: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ 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 @@ -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 &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 = Thunks::X->adjustExprForThunk(Expr, Type, File, Body); if (IsWrite || isStaticLinkTimeConstant(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(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>( - cast>(&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>(&C); + Thunks::X->addThunk(Type, Body, *Sec); } // At this point we are done with the relocated position. Some relocations Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -89,11 +89,9 @@ 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 typename ELFT::uint getVA(typename ELFT::uint Addend = 0) const; @@ -103,7 +101,6 @@ template typename ELFT::uint getGotPltOffset() const; template typename ELFT::uint getGotPltVA() const; template typename ELFT::uint getPltVA() const; - template typename ELFT::uint getThunkVA() const; template typename ELFT::uint getSize() const; // Returns the file from which the symbol was created. Index: ELF/Symbols.cpp =================================================================== --- ELF/Symbols.cpp +++ ELF/Symbols.cpp @@ -175,13 +175,6 @@ PltIndex * Target->PltEntrySize; } -template typename ELFT::uint SymbolBody::getThunkVA() const { - auto *D = cast>(this); - auto *S = cast>(D->Section); - return S->OutSec->getVA() + S->OutSecOff + S->getThunkOff() + - ThunkIndex * Target->ThunkSize; -} - template typename ELFT::uint SymbolBody::getSize() const { if (const auto *C = dyn_cast(this)) return C->Size; @@ -319,11 +312,6 @@ template uint64_t SymbolBody::template getSize() const; template uint64_t SymbolBody::template getSize() const; -template uint32_t SymbolBody::template getThunkVA() const; -template uint32_t SymbolBody::template getThunkVA() const; -template uint64_t SymbolBody::template getThunkVA() const; -template uint64_t SymbolBody::template getThunkVA() const; - template class elf::DefinedSynthetic; template class elf::DefinedSynthetic; template class elf::DefinedSynthetic; Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -52,7 +52,7 @@ 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 @@ // 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; Index: ELF/Target.cpp =================================================================== --- ELF/Target.cpp +++ 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 @@ 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 @@ // 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 @@ ((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 @@ ((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 @@ ((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 @@ // 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 @@ PageSize = 65536; PltEntrySize = 16; PltHeaderSize = 32; - ThunkSize = 16; CopyRel = R_MIPS_COPY; PltRel = R_MIPS_JUMP_SLOT; if (ELFT::Is64Bits) { @@ -1897,6 +1910,11 @@ } template +uint32_t MipsTargetInfo::thunkSize() const { + return 16; +} + +template uint64_t MipsTargetInfo::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const { const endianness E = ELFT::TargetEndianness; Index: ELF/Thunks.h =================================================================== --- /dev/null +++ ELF/Thunks.h @@ -0,0 +1,92 @@ +//===- 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" +#include "llvm/ADT/DenseMap.h" + +namespace lld { +namespace elf { +class SymbolBody; +class InputFile; +template class InputSection; +template 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 to be written to an InputSection are stored by the +// InputSection. +template class Thunk { +public: + virtual uint32_t size() const = 0; + virtual typename ELFT::uint getVA() const; + virtual void writeTo(uint8_t *Buf) const = 0; + Thunk(const SymbolBody &Destination, const InputSection &Owner); + virtual ~Thunk(); + +protected: + const SymbolBody &Destination; + const InputSection &Owner; + uint64_t Offset; +}; + +// Class to decide when Thunks are necessary and maintain the mapping +// between SymbolBodies and the Thunks that target them. Targets that +// require Thunk generation must override adjustExprForThunk() to +// express that a Thunk is required, and addThunk() to create a concrete +// instance of Thunk that will be registered with the InputSection that +// the Thunk will be written to. +// +template class ThunkInfo { +public: + // 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 adjustExprForThunk(RelExpr Expr, uint32_t RelocType, + const InputFile &File, + const SymbolBody &S) const { + return Expr; + } + + // Add a Thunk for Symbol S from a relocation from Src of type RelocType + // At present there can be at most one Thunk per Symbol. Additional calls + // to addThunk if S has a Thunk are ignored. + virtual void addThunk(uint32_t RelocType, const SymbolBody &S, + InputSection &Src){}; + + // Return the virtual address for the Thunk for Symbol S + // This must only be called if RelExpr is one of R_THUNK_ABS, R_THUNK_PC or + // R_THUNK_PLT_PC + uint64_t getThunkVA(const SymbolBody &S) const; + virtual ~ThunkInfo(); + +protected: + void registerThunk(const SymbolBody &S, const Thunk *T); + llvm::BumpPtrAllocator Alloc; + llvm::DenseMap *> ThunkMap; +}; + +// Thunks::X points to the Target specific implementation of ThunkInfo +template struct Thunks { static ThunkInfo *X; }; +template ThunkInfo *Thunks::X; + +template ThunkInfo *createThunkInfo(); + +} // namespace elf +} // namespace lld + +#endif Index: ELF/Thunks.cpp =================================================================== --- /dev/null +++ ELF/Thunks.cpp @@ -0,0 +1,310 @@ +//===- 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 Thunk::~Thunk() {} + +template +Thunk::Thunk(const SymbolBody &D, const InputSection &O) + : Destination(D), Owner(O), Offset(O.getThunkOff() + O.getThunksSize()) {} + +template typename ELFT::uint Thunk::getVA() const { + return Owner.OutSec->getVA() + Owner.OutSecOff + Offset; +} + +// ARM Target Thunks +template static uint64_t ARMThunkDestVA(const SymbolBody &S) { + return S.isInPlt() ? S.getPltVA() : S.getVA(); +} + +// Specific ARM Thunk implementations. The naming convention is: +// Source State, TargetState, Target Requirement, ABS or PI, Range + +template class ARMThumbV7ABSLongThunk final : public Thunk { +public: + virtual uint32_t size() const override { return 12; } + virtual 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 = ARMThunkDestVA(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); + } + ARMThumbV7ABSLongThunk(const SymbolBody &Destination, + const InputSection &Owner) + : Thunk(Destination, Owner) {} +}; + +template class ARMThumbV7PILongThunk final : public Thunk { +public: + virtual uint32_t size() const override { return 16; } + virtual 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 = ARMThunkDestVA(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); + } + ARMThumbV7PILongThunk(const SymbolBody &Destination, + const InputSection &Owner) + : Thunk(Destination, Owner) {} +}; + +template class ThumbARMV7ABSLongThunk final : public Thunk { +public: + virtual uint32_t size() const override { return 10; } + virtual 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 = ARMThunkDestVA(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); + } + ThumbARMV7ABSLongThunk(const SymbolBody &Destination, + const InputSection &Owner) + : Thunk(Destination, Owner) {} +}; + +template class ThumbARMV7PILongThunk final : public Thunk { +public: + virtual uint32_t size() const override { return 12; } + virtual 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 = ARMThunkDestVA(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); + } + ThumbARMV7PILongThunk(const SymbolBody &Destination, + const InputSection &Owner) + : Thunk(Destination, Owner) {} +}; + +// Mips Thunks +// Only the MIPS LA25 Thunk is supported, the implementation is delegated +// to the MipsTargetInfo class in Target.cpp +template class MipsThunk : public Thunk { +public: + MipsThunk(const SymbolBody &Destination, const InputSection &Owner); + virtual uint32_t size() const override; + virtual void writeTo(uint8_t *Buf) const override; +}; + +template +MipsThunk::MipsThunk(const SymbolBody &Destination, + const InputSection &Owner) + : Thunk(Destination, Owner) {} + +template uint32_t MipsThunk::size() const { + return Target->thunkSize(); +} + +template void MipsThunk::writeTo(uint8_t *Buf) const { + const SymbolBody &D = this->Destination; + uint64_t S = D.getVA(); + Target->writeThunk(Buf, S); +} + +// ThunkInfo implementation +template ThunkInfo::~ThunkInfo() {} + +template +uint64_t ThunkInfo::getThunkVA(const SymbolBody &S) const { + return ThunkMap.find(&S)->second->getVA(); +} + +template +void ThunkInfo::registerThunk(const SymbolBody &S, const Thunk *T) { + this->ThunkMap[&S] = T; +} + +// Target Specific ThunkInfo implementations + +template class ARMThunkInfo final : public ThunkInfo { +public: + void addThunk(uint32_t RelocType, const SymbolBody &S, + InputSection &IS) override; + RelExpr adjustExprForThunk(RelExpr Expr, uint32_t RelocType, + const InputFile &File, + const SymbolBody &S) const override; +}; + +template +RelExpr ARMThunkInfo::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() & 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() & 1) == 0) + return R_THUNK_PC; + break; + } + return Expr; +} + +template +void ARMThunkInfo::addThunk(uint32_t RelocType, const SymbolBody &S, + InputSection &IS) { + if (this->ThunkMap.find(&S) != this->ThunkMap.end()) + // only one Thunk supported per symbol + return; + + bool NeedsPI = Config->Pic || Config->Pie || Config->Shared; + Thunk *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(S, IS); + else + thunk = new (this->Alloc) ARMThumbV7ABSLongThunk(S, IS); + break; + case R_ARM_THM_JUMP19: + case R_ARM_THM_JUMP24: + if (NeedsPI) + thunk = new (this->Alloc) ThumbARMV7PILongThunk(S, IS); + else + thunk = new (this->Alloc) ThumbARMV7ABSLongThunk(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 MipsThunkInfo final : public ThunkInfo { +public: + RelExpr adjustExprForThunk(RelExpr Expr, uint32_t RelocType, + const InputFile &File, + const SymbolBody &S) const override; + void addThunk(uint32_t RelocType, const SymbolBody &S, + InputSection &IS) override; +}; + +template +RelExpr MipsThunkInfo::adjustExprForThunk(RelExpr Expr, + uint32_t RelocType, + const InputFile &File, + const SymbolBody &S) const { + return Target->needsThunk(RelocType, File, S) ? R_THUNK_ABS : Expr; +} + +template +void MipsThunkInfo::addThunk(uint32_t RelocType, const SymbolBody &S, + InputSection &) { + if (this->ThunkMap.find(&S) != this->ThunkMap.end()) + // only one Thunk supported per symbol + return; + // Mips Thunks are added to the InputSection defining S + auto *R = cast>(&S); + auto *Sec = cast>(R->Section); + auto *T = new (this->Alloc) MipsThunk(S, *Sec); + Sec->addThunk(T); + this->registerThunk(S, T); +} + +template ThunkInfo *createThunkInfo() { + switch (Config->EMachine) { + case EM_ARM: + return new ARMThunkInfo(); + case EM_MIPS: + return new MipsThunkInfo(); + default: + return new ThunkInfo(); + } +} + +template class ThunkInfo; +template class ThunkInfo; +template class ThunkInfo; +template class ThunkInfo; + +template ThunkInfo *createThunkInfo(); +template ThunkInfo *createThunkInfo(); +template ThunkInfo *createThunkInfo(); +template ThunkInfo *createThunkInfo(); + +} // namespace elf +} // namespace lld Index: test/ELF/arm-mov-relocs.s =================================================================== --- test/ELF/arm-mov-relocs.s +++ test/ELF/arm-mov-relocs.s @@ -41,16 +41,53 @@ // 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 Index: test/ELF/arm-thumb-interwork-thunk.s =================================================================== --- /dev/null +++ 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 +// CHECK-ABS-ARM-NEXT: 1304: 3d ff ff fa blx #-780 +// CHECK-ABS-ARM-NEXT: 1308: 06 00 00 ea b #24 +// CHECK-ABS-ARM-NEXT: 130c: 05 00 00 ea b #20 +// CHECK-ABS-ARM-NEXT: 1310: 07 00 00 ea b #28 +// CHECK-ABS-ARM-NEXT: 1314: 09 00 00 ea b #36 +// CHECK-ABS-ARM-NEXT: 1318: 78 ff ff ea b #-544 +// CHECK-ABS-ARM-NEXT: 131c: b7 00 00 0a beq #732 +// CHECK-ABS-ARM-NEXT: 1320: b7 00 00 1a bne #732 +// 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 +// CHECK-PI-ARM-NEXT: 1304: 3d ff ff fa blx #-780 +// 0x1308 + 8 + 0x18 = 0x1328 +// CHECK-PI-ARM-NEXT: 1308: 06 00 00 ea b #24 +// 0x130c + 8 + 0x14 = 0x1328 +// CHECK-PI-ARM-NEXT: 130c: 05 00 00 ea b #20 +// 0x1310 + 8 + 0x20 = 0x1338 +// CHECK-PI-ARM-NEXT: 1310: 08 00 00 ea b #32 +// 0x1314 + 8 + 0x2c = 0x1348 +// CHECK-PI-ARM-NEXT: 1314: 0b 00 00 ea b #44 +// CHECK-PI-ARM-NEXT: 1318: 78 ff ff ea b #-544 +// CHECK-PI-ARM-NEXT: 131c: b7 00 00 0a beq #732 +// CHECK-PI-ARM-NEXT: 1320: b7 00 00 1a bne #732 +// 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