35 changes: 35 additions & 0 deletions lld/ELF/OutputSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,41 @@ std::array<uint8_t, 4> OutputSection::getFiller() {
return {0, 0, 0, 0};
}

void OutputSection::checkDynRelAddends(const uint8_t *bufStart) {
assert(config->writeAddends && config->checkDynamicRelocs);
assert(type == SHT_REL || type == SHT_RELA);
std::vector<InputSection *> sections = getInputSections(this);
parallelForEachN(0, sections.size(), [&](size_t i) {
// When linking with -r or --emit-relocs we might also call this function
// for input .rel[a].<sec> sections which we simply pass through to the
// output. We skip over those and only look at the synthetic relocation
// sections created during linking.
const auto *sec = dyn_cast<RelocationBaseSection>(sections[i]);
if (!sec)
return;
for (const DynamicReloc &rel : sec->relocs) {
int64_t addend = rel.computeAddend();
const OutputSection *relOsec = rel.inputSec->getOutputSection();
assert(relOsec != nullptr && "missing output section for relocation");
const uint8_t *relocTarget =
bufStart + relOsec->offset + rel.inputSec->getOffset(rel.offsetInSec);
// For SHT_NOBITS the written addend is always zero.
int64_t writtenAddend =
relOsec->type == SHT_NOBITS
? 0
: target->getImplicitAddend(relocTarget, rel.type);
if (addend != writtenAddend)
internalLinkerError(
getErrorLocation(relocTarget),
"wrote incorrect addend value 0x" + utohexstr(writtenAddend) +
" instead of 0x" + utohexstr(addend) +
" for dynamic relocation " + toString(rel.type) +
" at offset 0x" + utohexstr(rel.getOffset()) +
(rel.sym ? " against symbol " + toString(*rel.sym) : ""));
}
});
}

