88 changes: 68 additions & 20 deletions lld/ELF/Target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -181,6 +182,9 @@ class ARMTargetInfo final : public TargetInfo {
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;
};

Expand All @@ -197,8 +201,9 @@ template <class ELFT> class MipsTargetInfo final : public TargetInfo {
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;
};
Expand Down Expand Up @@ -248,9 +253,10 @@ uint64_t TargetInfo::getVAStart() const { return Config->Pic ? 0 : VAStart; }

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; }
Expand Down Expand Up @@ -1483,8 +1489,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;
}
}
Expand Down Expand Up @@ -1532,6 +1542,34 @@ void ARMTargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
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) {
Expand Down Expand Up @@ -1615,17 +1653,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
Expand All @@ -1636,6 +1677,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
Expand Down Expand Up @@ -1682,8 +1724,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
Expand All @@ -1698,12 +1740,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);
Expand All @@ -1720,7 +1766,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) {
Expand Down Expand Up @@ -1887,28 +1932,31 @@ void MipsTargetInfo<ELFT>::writeThunk(uint8_t *Buf, uint64_t S) const {
}

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>
Expand Down
15 changes: 9 additions & 6 deletions lld/ELF/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,16 @@ class TargetInfo {
// 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();
Expand Down Expand Up @@ -87,8 +92,6 @@ class TargetInfo {
// 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;
Expand Down
251 changes: 251 additions & 0 deletions lld/ELF/Thunks.cpp
Original file line number Diff line number Diff line change
@@ -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
55 changes: 55 additions & 0 deletions lld/ELF/Thunks.h
Original file line number Diff line number Diff line change
@@ -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 = 0;
typename ELFT::uint getVA() const;
virtual void writeTo(uint8_t *Buf) const = 0;
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
37 changes: 37 additions & 0 deletions lld/test/ELF/arm-mov-relocs.s
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
375 changes: 375 additions & 0 deletions lld/test/ELF/arm-thumb-interwork-thunk.s

Large diffs are not rendered by default.