diff --git a/lld/ELF/Arch/ARM.cpp b/lld/ELF/Arch/ARM.cpp --- a/lld/ELF/Arch/ARM.cpp +++ b/lld/ELF/Arch/ARM.cpp @@ -707,20 +707,29 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const { switch (type) { default: + internalLinkerError(getErrorLocation(buf), + "cannot read addend for relocation " + toString(type)); return 0; case R_ARM_ABS32: case R_ARM_BASE_PREL: + case R_ARM_GLOB_DAT: case R_ARM_GOTOFF32: case R_ARM_GOT_BREL: case R_ARM_GOT_PREL: + case R_ARM_IRELATIVE: case R_ARM_REL32: + case R_ARM_RELATIVE: + case R_ARM_SBREL32: case R_ARM_TARGET1: case R_ARM_TARGET2: + case R_ARM_TLS_DTPMOD32: + case R_ARM_TLS_DTPOFF32: case R_ARM_TLS_GD32: - case R_ARM_TLS_LDM32: - case R_ARM_TLS_LDO32: case R_ARM_TLS_IE32: + case R_ARM_TLS_LDM32: case R_ARM_TLS_LE32: + case R_ARM_TLS_LDO32: + case R_ARM_TLS_TPOFF32: return SignExtend64<32>(read32le(buf)); case R_ARM_PREL31: return SignExtend64<31>(read32le(buf)); @@ -828,6 +837,9 @@ uint64_t imm12 = read16le(buf + 2) & 0x0fff; return u ? imm12 : -imm12; } + case R_ARM_NONE: + case R_ARM_JUMP_SLOT: + return 0; // The stored value at this location is not the addend. } } diff --git a/lld/ELF/Arch/Mips.cpp b/lld/ELF/Arch/Mips.cpp --- a/lld/ELF/Arch/Mips.cpp +++ b/lld/ELF/Arch/Mips.cpp @@ -385,8 +385,10 @@ const endianness e = ELFT::TargetEndianness; switch (type) { case R_MIPS_32: + case R_MIPS_REL32: case R_MIPS_GPREL32: case R_MIPS_TLS_DTPREL32: + case R_MIPS_TLS_DTPMOD32: case R_MIPS_TLS_TPREL32: return SignExtend64<32>(read32(buf)); case R_MIPS_26: @@ -394,25 +396,37 @@ // we should use another expression for calculation: // ((A << 2) | (P & 0xf0000000)) >> 2 return SignExtend64<28>(read32(buf) << 2); + case R_MIPS_CALL_HI16: case R_MIPS_GOT16: + case R_MIPS_GOT_HI16: case R_MIPS_HI16: case R_MIPS_PCHI16: return SignExtend64<16>(read32(buf)) << 16; + case R_MIPS_CALL16: + case R_MIPS_CALL_LO16: + case R_MIPS_GOT_LO16: case R_MIPS_GPREL16: case R_MIPS_LO16: case R_MIPS_PCLO16: case R_MIPS_TLS_DTPREL_HI16: case R_MIPS_TLS_DTPREL_LO16: + case R_MIPS_TLS_GD: + case R_MIPS_TLS_GOTTPREL: + case R_MIPS_TLS_LDM: case R_MIPS_TLS_TPREL_HI16: case R_MIPS_TLS_TPREL_LO16: return SignExtend64<16>(read32(buf)); case R_MICROMIPS_GOT16: case R_MICROMIPS_HI16: return SignExtend64<16>(readShuffle(buf)) << 16; + case R_MICROMIPS_CALL16: case R_MICROMIPS_GPREL16: case R_MICROMIPS_LO16: case R_MICROMIPS_TLS_DTPREL_HI16: case R_MICROMIPS_TLS_DTPREL_LO16: + case R_MICROMIPS_TLS_GD: + case R_MICROMIPS_TLS_GOTTPREL: + case R_MICROMIPS_TLS_LDM: case R_MICROMIPS_TLS_TPREL_HI16: case R_MICROMIPS_TLS_TPREL_LO16: return SignExtend64<16>(readShuffle(buf)); @@ -446,7 +460,21 @@ return SignExtend64<25>(readShuffle(buf) << 2); case R_MICROMIPS_PC26_S1: return SignExtend64<27>(readShuffle(buf) << 1); + case R_MIPS_64: + case R_MIPS_TLS_DTPMOD64: + case R_MIPS_TLS_DTPREL64: + case R_MIPS_TLS_TPREL64: + case (R_MIPS_64 << 8) | R_MIPS_REL32: + return read64(buf); + case R_MIPS_COPY: + return config->is64 ? read64(buf) : read32(buf); + case R_MIPS_NONE: + case R_MIPS_JUMP_SLOT: + case R_MIPS_JALR: + return 0; // The stored value at this location is not the addend. default: + internalLinkerError(getErrorLocation(buf), + "cannot read addend for relocation " + toString(type)); return 0; } } diff --git a/lld/ELF/Arch/X86.cpp b/lld/ELF/Arch/X86.cpp --- a/lld/ELF/Arch/X86.cpp +++ b/lld/ELF/Arch/X86.cpp @@ -250,16 +250,35 @@ case R_386_PC16: return SignExtend64<16>(read16le(buf)); case R_386_32: + case R_386_GLOB_DAT: case R_386_GOT32: case R_386_GOT32X: case R_386_GOTOFF: case R_386_GOTPC: + case R_386_IRELATIVE: case R_386_PC32: case R_386_PLT32: + case R_386_RELATIVE: + case R_386_TLS_DTPMOD32: + case R_386_TLS_DTPOFF32: case R_386_TLS_LDO_32: + case R_386_TLS_LDM: + case R_386_TLS_IE: + case R_386_TLS_IE_32: case R_386_TLS_LE: + case R_386_TLS_LE_32: + case R_386_TLS_GD: + case R_386_TLS_GD_32: + case R_386_TLS_GOTIE: + case R_386_TLS_TPOFF: + case R_386_TLS_TPOFF32: return SignExtend64<32>(read32le(buf)); + case R_386_NONE: + case R_386_JUMP_SLOT: + return 0; // The stored value at this location is not the addend. default: + internalLinkerError(getErrorLocation(buf), + "cannot read addend for relocation " + toString(type)); return 0; } } diff --git a/lld/ELF/Arch/X86_64.cpp b/lld/ELF/Arch/X86_64.cpp --- a/lld/ELF/Arch/X86_64.cpp +++ b/lld/ELF/Arch/X86_64.cpp @@ -37,6 +37,7 @@ uint64_t pltEntryAddr) const override; void relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const override; + int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; void applyJumpInstrMod(uint8_t *loc, JumpModType type, unsigned size) const override; @@ -674,6 +675,52 @@ } } +int64_t X86_64::getImplicitAddend(const uint8_t *buf, RelType type) const { + switch (type) { + case R_X86_64_8: + case R_X86_64_PC8: + return SignExtend64<8>(*buf); + case R_X86_64_16: + case R_X86_64_PC16: + return SignExtend64<16>(read16le(buf)); + case R_X86_64_32: + case R_X86_64_32S: + case R_X86_64_TPOFF32: + case R_X86_64_GOT32: + case R_X86_64_GOTPC32: + case R_X86_64_GOTPC32_TLSDESC: + case R_X86_64_GOTPCREL: + case R_X86_64_GOTPCRELX: + case R_X86_64_REX_GOTPCRELX: + case R_X86_64_PC32: + case R_X86_64_GOTTPOFF: + case R_X86_64_PLT32: + case R_X86_64_TLSGD: + case R_X86_64_TLSLD: + case R_X86_64_DTPOFF32: + case R_X86_64_SIZE32: + return SignExtend64<32>(read32le(buf)); + case R_X86_64_64: + case R_X86_64_DTPOFF64: + case R_X86_64_PC64: + case R_X86_64_SIZE64: + case R_X86_64_GLOB_DAT: + case R_X86_64_GOT64: + case R_X86_64_GOTOFF64: + case R_X86_64_GOTPC64: + case R_X86_64_IRELATIVE: + case R_X86_64_RELATIVE: + return read64le(buf); + case R_X86_64_JUMP_SLOT: + case R_X86_64_NONE: + return 0; // The stored value at this location is not the addend. + default: + internalLinkerError(getErrorLocation(buf), + "cannot read addend for relocation " + toString(type)); + return 0; + } +} + void X86_64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { switch (rel.type) { case R_X86_64_8: diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -20,6 +20,7 @@ #include "llvm/Support/CodeGen.h" #include "llvm/Support/Endian.h" #include "llvm/Support/GlobPattern.h" +#include "llvm/Support/PrettyStackTrace.h" #include #include @@ -356,6 +357,12 @@ else warn(msg); } + +static inline void internalLinkerError(StringRef loc, const Twine &msg) { + errorOrWarn(loc + "internal linker error: " + msg + "\n" + + llvm::getBugReportMsg()); +} + } // namespace elf } // namespace lld diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -1017,8 +1017,13 @@ continue; uint64_t offset = rel.offset; uint8_t *bufLoc = buf + offset; - RelType type = rel.type; + if (rel.expr == R_ADDEND) { + target->relocate(bufLoc, rel, rel.addend); + continue; + } + + RelType type = rel.type; uint64_t addrLoc = getOutputSection()->addr + offset; if (auto *sec = dyn_cast(this)) addrLoc += sec->outSecOff; diff --git a/lld/ELF/OutputSections.h b/lld/ELF/OutputSections.h --- a/lld/ELF/OutputSections.h +++ b/lld/ELF/OutputSections.h @@ -102,6 +102,8 @@ void finalize(); template void writeTo(uint8_t *buf); + // Check that the addends for dynamic relocations were written correctly. + void checkDynRelAddends(const uint8_t *bufStart); template void maybeCompress(); void sort(llvm::function_ref order); diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -19,6 +19,7 @@ #include "llvm/Support/MD5.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/Parallel.h" +#include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/SHA1.h" #include "llvm/Support/TimeProfiler.h" #include @@ -539,6 +540,41 @@ return {0, 0, 0, 0}; } +void OutputSection::checkDynRelAddends(const uint8_t *bufStart) { + assert(config->writeAddends); + assert(type == SHT_REL || type == SHT_RELA); + std::vector 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]. 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(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 " + + toString(rel.type) + " relocation against " + + (rel.sym ? toString(*rel.sym) : "load address") + + " at offset 0x" + utohexstr(rel.getOffset())); + } + }); +} + template void OutputSection::writeHeaderTo(ELF32LE::Shdr *Shdr); template void OutputSection::writeHeaderTo(ELF32BE::Shdr *Shdr); template void OutputSection::writeHeaderTo(ELF64LE::Shdr *Shdr); diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -200,8 +200,11 @@ 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->addReloc({target->tlsDescRel, in.got, off, + sym.isPreemptible + ? DynamicReloc::AgainstSymbol + : DynamicReloc::RelativeWithTargetVA, + sym, 0, R_ABS}); } if (expr != R_TLSDESC_CALL) c.relocations.push_back({expr, type, offset, addend, &sym}); @@ -245,8 +248,8 @@ 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; @@ -283,14 +286,15 @@ in.got->relocations.push_back( {R_ADDEND, target->symbolicRel, off, 1, &sym}); else - mainPart->relaDyn->addReloc(target->tlsModuleIndexRel, in.got, off, &sym); + mainPart->relaDyn->addReloc(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); + sym); else in.got->relocations.push_back( {R_ABS, target->tlsOffsetRel, offsetOff, 0, &sym}); @@ -307,8 +311,8 @@ addend, &sym}); if (!sym.isInGot()) { in.got->addEntry(sym); - mainPart->relaDyn->addReloc(target->tlsGotRel, in.got, sym.getGotOffset(), - &sym); + mainPart->relaDyn->addReloc(target->tlsGotRel, in.got, + sym.getGotOffset(), sym); } } else { c.relocations.push_back( @@ -618,7 +622,7 @@ for (SharedSymbol *sym : getSymbolsAt(ss)) replaceWithDefined(*sym, sec, 0, sym->size); - mainPart->relaDyn->addReloc(target->copyRel, sec, 0, &ss); + mainPart->relaDyn->addReloc(target->copyRel, sec, 0, ss); } // MIPS has an odd notion of "paired" relocations to calculate addends. @@ -1047,7 +1051,7 @@ } // 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(); @@ -1058,12 +1062,12 @@ // 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 @@ -1071,8 +1075,10 @@ 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::RelativeWithTargetVA, + sym, 0, R_ABS}); } static void addGotEntry(Symbol &sym) { @@ -1098,11 +1104,13 @@ // 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 && !isAbsolute(sym)) { - 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 ? DynamicReloc::AgainstSymbol + : DynamicReloc::RelativeWithTargetVA, + sym.isTls() ? target->tlsGotRel : target->gotRel, in.got, off, sym, 0, sym.isPreemptible ? R_ADDEND : R_ABS, target->symbolicRel); } @@ -1160,13 +1168,13 @@ 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->addReloc(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 @@ -1410,7 +1418,7 @@ // 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->addReloc(type, &sec, offset, sym, addend, type); return; } diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -425,39 +425,68 @@ class DynamicReloc { public: + enum Kind { + /// This dynamic relocation is a relative relocation that does not + /// reference a symbol (#sym must be nullptr) and uses #addend as the value + /// of computeAddend(). + RelativeNoSymbol, + /// This dynamic relocation will not be against the symbol but will instead + /// be a relative relocation that simply adds the load address. This means + /// we will write InputSection::getRelocTargetVA() for #sym + #addend as the + /// final Elf_Rel/Elf_Rela addend. + RelativeWithTargetVA, + /// This dynamic relocation references symbol #sym from the dynamic symbol + /// table and uses #addend as the value of computeAddend(). + AgainstSymbol, + /// This dynamic relocation references symbol #sym from the dynamic symbol + /// table and uses InputSection::getRelocTargetVA() + #addend for the + /// final Elf_Rel/Elf_Rela addend. + /// This 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 dynamic relocation is used by the MIPS multi-GOT implementation. It + /// relocates addresses of 64kb pages that lie inside the output section. + MipsMultiGotPage, + }; + DynamicReloc(RelType type, const InputSectionBase *inputSec, + uint64_t offsetInSec, Kind kind, Symbol &sym, int64_t addend = 0, + RelExpr expr = R_ABS) + : type(type), sym(&sym), inputSec(inputSec), offsetInSec(offsetInSec), + kind(kind), expr(expr), addend(addend) {} 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(RelativeNoSymbol), 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 sym && kind != RelativeWithTargetVA; } - // 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 DynamicSection final : public SyntheticSection { @@ -488,18 +517,31 @@ 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 addReloc(RelType dynType, InputSectionBase *isec, uint64_t offsetInSec, + Symbol &sym, int64_t addend = 0, + llvm::Optional 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); + 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 relocs; diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -999,7 +999,9 @@ Symbol *s = p.first; uint64_t offset = p.second * config->wordsize; if (s->isPreemptible) - mainPart->relaDyn->addReloc(target->tlsGotRel, this, offset, s); + mainPart->relaDyn->addReloc({target->tlsGotRel, this, offset, + DynamicReloc::AgainstSymbolWithTargetVA, + *s, 0, R_ABS}); } for (std::pair &p : got.dynTlsSymbols) { Symbol *s = p.first; @@ -1007,7 +1009,7 @@ if (s == nullptr) { if (!config->isPic) 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 @@ -1015,13 +1017,14 @@ // thread-locals that have been marked as local through a linker script) if (!s->isPreemptible && !config->isPic) continue; - mainPart->relaDyn->addReloc(target->tlsModuleIndexRel, this, offset, s); + mainPart->relaDyn->addReloc(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->addReloc(target->tlsOffsetRel, this, offset, *s); } } @@ -1033,7 +1036,7 @@ // Dynamic relocations for "global" entries. for (const std::pair &p : got.global) { uint64_t offset = p.second * config->wordsize; - mainPart->relaDyn->addReloc(target->relativeRel, this, offset, p.first); + mainPart->relaDyn->addReloc(target->relativeRel, this, offset, *p.first); } if (!config->isPic) continue; @@ -1044,13 +1047,14 @@ 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 &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::RelativeWithTargetVA, + *p.first.first, p.first.second, R_ABS}); } } } @@ -1106,7 +1110,7 @@ // If TLS entry has a corresponding dynamic relocations, leave it // initialized by zero. Write down adjusted TLS symbol's values otherwise. // To calculate the adjustments use offsets for thread-local storage. - // https://www.linux-mips.org/wiki/NPTL + // http://web.archive.org/web/20190324223224/https://www.linux-mips.org/wiki/NPTL for (const std::pair &p : g.local16) write(p.second, p.first.first, p.first.second); // Write VA to the primary GOT only. For secondary GOTs that @@ -1576,16 +1580,26 @@ } int64_t DynamicReloc::computeAddend() const { - if (useSymVA) - return sym->getVA(addend); - if (!outputSec) + switch (kind) { + case RelativeNoSymbol: + 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 RelativeWithTargetVA: + case AgainstSymbolWithTargetVA: + return InputSection::getRelocTargetVA(inputSec->file, type, addend, + getOffset(), *sym, expr); + case MipsMultiGotPage: + assert(sym == nullptr); + return getMipsPageAddr(outputSec->addr) + addend; + } + llvm_unreachable(""); } uint32_t DynamicReloc::getSymIndex(SymbolTableBaseSection *symTab) const { - if (sym && !useSymVA) + if (needsDynSymIndex()) return symTab->getSymbolIndex(sym); return 0; } @@ -1597,20 +1611,37 @@ 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::addReloc(RelType dynType, + uint64_t offsetInSec, Symbol &sym, + int64_t addend, + Optional 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::RelativeWithTargetVA, dynType, inputSec, offsetInSec, + sym, addend, expr, addendRelType); +} + +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) { diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp --- a/lld/ELF/Target.cpp +++ b/lld/ELF/Target.cpp @@ -130,6 +130,8 @@ 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; } diff --git a/lld/ELF/Thunks.cpp b/lld/ELF/Thunks.cpp --- a/lld/ELF/Thunks.cpp +++ b/lld/ELF/Thunks.cpp @@ -379,10 +379,10 @@ assert(!dest.isPreemptible); if (Optional 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); } } }; diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -2057,7 +2057,8 @@ 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); } } @@ -2971,6 +2972,13 @@ for (OutputSection *sec : outputSections) if (sec->type != SHT_REL && sec->type != SHT_RELA) sec->writeTo(Out::bufferStart + sec->offset); + + // Finally, check that all dynamic relocation addends were written correctly. + if (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. diff --git a/lld/MachO/CMakeLists.txt b/lld/MachO/CMakeLists.txt --- a/lld/MachO/CMakeLists.txt +++ b/lld/MachO/CMakeLists.txt @@ -52,3 +52,7 @@ MachOOptionsTableGen ${tablegen_deps} ) + +if(HAVE_LIBXAR) + target_link_libraries(lldMachO2 PRIVATE ${XAR_LIB}) +endif() diff --git a/lld/test/ELF/got32-i386-pie-rw.s b/lld/test/ELF/got32-i386-pie-rw.s --- a/lld/test/ELF/got32-i386-pie-rw.s +++ b/lld/test/ELF/got32-i386-pie-rw.s @@ -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" diff --git a/lld/test/ELF/i386-zrel-zrela.s b/lld/test/ELF/i386-zrel-zrela.s --- a/lld/test/ELF/i386-zrel-zrela.s +++ b/lld/test/ELF/i386-zrel-zrela.s @@ -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) @@ -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) @@ -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 diff --git a/lld/test/ELF/ppc64-abs64-dyn.s b/lld/test/ELF/ppc64-abs64-dyn.s --- a/lld/test/ELF/ppc64-abs64-dyn.s +++ b/lld/test/ELF/ppc64-abs64-dyn.s @@ -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 diff --git a/lld/test/ELF/ppc64-long-branch-pi.s b/lld/test/ELF/ppc64-long-branch-pi.s --- a/lld/test/ELF/ppc64-long-branch-pi.s +++ b/lld/test/ELF/ppc64-long-branch-pi.s @@ -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