template void OutputSection::writeHeaderTo<ELF32LE>(ELF32LE::Shdr *Shdr);
template void OutputSection::writeHeaderTo<ELF32BE>(ELF32BE::Shdr *Shdr);
template void OutputSection::writeHeaderTo<ELF64LE>(ELF64LE::Shdr *Shdr);
Expand Down
2 changes: 2 additions & 0 deletions lld/ELF/OutputSections.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ class OutputSection final : public BaseCommand, public SectionBase {

void finalize();
template <class ELFT> void writeTo(uint8_t *buf);
// Check that the addends for dynamic relocations were written correctly.
void checkDynRelAddends(const uint8_t *bufStart);
template <class ELFT> void maybeCompress();

void sort(llvm::function_ref<int(InputSectionBase *s)> order);
Expand Down
51 changes: 27 additions & 24 deletions lld/ELF/Relocations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,8 @@ handleTlsRelocation(RelType type, Symbol &sym, InputSectionBase &c,
config->shared) {
if (in.got->addDynTlsEntry(sym)) {
uint64_t off = in.got->getGlobalDynOffset(sym);
mainPart->relaDyn->addReloc(
{target->tlsDescRel, in.got, off, !sym.isPreemptible, &sym, 0});
mainPart->relaDyn->addAddendOnlyRelocIfNonPreemptible(
target->tlsDescRel, in.got, off, sym, target->tlsDescRel);
}
if (expr != R_TLSDESC_CALL)
c.relocations.push_back({expr, type, offset, addend, &sym});
Expand Down Expand Up @@ -245,8 +245,8 @@ handleTlsRelocation(RelType type, Symbol &sym, InputSectionBase &c,
in.got->relocations.push_back(
{R_ADDEND, target->symbolicRel, in.got->getTlsIndexOff(), 1, &sym});
else
mainPart->relaDyn->addReloc(target->tlsModuleIndexRel, in.got,
in.got->getTlsIndexOff(), nullptr);
mainPart->relaDyn->addReloc(
{target->tlsModuleIndexRel, in.got, in.got->getTlsIndexOff()});
}
c.relocations.push_back({expr, type, offset, addend, &sym});
return 1;
Expand Down Expand Up @@ -283,14 +283,15 @@ handleTlsRelocation(RelType type, Symbol &sym, InputSectionBase &c,
in.got->relocations.push_back(
{R_ADDEND, target->symbolicRel, off, 1, &sym});
else
mainPart->relaDyn->addReloc(target->tlsModuleIndexRel, in.got, off, &sym);
mainPart->relaDyn->addSymbolReloc(target->tlsModuleIndexRel, in.got,
off, sym);

// If the symbol is preemptible we need the dynamic linker to write
// the offset too.
uint64_t offsetOff = off + config->wordsize;
if (sym.isPreemptible)
mainPart->relaDyn->addReloc(target->tlsOffsetRel, in.got, offsetOff,
&sym);
mainPart->relaDyn->addSymbolReloc(target->tlsOffsetRel, in.got,
offsetOff, sym);
else
in.got->relocations.push_back(
{R_ABS, target->tlsOffsetRel, offsetOff, 0, &sym});
Expand All @@ -307,8 +308,8 @@ handleTlsRelocation(RelType type, Symbol &sym, InputSectionBase &c,
addend, &sym});
if (!sym.isInGot()) {
in.got->addEntry(sym);
mainPart->relaDyn->addReloc(target->tlsGotRel, in.got, sym.getGotOffset(),
&sym);
mainPart->relaDyn->addSymbolReloc(target->tlsGotRel, in.got,
sym.getGotOffset(), sym);
}
} else {
c.relocations.push_back(
Expand Down Expand Up @@ -618,7 +619,7 @@ template <class ELFT> static void addCopyRelSymbol(SharedSymbol &ss) {
for (SharedSymbol *sym : getSymbolsAt<ELFT>(ss))
replaceWithDefined(*sym, sec, 0, sym->size);

mainPart->relaDyn->addReloc(target->copyRel, sec, 0, &ss);
mainPart->relaDyn->addSymbolReloc(target->copyRel, sec, 0, ss);
}

// MIPS has an odd notion of "paired" relocations to calculate addends.
Expand Down Expand Up @@ -1047,7 +1048,7 @@ class OffsetGetter {
} // namespace

static void addRelativeReloc(InputSectionBase *isec, uint64_t offsetInSec,
Symbol *sym, int64_t addend, RelExpr expr,
Symbol &sym, int64_t addend, RelExpr expr,
RelType type) {
Partition &part = isec->getPartition();

Expand All @@ -1058,21 +1059,23 @@ static void addRelativeReloc(InputSectionBase *isec, uint64_t offsetInSec,
// don't store the addend values, so we must write it to the relocated
// address.
if (part.relrDyn && isec->alignment >= 2 && offsetInSec % 2 == 0) {
isec->relocations.push_back({expr, type, offsetInSec, addend, sym});
isec->relocations.push_back({expr, type, offsetInSec, addend, &sym});
part.relrDyn->relocs.push_back({isec, offsetInSec});
return;
}
part.relaDyn->addReloc(target->relativeRel, isec, offsetInSec, sym, addend,
expr, type);
part.relaDyn->addRelativeReloc(target->relativeRel, isec, offsetInSec, sym,
addend, type, expr);
}

template <class PltSection, class GotPltSection>
static void addPltEntry(PltSection *plt, GotPltSection *gotPlt,
RelocationBaseSection *rel, RelType type, Symbol &sym) {
plt->addEntry(sym);
gotPlt->addEntry(sym);
rel->addReloc(
{type, gotPlt, sym.getGotPltOffset(), !sym.isPreemptible, &sym, 0});
rel->addReloc({type, gotPlt, sym.getGotPltOffset(),
sym.isPreemptible ? DynamicReloc::AgainstSymbol
: DynamicReloc::AddendOnlyWithTargetVA,
sym, 0, R_ABS});
}

static void addGotEntry(Symbol &sym) {
Expand All @@ -1098,12 +1101,12 @@ static void addGotEntry(Symbol &sym) {
// Otherwise, we emit a dynamic relocation to .rel[a].dyn so that
// the GOT slot will be fixed at load-time.
if (!sym.isTls() && !sym.isPreemptible && config->isPic) {
addRelativeReloc(in.got, off, &sym, 0, R_ABS, target->symbolicRel);
addRelativeReloc(in.got, off, sym, 0, R_ABS, target->symbolicRel);
return;
}
mainPart->relaDyn->addReloc(
sym.isTls() ? target->tlsGotRel : target->gotRel, in.got, off, &sym, 0,
sym.isPreemptible ? R_ADDEND : R_ABS, target->symbolicRel);
mainPart->relaDyn->addAddendOnlyRelocIfNonPreemptible(
sym.isTls() ? target->tlsGotRel : target->gotRel, in.got, off, sym,
target->symbolicRel);
}

// Return true if we can define a symbol in the executable that
Expand Down Expand Up @@ -1167,13 +1170,13 @@ static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type,
if (canWrite) {
RelType rel = target->getDynRel(type);
if (expr == R_GOT || (rel == target->symbolicRel && !sym.isPreemptible)) {
addRelativeReloc(&sec, offset, &sym, addend, expr, type);
addRelativeReloc(&sec, offset, sym, addend, expr, type);
return;
} else if (rel != 0) {
if (config->emachine == EM_MIPS && rel == target->symbolicRel)
rel = target->relativeRel;
sec.getPartition().relaDyn->addReloc(rel, &sec, offset, &sym, addend,
R_ADDEND, type);
sec.getPartition().relaDyn->addSymbolReloc(rel, &sec, offset, sym, addend,
type);

// MIPS ABI turns using of GOT and dynamic relocations inside out.
// While regular ABI uses dynamic relocations to fill up GOT entries
Expand Down Expand Up @@ -1417,7 +1420,7 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
// direct relocation on through.
if (sym.isGnuIFunc() && config->zIfuncNoplt) {
sym.exportDynamic = true;
mainPart->relaDyn->addReloc(type, &sec, offset, &sym, addend, R_ADDEND, type);
mainPart->relaDyn->addSymbolReloc(type, &sec, offset, sym, addend, type);
return;
}

Expand Down
89 changes: 67 additions & 22 deletions lld/ELF/SyntheticSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -989,29 +989,33 @@ void MipsGotSection::build() {
// for the TP-relative offset as we don't know how much other data will
// be allocated before us in the static TLS block.
if (s->isPreemptible || config->shared)
mainPart->relaDyn->addReloc(target->tlsGotRel, this, offset, s);
mainPart->relaDyn->addReloc({target->tlsGotRel, this, offset,
DynamicReloc::AgainstSymbolWithTargetVA,
*s, 0, R_ABS});
}
for (std::pair<Symbol *, size_t> &p : got.dynTlsSymbols) {
Symbol *s = p.first;
uint64_t offset = p.second * config->wordsize;
if (s == nullptr) {
if (!config->shared)
continue;
mainPart->relaDyn->addReloc(target->tlsModuleIndexRel, this, offset, s);
mainPart->relaDyn->addReloc({target->tlsModuleIndexRel, this, offset});
} else {
// When building a shared library we still need a dynamic relocation
// for the module index. Therefore only checking for
// S->isPreemptible is not sufficient (this happens e.g. for
// thread-locals that have been marked as local through a linker script)
if (!s->isPreemptible && !config->shared)
continue;
mainPart->relaDyn->addReloc(target->tlsModuleIndexRel, this, offset, s);
mainPart->relaDyn->addSymbolReloc(target->tlsModuleIndexRel, this,
offset, *s);
// However, we can skip writing the TLS offset reloc for non-preemptible
// symbols since it is known even in shared libraries
if (!s->isPreemptible)
continue;
offset += config->wordsize;
mainPart->relaDyn->addReloc(target->tlsOffsetRel, this, offset, s);
mainPart->relaDyn->addSymbolReloc(target->tlsOffsetRel, this, offset,
*s);
}
}

Expand All @@ -1023,7 +1027,8 @@ void MipsGotSection::build() {
// Dynamic relocations for "global" entries.
for (const std::pair<Symbol *, size_t> &p : got.global) {
uint64_t offset = p.second * config->wordsize;
mainPart->relaDyn->addReloc(target->relativeRel, this, offset, p.first);
mainPart->relaDyn->addSymbolReloc(target->relativeRel, this, offset,
*p.first);
}
if (!config->isPic)
continue;
Expand All @@ -1034,13 +1039,14 @@ void MipsGotSection::build() {
for (size_t pi = 0; pi < pageCount; ++pi) {
uint64_t offset = (l.second.firstIndex + pi) * config->wordsize;
mainPart->relaDyn->addReloc({target->relativeRel, this, offset, l.first,
int64_t(pi * 0x10000)});
int64_t(pi * 0x10000)});
}
}
for (const std::pair<GotEntry, size_t> &p : got.local16) {
uint64_t offset = p.second * config->wordsize;
mainPart->relaDyn->addReloc({target->relativeRel, this, offset, true,
p.first.first, p.first.second});
mainPart->relaDyn->addReloc({target->relativeRel, this, offset,
DynamicReloc::AddendOnlyWithTargetVA,
*p.first.first, p.first.second, R_ABS});
}
}
}
Expand Down Expand Up @@ -1568,16 +1574,25 @@ uint64_t DynamicReloc::getOffset() const {
}

int64_t DynamicReloc::computeAddend() const {
if (useSymVA)
return sym->getVA(addend);
if (!outputSec)
switch (kind) {
case AddendOnly:
assert(sym == nullptr);
return addend;
// See the comment in the DynamicReloc ctor.
return getMipsPageAddr(outputSec->addr) + addend;
case AgainstSymbol:
assert(sym != nullptr);
return addend;
case AddendOnlyWithTargetVA:
case AgainstSymbolWithTargetVA:
return InputSection::getRelocTargetVA(inputSec->file, type, addend,
getOffset(), *sym, expr);
case MipsMultiGotPage:
assert(sym == nullptr);
return getMipsPageAddr(outputSec->addr) + addend;
}
}

uint32_t DynamicReloc::getSymIndex(SymbolTableBaseSection *symTab) const {
if (sym && !useSymVA)
if (needsDynSymIndex())
return symTab->getSymbolIndex(sym);
return 0;
}
Expand All @@ -1588,21 +1603,51 @@ RelocationBaseSection::RelocationBaseSection(StringRef name, uint32_t type,
: SyntheticSection(SHF_ALLOC, type, config->wordsize, name),
dynamicTag(dynamicTag), sizeDynamicTag(sizeDynamicTag) {}

void RelocationBaseSection::addReloc(RelType dynType, InputSectionBase *isec,
uint64_t offsetInSec, Symbol *sym) {
addReloc({dynType, isec, offsetInSec, false, sym, 0});
void RelocationBaseSection::addSymbolReloc(RelType dynType,
InputSectionBase *isec,
uint64_t offsetInSec, Symbol &sym,
int64_t addend,
Optional<RelType> addendRelType) {
addReloc(DynamicReloc::AgainstSymbol, dynType, isec, offsetInSec, sym, addend,
R_ADDEND, addendRelType ? *addendRelType : target->noneRel);
}

void RelocationBaseSection::addRelativeReloc(
RelType dynType, InputSectionBase *inputSec, uint64_t offsetInSec,
Symbol &sym, int64_t addend, RelType addendRelType, RelExpr expr) {
// This function should only be called for non-preemptible symbols or
// RelExpr values that refer to an address inside the output file (e.g. the
// address of the GOT entry for a potentially preemptible symbol).
assert((!sym.isPreemptible || expr == R_GOT) &&
"cannot add relative relocation against preemptible symbol");
assert(expr != R_ADDEND && "expected non-addend relocation expression");
addReloc(DynamicReloc::AddendOnlyWithTargetVA, dynType, inputSec, offsetInSec,
sym, addend, expr, addendRelType);
}

void RelocationBaseSection::addAddendOnlyRelocIfNonPreemptible(
RelType dynType, InputSectionBase *isec, uint64_t offsetInSec, Symbol &sym,
RelType addendRelType) {
// No need to write an addend to the section for preemptible symbols.
if (sym.isPreemptible)
addReloc({dynType, isec, offsetInSec, DynamicReloc::AgainstSymbol, sym, 0,
R_ABS});
else
addReloc(DynamicReloc::AddendOnlyWithTargetVA, dynType, isec, offsetInSec,
sym, 0, R_ABS, addendRelType);
}

void RelocationBaseSection::addReloc(RelType dynType,
void RelocationBaseSection::addReloc(DynamicReloc::Kind kind, RelType dynType,
InputSectionBase *inputSec,
uint64_t offsetInSec, Symbol *sym,
uint64_t offsetInSec, Symbol &sym,
int64_t addend, RelExpr expr,
RelType type) {
RelType addendRelType) {
// Write the addends to the relocated address if required. We skip
// it if the written value would be zero.
if (config->writeAddends && (expr != R_ADDEND || addend != 0))
inputSec->relocations.push_back({expr, type, offsetInSec, addend, sym});
addReloc({dynType, inputSec, offsetInSec, expr != R_ADDEND, sym, addend});
inputSec->relocations.push_back(
{expr, addendRelType, offsetInSec, addend, &sym});
addReloc({dynType, inputSec, offsetInSec, kind, sym, addend, expr});
}

void RelocationBaseSection::addReloc(const DynamicReloc &reloc) {
Expand Down
99 changes: 74 additions & 25 deletions lld/ELF/SyntheticSections.h
Original file line number Diff line number Diff line change
Expand Up @@ -425,39 +425,69 @@ class StringTableSection final : public SyntheticSection {

class DynamicReloc {
public:
enum Kind {
/// The resulting dynamic relocation does not reference a symbol (#sym must
/// be nullptr) and uses #addend as the result of computeAddend().
AddendOnly,
/// The resulting dynamic relocation will not reference a symbol: #sym is
/// only used to compute the addend with InputSection::getRelocTargetVA().
/// Useful for various relative and TLS relocations (e.g. R_X86_64_TPOFF64).
AddendOnlyWithTargetVA,
/// The resulting dynamic relocation references symbol #sym from the dynamic
/// symbol table and uses #addend as the value of computeAddend().
AgainstSymbol,
/// The resulting dynamic relocation references symbol #sym from the dynamic
/// symbol table and uses InputSection::getRelocTargetVA() + #addend for the
/// final addend. It can be used for relocations that write the symbol VA as
// the addend (e.g. R_MIPS_TLS_TPREL64) but still reference the symbol.
AgainstSymbolWithTargetVA,
/// This is used by the MIPS multi-GOT implementation. It relocates
/// addresses of 64kb pages that lie inside the output section.
MipsMultiGotPage,
};
/// This constructor records a relocation against a symbol.
DynamicReloc(RelType type, const InputSectionBase *inputSec,
uint64_t offsetInSec, Kind kind, Symbol &sym, int64_t addend,
RelExpr expr)
: type(type), sym(&sym), inputSec(inputSec), offsetInSec(offsetInSec),
kind(kind), expr(expr), addend(addend) {}
/// This constructor records a relative relocation with no symbol.
DynamicReloc(RelType type, const InputSectionBase *inputSec,
uint64_t offsetInSec, bool useSymVA, Symbol *sym, int64_t addend)
: type(type), sym(sym), inputSec(inputSec), offsetInSec(offsetInSec),
useSymVA(useSymVA), addend(addend), outputSec(nullptr) {}
// This constructor records dynamic relocation settings used by MIPS
// multi-GOT implementation. It's to relocate addresses of 64kb pages
// lie inside the output section.
uint64_t offsetInSec, int64_t addend = 0)
: type(type), sym(nullptr), inputSec(inputSec), offsetInSec(offsetInSec),
kind(AddendOnly), expr(R_ADDEND), addend(addend) {}
/// This constructor records dynamic relocation settings used by the MIPS
/// multi-GOT implementation.
DynamicReloc(RelType type, const InputSectionBase *inputSec,
uint64_t offsetInSec, const OutputSection *outputSec,
int64_t addend)
: type(type), sym(nullptr), inputSec(inputSec), offsetInSec(offsetInSec),
useSymVA(false), addend(addend), outputSec(outputSec) {}
kind(MipsMultiGotPage), expr(R_ADDEND), addend(addend),
outputSec(outputSec) {}

uint64_t getOffset() const;
uint32_t getSymIndex(SymbolTableBaseSection *symTab) const;
bool needsDynSymIndex() const {
return kind == AgainstSymbol || kind == AgainstSymbolWithTargetVA;
}

// Computes the addend of the dynamic relocation. Note that this is not the
// same as the addend member variable as it also includes the symbol address
// if useSymVA is true.
/// Computes the addend of the dynamic relocation. Note that this is not the
/// same as the #addend member variable as it may also include the symbol
/// address/the address of the corresponding GOT entry/etc.
int64_t computeAddend() const;

RelType type;

Symbol *sym;
const InputSectionBase *inputSec = nullptr;
const InputSectionBase *inputSec;
uint64_t offsetInSec;
// If this member is true, the dynamic relocation will not be against the
// symbol but will instead be a relative relocation that simply adds the
// load address. This means we need to write the symbol virtual address
// plus the original addend as the final relocation addend.
bool useSymVA;

private:
Kind kind;
// The kind of expression used to calculate the added (required e.g. for
// relative GOT relocations).
RelExpr expr;
int64_t addend;
const OutputSection *outputSec;
const OutputSection *outputSec = nullptr;
};

template <class ELFT> class DynamicSection final : public SyntheticSection {
Expand Down Expand Up @@ -488,18 +518,37 @@ class RelocationBaseSection : public SyntheticSection {
public:
RelocationBaseSection(StringRef name, uint32_t type, int32_t dynamicTag,
int32_t sizeDynamicTag);
void addReloc(RelType dynType, InputSectionBase *isec, uint64_t offsetInSec,
Symbol *sym);
// Add a dynamic relocation that might need an addend. This takes care of
// writing the addend to the output section if needed.
void addReloc(RelType dynType, InputSectionBase *inputSec,
uint64_t offsetInSec, Symbol *sym, int64_t addend, RelExpr expr,
RelType type);
/// Add a dynamic relocation without writing an addend to the output section.
/// This overload can be used if the addends are written directly instead of
/// using relocations on the input section (e.g. MipsGotSection::writeTo()).
void addReloc(const DynamicReloc &reloc);
/// Add a dynamic relocation against \p sym with an optional addend.
void addSymbolReloc(RelType dynType, InputSectionBase *isec,
uint64_t offsetInSec, Symbol &sym, int64_t addend = 0,
llvm::Optional<RelType> addendRelType = llvm::None);
/// Add a relative dynamic relocation that uses the target address of \p sym
/// (i.e. InputSection::getRelocTargetVA()) + \p addend as the addend.
void addRelativeReloc(RelType dynType, InputSectionBase *isec,
uint64_t offsetInSec, Symbol &sym, int64_t addend,
RelType addendRelType, RelExpr expr);
/// Add a dynamic relocation using the target address of \p sym as the addend
/// if \p sym is non-preemptible. Otherwise add a relocation against \p sym.
void addAddendOnlyRelocIfNonPreemptible(RelType dynType,
InputSectionBase *isec,
uint64_t offsetInSec, Symbol &sym,
RelType addendRelType);
void addReloc(DynamicReloc::Kind kind, RelType dynType,
InputSectionBase *inputSec, uint64_t offsetInSec, Symbol &sym,
int64_t addend, RelExpr expr, RelType addendRelType);
bool isNeeded() const override { return !relocs.empty(); }
size_t getSize() const override { return relocs.size() * this->entsize; }
size_t getRelativeRelocCount() const { return numRelativeRelocs; }
void finalizeContents() override;
static bool classof(const SectionBase *d) {
return SyntheticSection::classof(d) &&
(d->type == llvm::ELF::SHT_RELA || d->type == llvm::ELF::SHT_REL ||
d->type == llvm::ELF::SHT_RELR);
}
int32_t dynamicTag, sizeDynamicTag;
std::vector<DynamicReloc> relocs;

Expand Down
2 changes: 2 additions & 0 deletions lld/ELF/Target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ ErrorPlace elf::getErrorPlace(const uint8_t *loc) {
TargetInfo::~TargetInfo() {}

int64_t TargetInfo::getImplicitAddend(const uint8_t *buf, RelType type) const {
internalLinkerError(getErrorLocation(buf),
"cannot read addend for relocation " + toString(type));
return 0;
}

Expand Down
8 changes: 4 additions & 4 deletions lld/ELF/Thunks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -383,10 +383,10 @@ class PPC64PILongBranchThunk final : public PPC64LongBranchThunk {
assert(!dest.isPreemptible);
if (Optional<uint32_t> index =
in.ppc64LongBranchTarget->addEntry(&dest, addend)) {
mainPart->relaDyn->addReloc(
{target->relativeRel, in.ppc64LongBranchTarget, *index * UINT64_C(8),
true, &dest,
addend + getPPC64GlobalEntryToLocalEntryOffset(dest.stOther)});
mainPart->relaDyn->addRelativeReloc(
target->relativeRel, in.ppc64LongBranchTarget, *index * UINT64_C(8),
dest, addend + getPPC64GlobalEntryToLocalEntryOffset(dest.stOther),
target->symbolicRel, R_ABS);
}
}
};
Expand Down
10 changes: 9 additions & 1 deletion lld/ELF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2056,7 +2056,8 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
for (const SymbolTableEntry &e : part.dynSymTab->getSymbols())
syms.insert(e.sym);
for (DynamicReloc &reloc : part.relaDyn->relocs)
if (reloc.sym && !reloc.useSymVA && syms.insert(reloc.sym).second)
if (reloc.sym && reloc.needsDynSymIndex() &&
syms.insert(reloc.sym).second)
part.dynSymTab->addSymbol(reloc.sym);
}
}
Expand Down Expand Up @@ -2970,6 +2971,13 @@ template <class ELFT> void Writer<ELFT>::writeSections() {
for (OutputSection *sec : outputSections)
if (sec->type != SHT_REL && sec->type != SHT_RELA)
sec->writeTo<ELFT>(Out::bufferStart + sec->offset);

// Finally, check that all dynamic relocation addends were written correctly.
if (config->checkDynamicRelocs && config->writeAddends) {
for (OutputSection *sec : outputSections)
if (sec->type == SHT_REL || sec->type == SHT_RELA)
sec->checkDynRelAddends(Out::bufferStart);
}
}

// Computes a hash value of Data using a given hash function.
Expand Down
60 changes: 60 additions & 0 deletions lld/test/ELF/aarch64-tlsdesc-zrel.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/// Check that we write addends for AArch64 TLSDESC relocations with -z rel
/// See https://bugs.llvm.org/show_bug.cgi?id=47009
// REQUIRES: aarch64
// RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t.o
// RUN: ld.lld -shared %t.o -o %t-rela.so
// RUN: llvm-readobj -W -r -x .got %t-rela.so | FileCheck %s --check-prefixes=RELA,RELA-NO-ADDENDS
// RUN: ld.lld -shared %t.o -o %t-rela-addends.so --apply-dynamic-relocs
// RUN: llvm-readobj -W -r -x .got %t-rela-addends.so | FileCheck %s --check-prefixes=RELA,RELA-WITH-ADDENDS

// RELA: Relocations [
// RELA-NEXT: Section (5) .rela.dyn {
// RELA-NEXT: 0x20340 R_AARCH64_TLSDESC - 0x0
// RELA-NEXT: 0x20350 R_AARCH64_TLSDESC - 0x4
// RELA-NEXT: }
// RELA-NEXT: ]
// RELA-NEXT: Hex dump of section '.got':
// RELA-NEXT: 0x00020340 00000000 00000000 00000000 00000000
// RELA-NO-ADDENDS-NEXT: 0x00020350 00000000 00000000 00000000 00000000
// RELA-WITH-ADDENDS-NEXT: 0x00020350 00000000 00000000 04000000 00000000
/// Addend 0x4 for R_AARCH64_TLSDESC -----^
// RELA-EMPTY:

// RUN: ld.lld -shared %t.o -o %t-rel.so -z rel
// RUN: llvm-readobj -W -r -x .got %t-rel.so | FileCheck %s --check-prefix=REL
// REL: Relocations [
// REL-NEXT: Section (5) .rel.dyn {
// REL-NEXT: 0x20330 R_AARCH64_TLSDESC -{{$}}
// REL-NEXT: 0x20340 R_AARCH64_TLSDESC -{{$}}
// REL-NEXT: }
// REL-NEXT: ]
// REL-NEXT: Hex dump of section '.got':
// REL-NEXT: 0x00020330 00000000 00000000 00000000 00000000
// REL-NEXT: 0x00020340 00000000 00000000 04000000 00000000
/// Addend 0x4 for R_AARCH64_TLSDESC -----^
// REL-EMPTY:

.text
foo:
adrp x0, :tlsdesc:x
ldr x1, [x0, :tlsdesc_lo12:x]
add x0, x0, :tlsdesc_lo12:x
.tlsdesccall x
blr x1
adrp x0, :tlsdesc:y
ldr x1, [x0, :tlsdesc_lo12:y]
add x0, x0, :tlsdesc_lo12:y
.tlsdesccall y
blr x1
ret

.section .tbss,"awT",@nobits
.p2align 2
.hidden x
x:
.word 0

.p2align 2
.hidden y
y:
.word 0
46 changes: 41 additions & 5 deletions lld/test/ELF/got32-i386-pie-rw.s
Original file line number Diff line number Diff line change
@@ -1,14 +1,50 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t.o
# RUN: llvm-readobj -r %t.o | FileCheck %s --check-prefix=OBJ
# RUN: ld.lld %t.o -o %t -pie
# RUN: llvm-readelf -r -S %t | FileCheck %s
# RUN: llvm-objdump -s --section=.foobar --section=.got -r -d -t \
# RUN: --dynamic-reloc %t | FileCheck %s --check-prefixes=CHECK,REL
# RUN: ld.lld %t.o -o %t-rela -pie -z rela
# RUN: llvm-objdump -s --section=.foobar --section=.got -r -d -t \
# RUN: --dynamic-reloc %t-rela | FileCheck %s --check-prefixes=CHECK,RELA

# Unlike bfd and gold we accept this.

# CHECK: .foobar PROGBITS 00002180
# CHECK: .got PROGBITS [[GOT:[0-9a-z]*]]
# CHECK-DAG: 00002182 00000008 R_386_RELATIVE
# CHECK-DAG: [[GOT]] 00000008 R_386_RELATIVE
# OBJ: Relocations [
# OBJ-NEXT: Section (4) .rel.foobar {
# OBJ-NEXT: 0x2 R_386_GOT32 foo
# OBJ-NEXT: }
# OBJ-NEXT: ]

# CHECK-LABEL: SYMBOL TABLE:
# REL: 00001180 l .text 00000000 foo
# REL: 00002180 g .foobar 00000000 _start
# RELA: 00001188 l .text 00000000 foo
# RELA: 00002188 g .foobar 00000000 _start

# CHECK-LABEL: DYNAMIC RELOCATION RECORDS
# REL-NEXT: 00002182 R_386_RELATIVE *ABS*{{$}}
# REL-NEXT: 000031f0 R_386_RELATIVE *ABS*{{$}}
# RELA-NEXT: 0000218a R_386_RELATIVE *ABS*+0x31f8{{$}}
# RELA-NEXT: 000031f8 R_386_RELATIVE *ABS*+0x1188{{$}}
# CHECK-NEXT: Contents of section .foobar:
# REL-NEXT: 2180 8b1df031 0000
## ^--- VA of GOT entry (0x31f0)
# RELA-NEXT: 2188 8b1d0000 0000
## ^--- VA of GOT entry in Elf_Rela addend
# CHECK-NEXT: Contents of section .got:
# REL-NEXT: 31f0 80110000
## ^--- VA of foo (0x1180)
# RELA-NEXT: 31f8 00000000
## ^--- VA of foo in Elf_Rela addend

# CHECK-LABEL: Disassembly of section .foobar:
# CHECK: <_start>:
# REL-NEXT: 2180: 8b 1d f0 31 00 00 movl 12784, %ebx
## ^--- VA of GOT entry (0x31f0)
# RELA-NEXT: 2188: 8b 1d 00 00 00 00 movl 0, %ebx
## ^--- VA of GOT entry in in Elf_Rela addend

foo:

.section .foobar, "awx"
Expand Down
26 changes: 20 additions & 6 deletions lld/test/ELF/i386-zrel-zrela.s
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
## Elf32_Rel dynamic relocations by default, but can use Elf32_Rela with -z rela.

# RUN: llvm-mc -filetype=obj -triple=i386 %s -o %t.o
# RUN: ld.lld -shared %t.o -o %t.so
# RUN: llvm-readobj -d -r -x .data %t.so | FileCheck --check-prefix=REL %s
# RUN: ld.lld -shared %t.o -o %t.so --noinhibit-exec
# RUN: llvm-readobj -d -r -x .data -x .got.plt %t.so | FileCheck --check-prefix=REL %s
# RUN: ld.lld -shared -z rel %t.o -o %t1.so
# RUN: llvm-readobj -d -r -x .data %t1.so | FileCheck --check-prefix=REL %s
# RUN: llvm-readobj -d -r -x .data -x .got.plt %t1.so | FileCheck --check-prefix=REL %s

# REL: REL {{.*}}
# REL-NEXT: RELSZ 32 (bytes)
Expand All @@ -26,11 +26,16 @@
# REL-NEXT: R_386_JUMP_SLOT func
# REL-NEXT: }

# REL: Hex dump of section '.data':
# REL-NEXT: 0x000042d0 d0420000 2a000000
# REL-LABEL: Hex dump of section '.data':
# REL-NEXT: 0x000042d0 d0420000 2a000000
## ^--- R_386_RELATIVE addend (0x42d0)
## ^--- R_386_32 addend (0x2a)
# REL-LABEL: Hex dump of section '.got.plt':
# REL-NEXT: 0x000042d8 48320000 00000000 00000000 36120000
## R_386_JUMP_SLOT target (0x1236) ---------------^

# RUN: ld.lld -shared -z rel -z rela %t.o -o %t2.so
# RUN: llvm-readobj -d -r %t2.so | FileCheck --check-prefix=RELA %s
# RUN: llvm-readobj -d -r -x .data -x .got.plt %t2.so | FileCheck --check-prefix=RELA %s

# RELA: RELA {{.*}}
# RELA-NEXT: RELASZ 48 (bytes)
Expand All @@ -50,6 +55,15 @@
# RELA-NEXT: R_386_JUMP_SLOT func 0x0
# RELA-NEXT: }

# RELA-LABEL: Hex dump of section '.data':
# RELA-NEXT: 0x000042f0 00000000 2a000000
## ^--- R_386_32 addend (0x2a)
## TODO: we should probably clear the R_386_32 addend that was copied from the .o?
## no addend written for R_386_RELATIVE
# RELA-LABEL: Hex dump of section '.got.plt':
# RELA-NEXT: 0x000042f8 68320000 00000000 00000000 56120000
## R_386_JUMP_SLOT target (0x1256) ----------------^

.globl _start
_start:
call func@PLT
Expand Down
12 changes: 8 additions & 4 deletions lld/test/ELF/ppc64-abs64-dyn.s
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
# REQUIRES: ppc
# RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %s -o %t.o
# RUN: ld.lld -shared %t.o -o %t.so
# RUN: llvm-readobj -r %t.so | FileCheck %s
# RUN: llvm-readobj -r %t.so --syms | FileCheck %s

# RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-linux %s -o %t.o
# RUN: ld.lld -shared %t.o -o %t.so
# RUN: llvm-readobj -r %t.so | FileCheck %s
# RUN: llvm-readobj -r %t.so --syms | FileCheck %s

## Test that we create R_PPC64_RELATIVE for R_PPC64_ADDR64 to non-preemptable
## symbols and R_PPC64_TOC in writable sections.

## FIXME the addend for offset 0x20000 should be TOC base+0x8000+1, not 0x80001.
# CHECK: .rela.dyn {
# CHECK-NEXT: 0x303B8 R_PPC64_RELATIVE - 0x8001
# CHECK-NEXT: 0x303B8 R_PPC64_RELATIVE - 0x303B1
## TOC base (0x283b0) + 0x8000 + 1 ---------^
# CHECK-NEXT: 0x303C0 R_PPC64_RELATIVE - 0x303B9
# CHECK-NEXT: 0x303C8 R_PPC64_ADDR64 external 0x1
# CHECK-NEXT: 0x303D0 R_PPC64_ADDR64 global 0x1
# CHECK-NEXT: }
# CHECK-LABEL: Symbols [
# CHECK: Symbol {
# CHECK: Name: .TOC. ({{.+}})
# CHECK-NEXT: Value: 0x283B0

.data
.globl global
Expand Down
2 changes: 1 addition & 1 deletion lld/test/ELF/ppc64-long-branch-pi.s
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
# SEC-SHARED: .branch_lt NOBITS 00000000020020f0 20120f0 000020 00 WA 0 0 8

# RELOC: .rela.dyn {
# RELOC-NEXT: 0x2002108 R_PPC64_RELATIVE - 0x8000
# RELOC-NEXT: 0x2002108 R_PPC64_RELATIVE - 0x2012100
# RELOC-NEXT: 0x2002110 R_PPC64_RELATIVE - 0x2002000
# RELOC-NEXT: 0x2002118 R_PPC64_RELATIVE - 0x2002008
# RELOC-NEXT: 0x2002120 R_PPC64_RELATIVE - 0x200200C
Expand Down
16 changes: 14 additions & 2 deletions lld/test/ELF/riscv-ifunc-nonpreemptible.s
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
# REQUIRES: riscv
# RUN: llvm-mc -filetype=obj -triple=riscv32 %s -o %t.32.o
# RUN: ld.lld -pie %t.32.o -o %t.32
# RUN: llvm-readobj -r %t.32 | FileCheck --check-prefix=RELOC32 %s
# RUN: ld.lld -pie %t.32.o -o %t.32-apply --apply-dynamic-relocs
# RUN: llvm-readobj -r -x .got.plt %t.32 | FileCheck --check-prefixes=RELOC32,NO-APPLY-RELOC32 %s
# RUN: llvm-readobj -r -x .got.plt %t.32-apply | FileCheck --check-prefixes=RELOC32,APPLY-RELOC32 %s
# RUN: llvm-readelf -s %t.32 | FileCheck --check-prefix=SYM32 %s
# RUN: llvm-objdump -d --no-show-raw-insn %t.32 | FileCheck --check-prefix=DIS32 %s

# RUN: llvm-mc -filetype=obj -triple=riscv64 %s -o %t.64.o
# RUN: ld.lld -pie %t.64.o -o %t.64
# RUN: llvm-readobj -r %t.64 | FileCheck --check-prefix=RELOC64 %s
# RUN: ld.lld -pie %t.64.o -o %t.64-apply --apply-dynamic-relocs
# RUN: llvm-readobj -r -x .got.plt %t.64 | FileCheck --check-prefixes=RELOC64,NO-APPLY-RELOC64 %s
# RUN: llvm-readobj -r -x .got.plt %t.64-apply | FileCheck --check-prefixes=RELOC64,APPLY-RELOC64 %s
# RUN: llvm-readelf -s %t.64 | FileCheck --check-prefix=SYM64 %s
# RUN: llvm-objdump -d --no-show-raw-insn %t.64 | FileCheck --check-prefix=DIS64 %s

# RELOC32: .rela.dyn {
# RELOC32-NEXT: 0x3220 R_RISCV_IRELATIVE - 0x117C
# RELOC32-NEXT: }
# RELOC32-LABEL: Hex dump of section '.got.plt':
# NO-APPLY-RELOC32: 0x00003220 00000000
# APPLY-RELOC32: 0x00003220 7c110000
# RELOC32-EMPTY:

# SYM32: 0001190 0 FUNC GLOBAL DEFAULT {{.*}} func

Expand All @@ -31,6 +39,10 @@
# RELOC64: .rela.dyn {
# RELOC64-NEXT: 0x3380 R_RISCV_IRELATIVE - 0x1260
# RELOC64-NEXT: }
# RELOC64-LABEL: Hex dump of section '.got.plt':
# NO-APPLY-RELOC64: 0x00003380 00000000 00000000
# APPLY-RELOC64: 0x00003380 60120000 00000000
# RELOC64-EMPTY:

# SYM64: 000000000001270 0 FUNC GLOBAL DEFAULT {{.*}} func

Expand Down
18 changes: 14 additions & 4 deletions lld/test/ELF/x86-64-gotpc-relax.s
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -relax-relocations -triple=x86_64-unknown-linux %s -o %t.o
# RUN: ld.lld %t.o -o %t1 --no-apply-dynamic-relocs
# RUN: llvm-readobj -x .got.plt -r %t1 | FileCheck --check-prefixes=RELOC,NO-APPLY-DYNAMIC-RELOCS %s
# RUN: ld.lld %t.o -o %t1 --apply-dynamic-relocs
# RUN: llvm-readobj -x .got.plt -r %t1 | FileCheck --check-prefixes=RELOC,APPLY-DYNAMIC-RELOCS %s
# RUN: ld.lld %t.o -o %t1
# RUN: llvm-readobj -r %t1 | FileCheck --check-prefix=RELOC %s
# RUN: llvm-objdump -d %t1 | FileCheck --check-prefix=DISASM %s

## There is no relocations.
# RELOC: Relocations [
# RELOC: ]
## There is one R_X86_64_IRELATIVE relocations.
# RELOC-LABEL: Relocations [
# RELOC-NEXT: Section (1) .rela.dyn {
# RELOC-NEXT: 0x202220 R_X86_64_IRELATIVE - 0x201172
# RELOC-NEXT: }
# RELOC-NEXT: ]
# RELOC-LABEL: Hex dump of section '.got.plt':
# NO-APPLY-DYNAMIC-RELOCS-NEXT: 0x00202220 00000000 00000000
# APPLY-DYNAMIC-RELOCS-NEXT: 0x00202220 72112000 00000000
# RELOC-EMPTY:

# 0x201173 + 7 - 10 = 0x201170
# 0x20117a + 7 - 17 = 0x201170
Expand Down