3 changes: 1 addition & 2 deletions lld/ELF/OutputSections.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ template <class ELFT> class GotSection final : public OutputSectionBase<ELFT> {
bool addDynTlsEntry(SymbolBody &Sym);
bool addTlsIndex();
bool empty() const { return MipsLocalEntries == 0 && Entries.empty(); }
uintX_t getMipsLocalFullAddr(const SymbolBody &B);
uintX_t getMipsLocalEntryAddr(uintX_t EntryValue);
uintX_t getMipsLocalPageAddr(uintX_t Addr);
uintX_t getGlobalDynAddr(const SymbolBody &B) const;
uintX_t getGlobalDynOffset(const SymbolBody &B) const;
Expand All @@ -139,7 +139,6 @@ template <class ELFT> class GotSection final : public OutputSectionBase<ELFT> {
llvm::SmallPtrSet<const OutputSectionBase<ELFT> *, 10> MipsOutSections;
llvm::DenseMap<uintX_t, size_t> MipsLocalGotPos;

uintX_t getMipsLocalEntryAddr(uintX_t EntryValue);
};

template <class ELFT>
Expand Down
572 changes: 283 additions & 289 deletions lld/ELF/Target.cpp

Large diffs are not rendered by default.

22 changes: 9 additions & 13 deletions lld/ELF/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#ifndef LLD_ELF_TARGET_H
#define LLD_ELF_TARGET_H

#include "InputSection.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Object/ELF.h"

Expand Down Expand Up @@ -68,16 +69,15 @@ class TargetInfo {

virtual void writeThunk(uint8_t *Buf, uint64_t S) const {}

virtual void relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type,
uint64_t P, uint64_t SA) const = 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 bool isGotRelative(uint32_t Type) const;
bool canRelaxTls(uint32_t Type, const SymbolBody *S) const;
template <class ELFT>
bool needsCopyRel(uint32_t Type, const SymbolBody &S) const;
size_t relaxTls(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P,
uint64_t SA, const SymbolBody &S) const;
virtual ~TargetInfo();

unsigned TlsGdToLeSkip = 1;
unsigned PageSize = 4096;

// On freebsd x86_64 the first page cannot be mmaped.
Expand All @@ -103,18 +103,14 @@ class TargetInfo {
uint32_t ThunkSize = 0;
bool UseLazyBinding = false;

virtual void relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const;
virtual void relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const;
virtual void relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const;
virtual void relaxTlsLdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const;

private:
virtual bool needsCopyRelImpl(uint32_t Type) const;
virtual bool needsPltImpl(uint32_t Type) const;

virtual size_t relaxTlsGdToIe(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type,
uint64_t P, uint64_t SA) const;
virtual size_t relaxTlsGdToLe(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type,
uint64_t P, uint64_t SA) const;
virtual size_t relaxTlsIeToLe(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type,
uint64_t P, uint64_t SA) const;
virtual size_t relaxTlsLdToLe(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type,
uint64_t P, uint64_t SA) const;
};

uint64_t getPPC64TocBase();
Expand Down
220 changes: 175 additions & 45 deletions lld/ELF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/FileOutputBuffer.h"
#include "llvm/Support/StringSaver.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::support::endian;

using namespace lld;
using namespace lld::elf;
Expand Down Expand Up @@ -268,23 +270,40 @@ template <bool Is64Bits> struct DenseMapInfo<SectionKey<Is64Bits>> {
}

// Returns the number of relocations processed.
template <class ELFT, class RelT>
template <class ELFT>
static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body,
InputSectionBase<ELFT> &C, RelT &RI) {
InputSectionBase<ELFT> &C,
typename ELFT::uint Offset,
typename ELFT::uint Addend, RelExpr Expr) {
if (!(C.getSectionHdr()->sh_flags & SHF_ALLOC))
return 0;

typedef typename ELFT::uint uintX_t;
if (Target->pointsToLocalDynamicGotEntry(Type)) {
if (Target->canRelaxTls(Type, nullptr))
if (Target->canRelaxTls(Type, nullptr)) {
C.Relocations.push_back(
{R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Body});
return 2;
}
if (Out<ELFT>::Got->addTlsIndex())
Out<ELFT>::RelaDyn->addReloc({Target->TlsModuleIndexRel, Out<ELFT>::Got,
Out<ELFT>::Got->getTlsIndexOff(), false,
nullptr, 0});
Expr = Expr == R_PC ? R_TLSLD_PC : R_TLSLD;
C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
return 1;
}

if (!Body.isTls())
return 0;

if (Target->isTlsLocalDynamicRel(Type) &&
Target->canRelaxTls(Type, nullptr)) {
C.Relocations.push_back(
{R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Body});
return 1;
}

if (Target->isTlsGlobalDynamicRel(Type)) {
if (!Target->canRelaxTls(Type, &Body)) {
if (Out<ELFT>::Got->addDynTlsEntry(Body)) {
Expand All @@ -295,17 +314,30 @@ static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body,
Off + (uintX_t)sizeof(uintX_t), false,
&Body, 0});
}
Expr = Expr == R_PC ? R_TLSGD_PC : R_TLSGD;
C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
return 1;
}
if (!Body.isPreemptible())

if (Body.isPreemptible()) {
Expr = Expr == R_PC ? R_RELAX_TLS_GD_TO_IE_PC : R_RELAX_TLS_GD_TO_IE;
C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
if (!Body.isInGot()) {
Out<ELFT>::Got->addEntry(Body);
Out<ELFT>::RelaDyn->addReloc({Target->TlsGotRel, Out<ELFT>::Got,
Body.getGotOffset<ELFT>(), false, &Body,
0});
}
return 2;
if (!Body.isInGot()) {
Out<ELFT>::Got->addEntry(Body);
Out<ELFT>::RelaDyn->addReloc({Target->TlsGotRel, Out<ELFT>::Got,
Body.getGotOffset<ELFT>(), false, &Body,
0});
}
return 2;
C.Relocations.push_back(
{R_RELAX_TLS_GD_TO_LE, Type, Offset, Addend, &Body});
return Target->TlsGdToLeSkip;
}
if (Target->isTlsInitialExecRel(Type) && Target->canRelaxTls(Type, &Body)) {
C.Relocations.push_back(
{R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Body});
return 1;
}
return 0;
}
Expand All @@ -329,6 +361,56 @@ void Writer<ELFT>::scanRelocsForThunks(const elf::ObjectFile<ELFT> &File,
}
}

template <endianness E> static int16_t readSignedLo16(const uint8_t *Loc) {
return read32<E>(Loc) & 0xffff;
}

template <class RelTy>
static uint32_t getMipsPairType(const RelTy *Rel, const SymbolBody &Sym) {
switch (Rel->getType(Config->Mips64EL)) {
case R_MIPS_HI16:
return R_MIPS_LO16;
case R_MIPS_GOT16:
return Sym.isLocal() ? R_MIPS_LO16 : R_MIPS_NONE;
case R_MIPS_PCHI16:
return R_MIPS_PCLO16;
case R_MICROMIPS_HI16:
return R_MICROMIPS_LO16;
default:
return R_MIPS_NONE;
}
}

template <class ELFT, class RelTy>
static int32_t findMipsPairedAddend(const uint8_t *Buf, const uint8_t *BufLoc,
SymbolBody &Sym, const RelTy *Rel,
const RelTy *End) {
uint32_t SymIndex = Rel->getSymbol(Config->Mips64EL);
uint32_t Type = getMipsPairType(Rel, Sym);

// Some MIPS relocations use addend calculated from addend of the relocation
// itself and addend of paired relocation. ABI requires to compute such
// combined addend in case of REL relocation record format only.
// See p. 4-17 at ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
if (RelTy::IsRela || Type == R_MIPS_NONE)
return 0;

for (const RelTy *RI = Rel; RI != End; ++RI) {
if (RI->getType(Config->Mips64EL) != Type)
continue;
if (RI->getSymbol(Config->Mips64EL) != SymIndex)
continue;
const endianness E = ELFT::TargetEndianness;
return ((read32<E>(BufLoc) & 0xffff) << 16) +
readSignedLo16<E>(Buf + RI->r_offset);
}
unsigned OldType = Rel->getType(Config->Mips64EL);
StringRef OldName = getELFRelocationTypeName(Config->EMachine, OldType);
StringRef NewName = getELFRelocationTypeName(Config->EMachine, Type);
warning("can't find matching " + NewName + " relocation for " + OldName);
return 0;
}

// The reason we have to do this early scan is as follows
// * To mmap the output file, we need to know the size
// * For that, we need to know how many dynamic relocs we will have.
Expand All @@ -345,7 +427,16 @@ void Writer<ELFT>::scanRelocsForThunks(const elf::ObjectFile<ELFT> &File,
template <class ELFT>
template <class RelTy>
void Writer<ELFT>::scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
bool IsAlloc = C.getSectionHdr()->sh_flags & SHF_ALLOC;

auto AddDyn = [=](const DynamicReloc<ELFT> &Reloc) {
if (IsAlloc)
Out<ELFT>::RelaDyn->addReloc(Reloc);
};

const elf::ObjectFile<ELFT> &File = *C.getFile();
ArrayRef<uint8_t> SectionData = C.getSectionData();
const uint8_t *Buf = SectionData.begin();
for (auto I = Rels.begin(), E = Rels.end(); I != E; ++I) {
const RelTy &RI = *I;
uint32_t SymIndex = RI.getSymbol(Config->Mips64EL);
Expand All @@ -369,22 +460,33 @@ void Writer<ELFT>::scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
if (auto *S = dyn_cast<SharedSymbol<ELFT>>(&Body))
S->File->IsUsed = true;

RelExpr Expr = Target->getRelExpr(Type, Body);

uintX_t Addend = getAddend<ELFT>(RI);
const uint8_t *BufLoc = Buf + RI.r_offset;
if (!RelTy::IsRela)
Addend += Target->getImplicitAddend(BufLoc, Type);
if (Config->EMachine == EM_MIPS)
Addend += findMipsPairedAddend<ELFT>(Buf, BufLoc, Body, &RI, E);

bool Preemptible = Body.isPreemptible();
if (unsigned Processed = handleTlsRelocation<ELFT>(Type, Body, C, RI)) {
if (unsigned Processed =
handleTlsRelocation<ELFT>(Type, Body, C, Offset, Addend, Expr)) {
I += (Processed - 1);
continue;
}

if (Target->needsDynRelative(Type))
Out<ELFT>::RelaDyn->addReloc({Target->RelativeRel, C.OutSec, Offset, true,
&Body, getAddend<ELFT>(RI)});
AddDyn({Target->RelativeRel, C.OutSec, Offset, true, &Body,
getAddend<ELFT>(RI)});

// If a symbol in a DSO is referenced directly instead of through GOT,
// we need to create a copy relocation for the symbol.
if (auto *B = dyn_cast<SharedSymbol<ELFT>>(&Body)) {
if (Target->needsCopyRel<ELFT>(Type, Body)) {
if (IsAlloc && Target->needsCopyRel<ELFT>(Type, *B)) {
if (!B->needsCopy())
addCopyRelSymbol(B);
C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
continue;
}
}
Expand All @@ -395,6 +497,15 @@ void Writer<ELFT>::scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
if (NeedPlt) {
if (NeedPlt == TargetInfo::Plt_Implicit)
Body.NeedsCopyOrPltAddr = true;
RelExpr E;
if (Expr == R_PPC_OPD)
E = R_PPC_PLT_OPD;
else if (Expr == R_PC)
E = R_PLT_PC;
else
E = R_PLT;
C.Relocations.push_back({E, Type, Offset, Addend, &Body});

if (Body.isInPlt())
continue;
Out<ELFT>::Plt->addEntry(Body);
Expand All @@ -407,22 +518,43 @@ void Writer<ELFT>::scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {

if (Target->UseLazyBinding) {
Out<ELFT>::GotPlt->addEntry(Body);
Out<ELFT>::RelaPlt->addReloc({Rel, Out<ELFT>::GotPlt,
Body.getGotPltOffset<ELFT>(),
!Preemptible, &Body, 0});
if (IsAlloc)
Out<ELFT>::RelaPlt->addReloc({Rel, Out<ELFT>::GotPlt,
Body.getGotPltOffset<ELFT>(),
!Preemptible, &Body, 0});
} else {
if (Body.isInGot())
continue;
Out<ELFT>::Got->addEntry(Body);
Out<ELFT>::RelaDyn->addReloc({Rel, Out<ELFT>::Got,
Body.getGotOffset<ELFT>(), !Preemptible,
&Body, 0});
AddDyn({Rel, Out<ELFT>::Got, Body.getGotOffset<ELFT>(), !Preemptible,
&Body, 0});
}
continue;
}

if (Target->needsThunk(Type, File, Body)) {
C.Relocations.push_back({R_THUNK, Type, Offset, Addend, &Body});
continue;
}

// If a relocation needs GOT, we create a GOT slot for the symbol.
if (Target->needsGot(Type, Body)) {
uint32_t T = Body.isTls() ? Target->getTlsGotRel(Type) : Type;
RelExpr E;
if (Expr == R_PC)
E = R_GOT_PC;
else if (Expr == R_PAGE_PC)
E = R_GOT_PAGE_PC;
else if (Config->EMachine == EM_MIPS) {
if (Body.isLocal())
E = R_MIPS_GOT_LOCAL;
else if (!Body.isPreemptible())
E = R_MIPS_GOT;
else
E = R_GOT;
} else
E = R_GOT;
C.Relocations.push_back({E, T, Offset, Addend, &Body});
if (Body.isInGot())
continue;
Out<ELFT>::Got->addEntry(Body);
Expand All @@ -443,26 +575,23 @@ void Writer<ELFT>::scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
DynType = Target->GotRel;
else
DynType = Target->RelativeRel;
Out<ELFT>::RelaDyn->addReloc({DynType, Out<ELFT>::Got,
Body.getGotOffset<ELFT>(), !Preemptible,
&Body, 0});
AddDyn({DynType, Out<ELFT>::Got, Body.getGotOffset<ELFT>(),
!Preemptible, &Body, 0});
}
continue;
}

// MIPS _gp_disp designates offset between start of function and 'gp'
// pointer into GOT. __gnu_local_gp is equal to the current value of
// the 'gp'. Therefore any relocations against them do not require
// dynamic relocation.
if (Config->EMachine == EM_MIPS && (&Body == ElfSym<ELFT>::MipsGpDisp ||
&Body == ElfSym<ELFT>::MipsLocalGp))
continue;

if (Preemptible) {
// We don't know anything about the finaly symbol. Just ask the dynamic
// linker to handle the relocation for us.
Out<ELFT>::RelaDyn->addReloc({Target->getDynRel(Type), C.OutSec, Offset,
false, &Body, getAddend<ELFT>(RI)});
AddDyn({Target->getDynRel(Type), C.OutSec, Offset, false, &Body, Addend});
continue;
}

if (Config->EMachine == EM_PPC64 && RI.getType(false) == R_PPC64_TOC) {
C.Relocations.push_back({R_PPC_TOC, Type, Offset, Addend, &Body});
AddDyn({R_PPC64_RELATIVE, C.OutSec, Offset, false, nullptr,
(uintX_t)getPPC64TocBase() + Addend});
continue;
}

Expand All @@ -473,18 +602,20 @@ void Writer<ELFT>::scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
// We can however do better than just copying the incoming relocation. We
// can process some of it and and just ask the dynamic linker to add the
// load address.
if (!Config->Pic || Target->isRelRelative(Type) || Target->isSizeRel(Type))
if (Target->isSizeRel(Type)) {
C.Relocations.push_back({R_SIZE, Type, Offset, Addend, &Body});
continue;

uintX_t Addend = getAddend<ELFT>(RI);
if (Config->EMachine == EM_PPC64 && RI.getType(false) == R_PPC64_TOC) {
Out<ELFT>::RelaDyn->addReloc({R_PPC64_RELATIVE, C.OutSec, Offset, false,
nullptr,
(uintX_t)getPPC64TocBase() + Addend});
}
if (!Config->Pic || Target->isRelRelative(Type) || Expr == R_PC) {
if (Config->EMachine == EM_MIPS && Body.isLocal() &&
(Type == R_MIPS_GPREL16 || Type == R_MIPS_GPREL32))
Expr = R_MIPS_GP0;
C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
continue;
}
Out<ELFT>::RelaDyn->addReloc(
{Target->RelativeRel, C.OutSec, Offset, true, &Body, Addend});

AddDyn({Target->RelativeRel, C.OutSec, Offset, true, &Body, Addend});
C.Relocations.push_back({R_ABS, Type, Offset, Addend, &Body});
}

// Scan relocations for necessary thunks.
Expand All @@ -493,9 +624,8 @@ void Writer<ELFT>::scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
}

template <class ELFT> void Writer<ELFT>::scanRelocs(InputSection<ELFT> &C) {
if (C.getSectionHdr()->sh_flags & SHF_ALLOC)
for (const Elf_Shdr *RelSec : C.RelocSections)
scanRelocs(C, *RelSec);
for (const Elf_Shdr *RelSec : C.RelocSections)
scanRelocs(C, *RelSec);
}

template <class ELFT>
Expand Down
4 changes: 2 additions & 2 deletions lld/test/ELF/tls-opt.s
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
// DISASM-NEXT: 1104d: 49 81 c4 fc ff ff ff addq $-4, %r12
// Corrupred output:
// DISASM-NEXT: 11054: 48 8d 80 f8 ff ff ff leaq -8(%rax), %rax
// DISASM-NEXT: 1105b: 48 d1 81 c4 f8 ff ff rolq -1852(%rcx)
// DISASM-NEXT: 1105b: 48 d1 81 c4 fc ff ff rolq -828(%rcx)
// DISASM-NEXT: 11062: ff 48 d1 decl -47(%rax)
// DISASM-NEXT: 11065: 81 c4 f8 ff ff ff addl $4294967288, %esp
// DISASM-NEXT: 11065: 81 c4 fc ff ff ff addl $4294967292, %esp
// LD to LE:
// DISASM-NEXT: 1106b: 66 66 66 64 48 8b 04 25 00 00 00 00 movq %fs:0, %rax
// DISASM-NEXT: 11077: 48 8d 88 f8 ff ff ff leaq -8(%rax), %rcx
Expand Down