diff options
Diffstat (limited to 'contrib/llvm/tools/lld')
153 files changed, 12372 insertions, 4476 deletions
diff --git a/contrib/llvm/tools/lld/COFF/CMakeLists.txt b/contrib/llvm/tools/lld/COFF/CMakeLists.txt index 4610ccc880fd..bb241e788c19 100644 --- a/contrib/llvm/tools/lld/COFF/CMakeLists.txt +++ b/contrib/llvm/tools/lld/COFF/CMakeLists.txt @@ -18,7 +18,6 @@ add_lld_library(lldCOFF MarkLive.cpp MinGW.cpp PDB.cpp - Strings.cpp SymbolTable.cpp Symbols.cpp Writer.cpp diff --git a/contrib/llvm/tools/lld/COFF/Chunks.cpp b/contrib/llvm/tools/lld/COFF/Chunks.cpp index 557b02654426..412ff783222b 100644 --- a/contrib/llvm/tools/lld/COFF/Chunks.cpp +++ b/contrib/llvm/tools/lld/COFF/Chunks.cpp @@ -31,8 +31,7 @@ namespace coff { SectionChunk::SectionChunk(ObjFile *F, const coff_section *H) : Chunk(SectionKind), Repl(this), Header(H), File(F), - Relocs(File->getCOFFObj()->getRelocations(Header)), - NumRelocs(std::distance(Relocs.begin(), Relocs.end())) { + Relocs(File->getCOFFObj()->getRelocations(Header)) { // Initialize SectionName. File->getCOFFObj()->getSectionName(Header, SectionName); @@ -51,13 +50,21 @@ static void add64(uint8_t *P, int64_t V) { write64le(P, read64le(P) + V); } static void or16(uint8_t *P, uint16_t V) { write16le(P, read16le(P) | V); } static void or32(uint8_t *P, uint32_t V) { write32le(P, read32le(P) | V); } +// Verify that given sections are appropriate targets for SECREL +// relocations. This check is relaxed because unfortunately debug +// sections have section-relative relocations against absolute symbols. +static bool checkSecRel(const SectionChunk *Sec, OutputSection *OS) { + if (OS) + return true; + if (Sec->isCodeView()) + return false; + fatal("SECREL relocation cannot be applied to absolute symbols"); +} + static void applySecRel(const SectionChunk *Sec, uint8_t *Off, OutputSection *OS, uint64_t S) { - if (!OS) { - if (Sec->isCodeView()) - return; - fatal("SECREL relocation cannot be applied to absolute symbols"); - } + if (!checkSecRel(Sec, OS)) + return; uint64_t SecRel = S - OS->getRVA(); if (SecRel > UINT32_MAX) { error("overflow in SECREL relocation in section: " + Sec->getSectionName()); @@ -67,10 +74,13 @@ static void applySecRel(const SectionChunk *Sec, uint8_t *Off, } static void applySecIdx(uint8_t *Off, OutputSection *OS) { - // If we have no output section, this must be an absolute symbol. Use the - // sentinel absolute symbol section index. - uint16_t SecIdx = OS ? OS->SectionIndex : DefinedAbsolute::OutputSectionIndex; - add16(Off, SecIdx); + // Absolute symbol doesn't have section index, but section index relocation + // against absolute symbol should be resolved to one plus the last output + // section index. This is required for compatibility with MSVC. + if (OS) + add16(Off, OS->SectionIndex); + else + add16(Off, DefinedAbsolute::NumOutputSections + 1); } void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, OutputSection *OS, @@ -88,7 +98,8 @@ void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, OutputSection *OS, case IMAGE_REL_AMD64_SECTION: applySecIdx(Off, OS); break; case IMAGE_REL_AMD64_SECREL: applySecRel(this, Off, OS, S); break; default: - fatal("unsupported relocation type 0x" + Twine::utohexstr(Type)); + fatal("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " + + toString(File)); } } @@ -102,7 +113,8 @@ void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, OutputSection *OS, case IMAGE_REL_I386_SECTION: applySecIdx(Off, OS); break; case IMAGE_REL_I386_SECREL: applySecRel(this, Off, OS, S); break; default: - fatal("unsupported relocation type 0x" + Twine::utohexstr(Type)); + fatal("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " + + toString(File)); } } @@ -112,11 +124,10 @@ static void applyMOV(uint8_t *Off, uint16_t V) { } static uint16_t readMOV(uint8_t *Off) { - uint16_t Opcode1 = read16le(Off); - uint16_t Opcode2 = read16le(Off + 2); - uint16_t Imm = (Opcode2 & 0x00ff) | ((Opcode2 >> 4) & 0x0700); - Imm |= ((Opcode1 << 1) & 0x0800) | ((Opcode1 & 0x000f) << 12); - return Imm; + uint16_t Op1 = read16le(Off); + uint16_t Op2 = read16le(Off + 2); + return (Op2 & 0x00ff) | ((Op2 >> 4) & 0x0700) | ((Op1 << 1) & 0x0800) | + ((Op1 & 0x000f) << 12); } void applyMOV32T(uint8_t *Off, uint32_t V) { @@ -153,7 +164,7 @@ void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S, uint64_t P) const { // Pointer to thumb code must have the LSB set. uint64_t SX = S; - if (OS && (OS->getPermissions() & IMAGE_SCN_MEM_EXECUTE)) + if (OS && (OS->Header.Characteristics & IMAGE_SCN_MEM_EXECUTE)) SX |= 1; switch (Type) { case IMAGE_REL_ARM_ADDR32: add32(Off, SX + Config->ImageBase); break; @@ -165,18 +176,19 @@ void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS, case IMAGE_REL_ARM_SECTION: applySecIdx(Off, OS); break; case IMAGE_REL_ARM_SECREL: applySecRel(this, Off, OS, S); break; default: - fatal("unsupported relocation type 0x" + Twine::utohexstr(Type)); + fatal("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " + + toString(File)); } } // Interpret the existing immediate value as a byte offset to the // target symbol, then update the instruction with the immediate as // the page offset from the current instruction to the target. -static void applyArm64Addr(uint8_t *Off, uint64_t S, uint64_t P) { +static void applyArm64Addr(uint8_t *Off, uint64_t S, uint64_t P, int Shift) { uint32_t Orig = read32le(Off); uint64_t Imm = ((Orig >> 29) & 0x3) | ((Orig >> 3) & 0x1FFFFC); S += Imm; - Imm = (S >> 12) - (P >> 12); + Imm = (S >> Shift) - (P >> Shift); uint32_t ImmLo = (Imm & 0x3) << 29; uint32_t ImmHi = (Imm & 0x1FFFFC) << 3; uint64_t Mask = (0x3 << 29) | (0x1FFFFC << 3); @@ -213,19 +225,70 @@ static void applyArm64Ldr(uint8_t *Off, uint64_t Imm) { applyArm64Imm(Off, Imm >> Size, Size); } +static void applySecRelLow12A(const SectionChunk *Sec, uint8_t *Off, + OutputSection *OS, uint64_t S) { + if (checkSecRel(Sec, OS)) + applyArm64Imm(Off, (S - OS->getRVA()) & 0xfff, 0); +} + +static void applySecRelHigh12A(const SectionChunk *Sec, uint8_t *Off, + OutputSection *OS, uint64_t S) { + if (!checkSecRel(Sec, OS)) + return; + uint64_t SecRel = (S - OS->getRVA()) >> 12; + if (0xfff < SecRel) { + error("overflow in SECREL_HIGH12A relocation in section: " + + Sec->getSectionName()); + return; + } + applyArm64Imm(Off, SecRel & 0xfff, 0); +} + +static void applySecRelLdr(const SectionChunk *Sec, uint8_t *Off, + OutputSection *OS, uint64_t S) { + if (checkSecRel(Sec, OS)) + applyArm64Ldr(Off, (S - OS->getRVA()) & 0xfff); +} + +static void applyArm64Branch26(uint8_t *Off, int64_t V) { + if (!isInt<28>(V)) + fatal("relocation out of range"); + or32(Off, (V & 0x0FFFFFFC) >> 2); +} + +static void applyArm64Branch19(uint8_t *Off, int64_t V) { + if (!isInt<21>(V)) + fatal("relocation out of range"); + or32(Off, (V & 0x001FFFFC) << 3); +} + +static void applyArm64Branch14(uint8_t *Off, int64_t V) { + if (!isInt<16>(V)) + fatal("relocation out of range"); + or32(Off, (V & 0x0000FFFC) << 3); +} + void SectionChunk::applyRelARM64(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S, uint64_t P) const { switch (Type) { - case IMAGE_REL_ARM64_PAGEBASE_REL21: applyArm64Addr(Off, S, P); break; + case IMAGE_REL_ARM64_PAGEBASE_REL21: applyArm64Addr(Off, S, P, 12); break; + case IMAGE_REL_ARM64_REL21: applyArm64Addr(Off, S, P, 0); break; case IMAGE_REL_ARM64_PAGEOFFSET_12A: applyArm64Imm(Off, S & 0xfff, 0); break; case IMAGE_REL_ARM64_PAGEOFFSET_12L: applyArm64Ldr(Off, S & 0xfff); break; - case IMAGE_REL_ARM64_BRANCH26: or32(Off, ((S - P) & 0x0FFFFFFC) >> 2); break; + case IMAGE_REL_ARM64_BRANCH26: applyArm64Branch26(Off, S - P); break; + case IMAGE_REL_ARM64_BRANCH19: applyArm64Branch19(Off, S - P); break; + case IMAGE_REL_ARM64_BRANCH14: applyArm64Branch14(Off, S - P); break; case IMAGE_REL_ARM64_ADDR32: add32(Off, S + Config->ImageBase); break; case IMAGE_REL_ARM64_ADDR32NB: add32(Off, S); break; case IMAGE_REL_ARM64_ADDR64: add64(Off, S + Config->ImageBase); break; case IMAGE_REL_ARM64_SECREL: applySecRel(this, Off, OS, S); break; + case IMAGE_REL_ARM64_SECREL_LOW12A: applySecRelLow12A(this, Off, OS, S); break; + case IMAGE_REL_ARM64_SECREL_HIGH12A: applySecRelHigh12A(this, Off, OS, S); break; + case IMAGE_REL_ARM64_SECREL_LOW12L: applySecRelLdr(this, Off, OS, S); break; + case IMAGE_REL_ARM64_SECTION: applySecIdx(Off, OS); break; default: - fatal("unsupported relocation type 0x" + Twine::utohexstr(Type)); + fatal("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " + + toString(File)); } } @@ -234,7 +297,8 @@ void SectionChunk::writeTo(uint8_t *Buf) const { return; // Copy section contents from source object file to output file. ArrayRef<uint8_t> A = getContents(); - memcpy(Buf + OutputSectionOff, A.data(), A.size()); + if (!A.empty()) + memcpy(Buf + OutputSectionOff, A.data(), A.size()); // Apply relocations. size_t InputSize = getSize(); @@ -350,8 +414,8 @@ bool SectionChunk::hasData() const { return !(Header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA); } -uint32_t SectionChunk::getPermissions() const { - return Header->Characteristics & PermMask; +uint32_t SectionChunk::getOutputCharacteristics() const { + return Header->Characteristics & (PermMask | TypeMask); } bool SectionChunk::isCOMDAT() const { @@ -378,6 +442,7 @@ ArrayRef<uint8_t> SectionChunk::getContents() const { } void SectionChunk::replace(SectionChunk *Other) { + Alignment = std::max(Alignment, Other->Alignment); Other->Repl = Repl; Other->Live = false; } @@ -388,7 +453,7 @@ CommonChunk::CommonChunk(const COFFSymbolRef S) : Sym(S) { Alignment = std::min(uint64_t(32), PowerOf2Ceil(Sym.getValue())); } -uint32_t CommonChunk::getPermissions() const { +uint32_t CommonChunk::getOutputCharacteristics() const { return IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE; } @@ -433,7 +498,7 @@ void ImportThunkChunkARM::writeTo(uint8_t *Buf) const { void ImportThunkChunkARM64::writeTo(uint8_t *Buf) const { int64_t Off = ImpSymbol->getRVA() & 0xfff; memcpy(Buf + OutputSectionOff, ImportThunkARM64, sizeof(ImportThunkARM64)); - applyArm64Addr(Buf + OutputSectionOff, ImpSymbol->getRVA(), RVA); + applyArm64Addr(Buf + OutputSectionOff, ImpSymbol->getRVA(), RVA, 12); applyArm64Ldr(Buf + OutputSectionOff + 4, Off); } @@ -453,12 +518,14 @@ void LocalImportChunk::writeTo(uint8_t *Buf) const { } } -void SEHTableChunk::writeTo(uint8_t *Buf) const { +void RVATableChunk::writeTo(uint8_t *Buf) const { ulittle32_t *Begin = reinterpret_cast<ulittle32_t *>(Buf + OutputSectionOff); size_t Cnt = 0; - for (Defined *D : Syms) - Begin[Cnt++] = D->getRVA(); + for (const ChunkAndOffset &CO : Syms) + Begin[Cnt++] = CO.InputChunk->getRVA() + CO.Offset; std::sort(Begin, Begin + Cnt); + assert(std::unique(Begin, Begin + Cnt) == Begin + Cnt && + "RVA tables should be de-duplicated"); } // Windows-specific. This class represents a block in .reloc section. @@ -531,5 +598,47 @@ uint8_t Baserel::getDefaultType() { } } +std::map<uint32_t, MergeChunk *> MergeChunk::Instances; + +MergeChunk::MergeChunk(uint32_t Alignment) + : Builder(StringTableBuilder::RAW, Alignment) { + this->Alignment = Alignment; +} + +void MergeChunk::addSection(SectionChunk *C) { + auto *&MC = Instances[C->Alignment]; + if (!MC) + MC = make<MergeChunk>(C->Alignment); + MC->Sections.push_back(C); +} + +void MergeChunk::finalizeContents() { + for (SectionChunk *C : Sections) + if (C->isLive()) + Builder.add(toStringRef(C->getContents())); + Builder.finalize(); + + for (SectionChunk *C : Sections) { + if (!C->isLive()) + continue; + size_t Off = Builder.getOffset(toStringRef(C->getContents())); + C->setOutputSection(Out); + C->setRVA(RVA + Off); + C->OutputSectionOff = OutputSectionOff + Off; + } +} + +uint32_t MergeChunk::getOutputCharacteristics() const { + return IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA; +} + +size_t MergeChunk::getSize() const { + return Builder.getSize(); +} + +void MergeChunk::writeTo(uint8_t *Buf) const { + Builder.write(Buf + OutputSectionOff); +} + } // namespace coff } // namespace lld diff --git a/contrib/llvm/tools/lld/COFF/Chunks.h b/contrib/llvm/tools/lld/COFF/Chunks.h index 381527ee6ef2..9e896531bd9a 100644 --- a/contrib/llvm/tools/lld/COFF/Chunks.h +++ b/contrib/llvm/tools/lld/COFF/Chunks.h @@ -16,6 +16,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/iterator.h" #include "llvm/ADT/iterator_range.h" +#include "llvm/MC/StringTableBuilder.h" #include "llvm/Object/COFF.h" #include <utility> #include <vector> @@ -37,9 +38,11 @@ class ObjFile; class OutputSection; class Symbol; -// Mask for section types (code, data, bss, disacardable, etc.) -// and permissions (writable, readable or executable). -const uint32_t PermMask = 0xFF0000F0; +// Mask for permissions (discardable, writable, readable, executable, etc). +const uint32_t PermMask = 0xFE000000; + +// Mask for section types (code, data, bss). +const uint32_t TypeMask = 0x000000E0; // A Chunk represents a chunk of data that will occupy space in the // output (if the resolver chose that). It may or may not be backed by @@ -60,6 +63,10 @@ public: // before calling this function. virtual void writeTo(uint8_t *Buf) const {} + // Called by the writer after an RVA is assigned, but before calling + // getSize(). + virtual void finalizeContents() {} + // The writer sets and uses the addresses. uint64_t getRVA() const { return RVA; } void setRVA(uint64_t V) { RVA = V; } @@ -70,7 +77,7 @@ public: virtual bool hasData() const { return true; } // Returns readable/writable/executable bits. - virtual uint32_t getPermissions() const { return 0; } + virtual uint32_t getOutputCharacteristics() const { return 0; } // Returns the section name if this is a section chunk. // It is illegal to call this function on non-section chunks. @@ -137,7 +144,7 @@ public: ArrayRef<uint8_t> getContents() const; void writeTo(uint8_t *Buf) const override; bool hasData() const override; - uint32_t getPermissions() const override; + uint32_t getOutputCharacteristics() const override; StringRef getSectionName() const override { return SectionName; } void getBaserels(std::vector<Baserel> *Res) override; bool isCOMDAT() const; @@ -208,11 +215,11 @@ public: // The COMDAT leader symbol if this is a COMDAT chunk. DefinedRegular *Sym = nullptr; + ArrayRef<coff_relocation> Relocs; + private: StringRef SectionName; std::vector<SectionChunk *> AssocChildren; - llvm::iterator_range<const coff_relocation *> Relocs; - size_t NumRelocs; // Used by the garbage collector. bool Live; @@ -222,13 +229,40 @@ private: uint32_t Class[2] = {0, 0}; }; +// This class is used to implement an lld-specific feature (not implemented in +// MSVC) that minimizes the output size by finding string literals sharing tail +// parts and merging them. +// +// If string tail merging is enabled and a section is identified as containing a +// string literal, it is added to a MergeChunk with an appropriate alignment. +// The MergeChunk then tail merges the strings using the StringTableBuilder +// class and assigns RVAs and section offsets to each of the member chunks based +// on the offsets assigned by the StringTableBuilder. +class MergeChunk : public Chunk { +public: + MergeChunk(uint32_t Alignment); + static void addSection(SectionChunk *C); + void finalizeContents() override; + + uint32_t getOutputCharacteristics() const override; + StringRef getSectionName() const override { return ".rdata"; } + size_t getSize() const override; + void writeTo(uint8_t *Buf) const override; + + static std::map<uint32_t, MergeChunk *> Instances; + std::vector<SectionChunk *> Sections; + +private: + llvm::StringTableBuilder Builder; +}; + // A chunk for common symbols. Common chunks don't have actual data. class CommonChunk : public Chunk { public: CommonChunk(const COFFSymbolRef Sym); size_t getSize() const override { return Sym.getValue(); } bool hasData() const override { return false; } - uint32_t getPermissions() const override; + uint32_t getOutputCharacteristics() const override; StringRef getSectionName() const override { return ".bss"; } private: @@ -320,17 +354,41 @@ private: Defined *Sym; }; -// Windows-specific. -// A chunk for SEH table which contains RVAs of safe exception handler -// functions. x86-only. -class SEHTableChunk : public Chunk { +// Duplicate RVAs are not allowed in RVA tables, so unique symbols by chunk and +// offset into the chunk. Order does not matter as the RVA table will be sorted +// later. +struct ChunkAndOffset { + Chunk *InputChunk; + uint32_t Offset; + + struct DenseMapInfo { + static ChunkAndOffset getEmptyKey() { + return {llvm::DenseMapInfo<Chunk *>::getEmptyKey(), 0}; + } + static ChunkAndOffset getTombstoneKey() { + return {llvm::DenseMapInfo<Chunk *>::getTombstoneKey(), 0}; + } + static unsigned getHashValue(const ChunkAndOffset &CO) { + return llvm::DenseMapInfo<std::pair<Chunk *, uint32_t>>::getHashValue( + {CO.InputChunk, CO.Offset}); + } + static bool isEqual(const ChunkAndOffset &LHS, const ChunkAndOffset &RHS) { + return LHS.InputChunk == RHS.InputChunk && LHS.Offset == RHS.Offset; + } + }; +}; + +using SymbolRVASet = llvm::DenseSet<ChunkAndOffset>; + +// Table which contains symbol RVAs. Used for /safeseh and /guard:cf. +class RVATableChunk : public Chunk { public: - explicit SEHTableChunk(std::set<Defined *> S) : Syms(std::move(S)) {} + explicit RVATableChunk(SymbolRVASet S) : Syms(std::move(S)) {} size_t getSize() const override { return Syms.size() * 4; } void writeTo(uint8_t *Buf) const override; private: - std::set<Defined *> Syms; + SymbolRVASet Syms; }; // Windows-specific. @@ -362,4 +420,10 @@ void applyBranch24T(uint8_t *Off, int32_t V); } // namespace coff } // namespace lld +namespace llvm { +template <> +struct DenseMapInfo<lld::coff::ChunkAndOffset> + : lld::coff::ChunkAndOffset::DenseMapInfo {}; +} + #endif diff --git a/contrib/llvm/tools/lld/COFF/Config.h b/contrib/llvm/tools/lld/COFF/Config.h index b01689930fac..3ae50b868333 100644 --- a/contrib/llvm/tools/lld/COFF/Config.h +++ b/contrib/llvm/tools/lld/COFF/Config.h @@ -10,6 +10,7 @@ #ifndef LLD_COFF_CONFIG_H #define LLD_COFF_CONFIG_H +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Object/COFF.h" #include "llvm/Support/CachePruning.h" @@ -71,6 +72,12 @@ enum class DebugType { Fixup = 0x4, /// Relocation Table }; +enum class GuardCFLevel { + Off, + NoLongJmp, // Emit gfids but no longjmp tables + Full, // Enable all protections. +}; + // Global configuration. struct Configuration { enum ManifestKind { SideBySide, Embed, No }; @@ -85,13 +92,19 @@ struct Configuration { std::string ImportName; bool DoGC = true; bool DoICF = true; + bool TailMerge; bool Relocatable = true; bool Force = false; bool Debug = false; bool DebugDwarf = false; bool DebugGHashes = false; + bool DebugSymtab = false; + bool ShowTiming = false; unsigned DebugTypes = static_cast<unsigned>(DebugType::None); + std::vector<std::string> NatvisFiles; + llvm::SmallString<128> PDBAltPath; llvm::SmallString<128> PDBPath; + llvm::SmallString<128> PDBSourcePath; std::vector<llvm::StringRef> Argv; // Symbols in this set are considered as live by the garbage collector. @@ -110,15 +123,18 @@ struct Configuration { bool SaveTemps = false; + // /guard:cf + GuardCFLevel GuardCF = GuardCFLevel::Off; + // Used for SafeSEH. Symbol *SEHTable = nullptr; Symbol *SEHCount = nullptr; // Used for /opt:lldlto=N - unsigned LTOOptLevel = 2; + unsigned LTOO = 2; // Used for /opt:lldltojobs=N - unsigned LTOJobs = 0; + unsigned ThinLTOJobs = 0; // Used for /opt:lldltopartitions=N unsigned LTOPartitions = 1; @@ -152,6 +168,9 @@ struct Configuration { // Used for /alternatename. std::map<StringRef, StringRef> AlternateNames; + // Used for /order. + llvm::StringMap<int> Order; + // Used for /lldmap. std::string MapFile; @@ -164,7 +183,7 @@ struct Configuration { uint32_t MinorImageVersion = 0; uint32_t MajorOSVersion = 6; uint32_t MinorOSVersion = 0; - bool CanExitEarly = false; + uint32_t Timestamp = 0; bool DynamicBase = true; bool AllowBind = true; bool NxCompat = true; @@ -174,8 +193,12 @@ struct Configuration { bool HighEntropyVA = false; bool AppContainer = false; bool MinGW = false; + bool WarnMissingOrderSymbol = true; bool WarnLocallyDefinedImported = true; + bool Incremental = true; + bool IntegrityCheck = false; bool KillAt = false; + bool Repro = false; }; extern Configuration *Config; diff --git a/contrib/llvm/tools/lld/COFF/DLL.cpp b/contrib/llvm/tools/lld/COFF/DLL.cpp index 195839139670..464abe8e0894 100644 --- a/contrib/llvm/tools/lld/COFF/DLL.cpp +++ b/contrib/llvm/tools/lld/COFF/DLL.cpp @@ -18,8 +18,8 @@ // //===----------------------------------------------------------------------===// -#include "Chunks.h" #include "DLL.h" +#include "Chunks.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Path.h" diff --git a/contrib/llvm/tools/lld/COFF/DLL.h b/contrib/llvm/tools/lld/COFF/DLL.h index ad312789edf1..c5d6e7c93abf 100644 --- a/contrib/llvm/tools/lld/COFF/DLL.h +++ b/contrib/llvm/tools/lld/COFF/DLL.h @@ -76,6 +76,11 @@ class EdataContents { public: EdataContents(); std::vector<Chunk *> Chunks; + + uint64_t getRVA() { return Chunks[0]->getRVA(); } + uint64_t getSize() { + return Chunks.back()->getRVA() + Chunks.back()->getSize() - getRVA(); + } }; } // namespace coff diff --git a/contrib/llvm/tools/lld/COFF/Driver.cpp b/contrib/llvm/tools/lld/COFF/Driver.cpp index d4030588e211..eefdb48beadd 100644 --- a/contrib/llvm/tools/lld/COFF/Driver.cpp +++ b/contrib/llvm/tools/lld/COFF/Driver.cpp @@ -9,14 +9,18 @@ #include "Driver.h" #include "Config.h" +#include "ICF.h" #include "InputFiles.h" +#include "MarkLive.h" #include "MinGW.h" #include "SymbolTable.h" #include "Symbols.h" #include "Writer.h" +#include "lld/Common/Args.h" #include "lld/Common/Driver.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" +#include "lld/Common/Timer.h" #include "lld/Common/Version.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" @@ -35,9 +39,8 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/ToolDrivers/llvm-lib/LibDriver.h" #include <algorithm> -#include <memory> - #include <future> +#include <memory> using namespace llvm; using namespace llvm::object; @@ -47,20 +50,20 @@ using llvm::sys::Process; namespace lld { namespace coff { +static Timer InputFileTimer("Input File Reading", Timer::root()); + Configuration *Config; LinkerDriver *Driver; bool link(ArrayRef<const char *> Args, bool CanExitEarly, raw_ostream &Diag) { - errorHandler().LogName = Args[0]; + errorHandler().LogName = sys::path::filename(Args[0]); errorHandler().ErrorOS = &Diag; errorHandler().ColorDiagnostics = Diag.has_colors(); errorHandler().ErrorLimitExceededMsg = "too many errors emitted, stopping now" - " (use /ERRORLIMIT:0 to see all errors)"; + " (use /errorlimit:0 to see all errors)"; errorHandler().ExitEarly = CanExitEarly; Config = make<Configuration>(); - Config->Argv = {Args.begin(), Args.end()}; - Config->CanExitEarly = CanExitEarly; Symtab = make<SymbolTable>(); @@ -72,6 +75,9 @@ bool link(ArrayRef<const char *> Args, bool CanExitEarly, raw_ostream &Diag) { exitLld(errorCount() ? 1 : 0); freeArena(); + ObjFile::Instances.clear(); + ImportFile::Instances.clear(); + BitcodeFile::Instances.clear(); return !errorCount(); } @@ -93,7 +99,7 @@ typedef std::pair<std::unique_ptr<MemoryBuffer>, std::error_code> MBErrPair; // Create a std::future that opens and maps a file using the best strategy for // the host platform. static std::future<MBErrPair> createFutureForFile(std::string Path) { -#if LLVM_ON_WIN32 +#if _WIN32 // On Windows, file I/O is relatively slow so it is best to do this // asynchronously. auto Strategy = std::launch::async; @@ -101,7 +107,9 @@ static std::future<MBErrPair> createFutureForFile(std::string Path) { auto Strategy = std::launch::deferred; #endif return std::async(Strategy, [=]() { - auto MBOrErr = MemoryBuffer::getFile(Path); + auto MBOrErr = MemoryBuffer::getFile(Path, + /*FileSize*/ -1, + /*RequiresNullTerminator*/ false); if (!MBOrErr) return MBErrPair{nullptr, MBOrErr.getError()}; return MBErrPair{std::move(*MBOrErr), std::error_code()}; @@ -120,39 +128,46 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> MB) { void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> MB, bool WholeArchive) { + StringRef Filename = MB->getBufferIdentifier(); + MemoryBufferRef MBRef = takeBuffer(std::move(MB)); - FilePaths.push_back(MBRef.getBufferIdentifier()); + FilePaths.push_back(Filename); // File type is detected by contents, not by file extension. switch (identify_magic(MBRef.getBuffer())) { case file_magic::windows_resource: Resources.push_back(MBRef); break; - case file_magic::archive: if (WholeArchive) { std::unique_ptr<Archive> File = - CHECK(Archive::create(MBRef), - MBRef.getBufferIdentifier() + ": failed to parse archive"); + CHECK(Archive::create(MBRef), Filename + ": failed to parse archive"); for (MemoryBufferRef M : getArchiveMembers(File.get())) - addArchiveBuffer(M, "<whole-archive>", MBRef.getBufferIdentifier()); + addArchiveBuffer(M, "<whole-archive>", Filename); return; } Symtab->addFile(make<ArchiveFile>(MBRef)); break; - case file_magic::bitcode: Symtab->addFile(make<BitcodeFile>(MBRef)); break; - + case file_magic::coff_object: + case file_magic::coff_import_library: + Symtab->addFile(make<ObjFile>(MBRef)); + break; case file_magic::coff_cl_gl_object: - error(MBRef.getBufferIdentifier() + ": is not a native COFF file. " - "Recompile without /GL"); + error(Filename + ": is not a native COFF file. Recompile without /GL"); break; - + case file_magic::pecoff_executable: + if (Filename.endswith_lower(".dll")) { + error(Filename + ": bad file type. Did you specify a DLL instead of an " + "import library?"); + break; + } + LLVM_FALLTHROUGH; default: - Symtab->addFile(make<ObjFile>(MBRef)); + error(MBRef.getBufferIdentifier() + ": unknown file type"); break; } } @@ -228,7 +243,29 @@ static bool isDecorated(StringRef Sym) { void LinkerDriver::parseDirectives(StringRef S) { ArgParser Parser; // .drectve is always tokenized using Windows shell rules. - opt::InputArgList Args = Parser.parseDirectives(S); + // /EXPORT: option can appear too many times, processing in fastpath. + opt::InputArgList Args; + std::vector<StringRef> Exports; + std::tie(Args, Exports) = Parser.parseDirectives(S); + + for (StringRef E : Exports) { + // If a common header file contains dllexported function + // declarations, many object files may end up with having the + // same /EXPORT options. In order to save cost of parsing them, + // we dedup them first. + if (!DirectivesExports.insert(E).second) + continue; + + Export Exp = parseExport(E); + if (Config->Machine == I386 && Config->MinGW) { + if (!isDecorated(Exp.Name)) + Exp.Name = Saver.save("_" + Exp.Name); + if (!Exp.ExtName.empty() && !isDecorated(Exp.ExtName)) + Exp.ExtName = Saver.save("_" + Exp.ExtName); + } + Exp.Directives = true; + Config->Exports.push_back(Exp); + } for (auto *Arg : Args) { switch (Arg->getOption().getUnaliasedOption().getID()) { @@ -245,25 +282,6 @@ void LinkerDriver::parseDirectives(StringRef S) { case OPT_entry: Config->Entry = addUndefined(mangle(Arg->getValue())); break; - case OPT_export: { - // If a common header file contains dllexported function - // declarations, many object files may end up with having the - // same /EXPORT options. In order to save cost of parsing them, - // we dedup them first. - if (!DirectivesExports.insert(Arg->getValue()).second) - break; - - Export E = parseExport(Arg->getValue()); - if (Config->Machine == I386 && Config->MinGW) { - if (!isDecorated(E.Name)) - E.Name = Saver.save("_" + E.Name); - if (!E.ExtName.empty() && !isDecorated(E.ExtName)) - E.ExtName = Saver.save("_" + E.ExtName); - } - E.Directives = true; - Config->Exports.push_back(E); - break; - } case OPT_failifmismatch: checkFailIfMismatch(Arg->getValue()); break; @@ -316,13 +334,24 @@ StringRef LinkerDriver::doFindFile(StringRef Filename) { return Filename; } +static Optional<sys::fs::UniqueID> getUniqueID(StringRef Path) { + sys::fs::UniqueID Ret; + if (sys::fs::getUniqueID(Path, Ret)) + return None; + return Ret; +} + // Resolves a file path. This never returns the same path // (in that case, it returns None). Optional<StringRef> LinkerDriver::findFile(StringRef Filename) { StringRef Path = doFindFile(Filename); - bool Seen = !VisitedFiles.insert(Path.lower()).second; - if (Seen) - return None; + + if (Optional<sys::fs::UniqueID> ID = getUniqueID(Path)) { + bool Seen = !VisitedFiles.insert(*ID).second; + if (Seen) + return None; + } + if (Path.endswith_lower(".lib")) VisitedLibs.insert(sys::path::filename(Path)); return Path; @@ -345,11 +374,14 @@ Optional<StringRef> LinkerDriver::findLib(StringRef Filename) { return None; if (!VisitedLibs.insert(Filename.lower()).second) return None; + StringRef Path = doFindLib(Filename); if (Config->NoDefaultLibs.count(Path)) return None; - if (!VisitedFiles.insert(Path.lower()).second) - return None; + + if (Optional<sys::fs::UniqueID> ID = getUniqueID(Path)) + if (!VisitedFiles.insert(*ID).second) + return None; return Path; } @@ -384,7 +416,24 @@ StringRef LinkerDriver::mangle(StringRef Sym) { } // Windows specific -- find default entry point name. +// +// There are four different entry point functions for Windows executables, +// each of which corresponds to a user-defined "main" function. This function +// infers an entry point from a user-defined "main" function. StringRef LinkerDriver::findDefaultEntry() { + // As a special case, if /nodefaultlib is given, we directly look for an + // entry point. This is because, if no default library is linked, users + // need to define an entry point instead of a "main". + if (Config->NoDefaultLibAll) { + for (StringRef S : {"mainCRTStartup", "wmainCRTStartup", + "WinMainCRTStartup", "wWinMainCRTStartup"}) { + StringRef Entry = Symtab->findMangle(S); + if (!Entry.empty() && !isa<Undefined>(Symtab->find(Entry))) + return mangle(S); + } + return ""; + } + // User-defined main functions and their corresponding entry points. static const char *Entries[][2] = { {"main", "mainCRTStartup"}, @@ -534,10 +583,49 @@ static void createImportLibrary(bool AsLib) { Exports.push_back(E2); } - auto E = writeImportLibrary(getImportName(AsLib), getImplibPath(), Exports, - Config->Machine, false); - handleAllErrors(std::move(E), - [&](ErrorInfoBase &EIB) { error(EIB.message()); }); + auto HandleError = [](Error &&E) { + handleAllErrors(std::move(E), + [](ErrorInfoBase &EIB) { error(EIB.message()); }); + }; + std::string LibName = getImportName(AsLib); + std::string Path = getImplibPath(); + + if (!Config->Incremental) { + HandleError(writeImportLibrary(LibName, Path, Exports, Config->Machine, + Config->MinGW)); + return; + } + + // If the import library already exists, replace it only if the contents + // have changed. + ErrorOr<std::unique_ptr<MemoryBuffer>> OldBuf = MemoryBuffer::getFile( + Path, /*FileSize*/ -1, /*RequiresNullTerminator*/ false); + if (!OldBuf) { + HandleError(writeImportLibrary(LibName, Path, Exports, Config->Machine, + Config->MinGW)); + return; + } + + SmallString<128> TmpName; + if (std::error_code EC = + sys::fs::createUniqueFile(Path + ".tmp-%%%%%%%%.lib", TmpName)) + fatal("cannot create temporary file for import library " + Path + ": " + + EC.message()); + + if (Error E = writeImportLibrary(LibName, TmpName, Exports, Config->Machine, + Config->MinGW)) { + HandleError(std::move(E)); + return; + } + + std::unique_ptr<MemoryBuffer> NewBuf = check(MemoryBuffer::getFile( + TmpName, /*FileSize*/ -1, /*RequiresNullTerminator*/ false)); + if ((*OldBuf)->getBuffer() != NewBuf->getBuffer()) { + OldBuf->reset(); + HandleError(errorCodeToError(sys::fs::rename(TmpName, Path))); + } else { + sys::fs::remove(TmpName); + } } static void parseModuleDefs(StringRef Path) { @@ -570,9 +658,18 @@ static void parseModuleDefs(StringRef Path) { for (COFFShortExport E1 : M.Exports) { Export E2; + // In simple cases, only Name is set. Renamed exports are parsed + // and set as "ExtName = Name". If Name has the form "OtherDll.Func", + // it shouldn't be a normal exported function but a forward to another + // DLL instead. This is supported by both MS and GNU linkers. + if (E1.ExtName != E1.Name && StringRef(E1.Name).contains('.')) { + E2.Name = Saver.save(E1.ExtName); + E2.ForwardTo = Saver.save(E1.Name); + Config->Exports.push_back(E2); + continue; + } E2.Name = Saver.save(E1.Name); - if (E1.isWeak()) - E2.ExtName = Saver.save(E1.ExtName); + E2.ExtName = Saver.save(E1.ExtName); E2.Ordinal = E1.Ordinal; E2.Noname = E1.Noname; E2.Data = E1.Data; @@ -635,8 +732,8 @@ filterBitcodeFiles(StringRef Path, std::vector<std::string> &TemporaryFiles) { log("Creating a temporary archive for " + Path + " to remove bitcode files"); SmallString<128> S; - if (auto EC = sys::fs::createTemporaryFile("lld-" + sys::path::stem(Path), - ".lib", S)) + if (std::error_code EC = sys::fs::createTemporaryFile( + "lld-" + sys::path::stem(Path), ".lib", S)) fatal("cannot create a temporary file: " + EC.message()); std::string Temp = S.str(); TemporaryFiles.push_back(Temp); @@ -712,6 +809,8 @@ void LinkerDriver::enqueueTask(std::function<void()> Task) { } bool LinkerDriver::run() { + ScopedTimer T(InputFileTimer); + bool DidWork = !TaskQueue.empty(); while (!TaskQueue.empty()) { TaskQueue.front()(); @@ -720,6 +819,46 @@ bool LinkerDriver::run() { return DidWork; } +// Parse an /order file. If an option is given, the linker places +// COMDAT sections in the same order as their names appear in the +// given file. +static void parseOrderFile(StringRef Arg) { + // For some reason, the MSVC linker requires a filename to be + // preceded by "@". + if (!Arg.startswith("@")) { + error("malformed /order option: '@' missing"); + return; + } + + // Get a list of all comdat sections for error checking. + DenseSet<StringRef> Set; + for (Chunk *C : Symtab->getChunks()) + if (auto *Sec = dyn_cast<SectionChunk>(C)) + if (Sec->Sym) + Set.insert(Sec->Sym->getName()); + + // Open a file. + StringRef Path = Arg.substr(1); + std::unique_ptr<MemoryBuffer> MB = CHECK( + MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path); + + // Parse a file. An order file contains one symbol per line. + // All symbols that were not present in a given order file are + // considered to have the lowest priority 0 and are placed at + // end of an output section. + for (std::string S : args::getLines(MB->getMemBufferRef())) { + if (Config->Machine == I386 && !isDecorated(S)) + S = "_" + S; + + if (Set.count(S) == 0) { + if (Config->WarnMissingOrderSymbol) + warn("/order:" + Arg + ": missing symbol: " + S + " [LNK4037]"); + } + else + Config->Order[S] = INT_MIN + Config->Order.size(); + } +} + void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // If the first command line argument is "/lib", link.exe acts like lib.exe. // We call our own implementation of lib.exe that understands bitcode files. @@ -735,11 +874,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { InitializeAllTargetMCs(); InitializeAllAsmParsers(); InitializeAllAsmPrinters(); - InitializeAllDisassemblers(); // Parse command line options. ArgParser Parser; - opt::InputArgList Args = Parser.parseLINK(ArgsArr.slice(1)); + opt::InputArgList Args = Parser.parseLINK(ArgsArr); // Parse and evaluate -mllvm options. std::vector<const char *> V; @@ -763,6 +901,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { return; } + if (Args.hasArg(OPT_show_timing)) + Config->ShowTiming = true; + + ScopedTimer T(Timer::root()); // Handle --version, which is an lld extension. This option is a bit odd // because it doesn't start with "/", but we deliberately chose "--" to // avoid conflict with /version and for compatibility with clang-cl. @@ -805,7 +947,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // Handle /ignore for (auto *Arg : Args.filtered(OPT_ignore)) { - if (StringRef(Arg->getValue()) == "4217") + if (StringRef(Arg->getValue()) == "4037") + Config->WarnMissingOrderSymbol = false; + else if (StringRef(Arg->getValue()) == "4217") Config->WarnLocallyDefinedImported = false; // Other warning numbers are ignored. } @@ -826,6 +970,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // Handle /debug if (Args.hasArg(OPT_debug, OPT_debug_dwarf, OPT_debug_ghash)) { Config->Debug = true; + Config->Incremental = true; if (auto *Arg = Args.getLastArg(OPT_debugtype)) Config->DebugTypes = parseDebugType(Arg->getValue()); else @@ -834,9 +979,17 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // Handle /pdb bool ShouldCreatePDB = Args.hasArg(OPT_debug, OPT_debug_ghash); - if (ShouldCreatePDB) + if (ShouldCreatePDB) { if (auto *Arg = Args.getLastArg(OPT_pdb)) Config->PDBPath = Arg->getValue(); + if (auto *Arg = Args.getLastArg(OPT_pdbaltpath)) + Config->PDBAltPath = Arg->getValue(); + if (Args.hasArg(OPT_natvis)) + Config->NatvisFiles = Args.getAllArgValues(OPT_natvis); + + if (auto *Arg = Args.getLastArg(OPT_pdb_source_path)) + Config->PDBSourcePath = Arg->getValue(); + } // Handle /noentry if (Args.hasArg(OPT_noentry)) { @@ -860,6 +1013,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { DynamicBaseArg->getOption().getID() == OPT_dynamicbase_no) Config->DynamicBase = false; + // MSDN claims "/FIXED:NO is the default setting for a DLL, and /FIXED is the + // default setting for any other project type.", but link.exe defaults to + // /FIXED:NO for exe outputs as well. Match behavior, not docs. bool Fixed = Args.hasFlag(OPT_fixed, OPT_fixed_no, false); if (Fixed) { if (DynamicBaseArg && @@ -895,6 +1051,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { if (auto *Arg = Args.getLastArg(OPT_stack)) parseNumbers(Arg->getValue(), &Config->StackReserve, &Config->StackCommit); + // Handle /guard:cf + if (auto *Arg = Args.getLastArg(OPT_guard)) + parseGuard(Arg->getValue()); + // Handle /heap if (auto *Arg = Args.getLastArg(OPT_heap)) parseNumbers(Arg->getValue(), &Config->HeapReserve, &Config->HeapCommit); @@ -909,6 +1069,23 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { parseSubsystem(Arg->getValue(), &Config->Subsystem, &Config->MajorOSVersion, &Config->MinorOSVersion); + // Handle /timestamp + if (llvm::opt::Arg *Arg = Args.getLastArg(OPT_timestamp, OPT_repro)) { + if (Arg->getOption().getID() == OPT_repro) { + Config->Timestamp = 0; + Config->Repro = true; + } else { + Config->Repro = false; + StringRef Value(Arg->getValue()); + if (Value.getAsInteger(0, Config->Timestamp)) + fatal(Twine("invalid timestamp: ") + Value + + ". Expected 32-bit integer"); + } + } else { + Config->Repro = false; + Config->Timestamp = time(nullptr); + } + // Handle /alternatename for (auto *Arg : Args.filtered(OPT_alternatename)) parseAlternateName(Arg->getValue()); @@ -922,8 +1099,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { Config->Implib = Arg->getValue(); // Handle /opt. - bool DoGC = !Args.hasArg(OPT_debug); - unsigned ICFLevel = 1; // 0: off, 1: limited, 2: on + bool DoGC = !Args.hasArg(OPT_debug) || Args.hasArg(OPT_profile); + unsigned ICFLevel = + Args.hasArg(OPT_profile) ? 0 : 1; // 0: off, 1: limited, 2: on + unsigned TailMerge = 1; for (auto *Arg : Args.filtered(OPT_opt)) { std::string Str = StringRef(Arg->getValue()).lower(); SmallVector<StringRef, 1> Vec; @@ -937,14 +1116,18 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { ICFLevel = 2; } else if (S == "noicf") { ICFLevel = 0; + } else if (S == "lldtailmerge") { + TailMerge = 2; + } else if (S == "nolldtailmerge") { + TailMerge = 0; } else if (S.startswith("lldlto=")) { StringRef OptLevel = S.substr(7); - if (OptLevel.getAsInteger(10, Config->LTOOptLevel) || - Config->LTOOptLevel > 3) + if (OptLevel.getAsInteger(10, Config->LTOO) || Config->LTOO > 3) error("/opt:lldlto: invalid optimization level: " + OptLevel); } else if (S.startswith("lldltojobs=")) { StringRef Jobs = S.substr(11); - if (Jobs.getAsInteger(10, Config->LTOJobs) || Config->LTOJobs == 0) + if (Jobs.getAsInteger(10, Config->ThinLTOJobs) || + Config->ThinLTOJobs == 0) error("/opt:lldltojobs: invalid job count: " + Jobs); } else if (S.startswith("lldltopartitions=")) { StringRef N = S.substr(17); @@ -965,6 +1148,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { ICFLevel = 0; Config->DoGC = DoGC; Config->DoICF = ICFLevel > 0; + Config->TailMerge = (TailMerge == 1 && Config->DoICF) || TailMerge == 2; // Handle /lldsavetemps if (Args.hasArg(OPT_lldsavetemps)) @@ -992,6 +1176,14 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { for (auto *Arg : Args.filtered(OPT_merge)) parseMerge(Arg->getValue()); + // Add default section merging rules after user rules. User rules take + // precedence, but we will emit a warning if there is a conflict. + parseMerge(".idata=.rdata"); + parseMerge(".didat=.rdata"); + parseMerge(".edata=.rdata"); + parseMerge(".xdata=.rdata"); + parseMerge(".bss=.data"); + // Handle /section for (auto *Arg : Args.filtered(OPT_section)) parseSection(Arg->getValue()); @@ -1029,39 +1221,77 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { if (!Config->ManifestInput.empty() && Config->Manifest != Configuration::Embed) { - fatal("/MANIFESTINPUT: requires /MANIFEST:EMBED"); + fatal("/manifestinput: requires /manifest:embed"); } // Handle miscellaneous boolean flags. Config->AllowBind = Args.hasFlag(OPT_allowbind, OPT_allowbind_no, true); Config->AllowIsolation = Args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true); + Config->Incremental = + Args.hasFlag(OPT_incremental, OPT_incremental_no, + !Config->DoGC && !Config->DoICF && !Args.hasArg(OPT_order) && + !Args.hasArg(OPT_profile)); + Config->IntegrityCheck = + Args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false); Config->NxCompat = Args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true); - Config->TerminalServerAware = Args.hasFlag(OPT_tsaware, OPT_tsaware_no, true); + Config->TerminalServerAware = + !Config->DLL && Args.hasFlag(OPT_tsaware, OPT_tsaware_no, true); Config->DebugDwarf = Args.hasArg(OPT_debug_dwarf); Config->DebugGHashes = Args.hasArg(OPT_debug_ghash); + Config->DebugSymtab = Args.hasArg(OPT_debug_symtab); Config->MapFile = getMapFile(Args); + if (Config->Incremental && Args.hasArg(OPT_profile)) { + warn("ignoring '/incremental' due to '/profile' specification"); + Config->Incremental = false; + } + + if (Config->Incremental && Args.hasArg(OPT_order)) { + warn("ignoring '/incremental' due to '/order' specification"); + Config->Incremental = false; + } + + if (Config->Incremental && Config->DoGC) { + warn("ignoring '/incremental' because REF is enabled; use '/opt:noref' to " + "disable"); + Config->Incremental = false; + } + + if (Config->Incremental && Config->DoICF) { + warn("ignoring '/incremental' because ICF is enabled; use '/opt:noicf' to " + "disable"); + Config->Incremental = false; + } + if (errorCount()) return; - bool WholeArchiveFlag = Args.hasArg(OPT_wholearchive_flag); + std::set<sys::fs::UniqueID> WholeArchives; + for (auto *Arg : Args.filtered(OPT_wholearchive_file)) + if (Optional<StringRef> Path = doFindFile(Arg->getValue())) + if (Optional<sys::fs::UniqueID> ID = getUniqueID(*Path)) + WholeArchives.insert(*ID); + + // A predicate returning true if a given path is an argument for + // /wholearchive:, or /wholearchive is enabled globally. + // This function is a bit tricky because "foo.obj /wholearchive:././foo.obj" + // needs to be handled as "/wholearchive:foo.obj foo.obj". + auto IsWholeArchive = [&](StringRef Path) -> bool { + if (Args.hasArg(OPT_wholearchive_flag)) + return true; + if (Optional<sys::fs::UniqueID> ID = getUniqueID(Path)) + return WholeArchives.count(*ID); + return false; + }; + // Create a list of input files. Files can be given as arguments // for /defaultlib option. - std::vector<MemoryBufferRef> MBs; - for (auto *Arg : Args.filtered(OPT_INPUT, OPT_wholearchive_file)) { - switch (Arg->getOption().getID()) { - case OPT_INPUT: - if (Optional<StringRef> Path = findFile(Arg->getValue())) - enqueuePath(*Path, WholeArchiveFlag); - break; - case OPT_wholearchive_file: - if (Optional<StringRef> Path = findFile(Arg->getValue())) - enqueuePath(*Path, true); - break; - } - } + for (auto *Arg : Args.filtered(OPT_INPUT, OPT_wholearchive_file)) + if (Optional<StringRef> Path = findFile(Arg->getValue())) + enqueuePath(*Path, IsWholeArchive(*Path)); + for (auto *Arg : Args.filtered(OPT_defaultlib)) if (Optional<StringRef> Path = findLib(Arg->getValue())) enqueuePath(*Path, false); @@ -1165,10 +1395,24 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { getOutputPath((*Args.filtered(OPT_INPUT).begin())->getValue()); } - // Put the PDB next to the image if no /pdb flag was passed. - if (ShouldCreatePDB && Config->PDBPath.empty()) { - Config->PDBPath = Config->OutputFile; - sys::path::replace_extension(Config->PDBPath, ".pdb"); + if (ShouldCreatePDB) { + // Put the PDB next to the image if no /pdb flag was passed. + if (Config->PDBPath.empty()) { + Config->PDBPath = Config->OutputFile; + sys::path::replace_extension(Config->PDBPath, ".pdb"); + } + + // The embedded PDB path should be the absolute path to the PDB if no + // /pdbaltpath flag was passed. + if (Config->PDBAltPath.empty()) { + Config->PDBAltPath = Config->PDBPath; + + // It's important to make the path absolute and remove dots. This path + // will eventually be written into the PE header, and certain Microsoft + // tools won't work correctly if these assumptions are not held. + sys::fs::make_absolute(Config->PDBAltPath); + sys::path::remove_dots(Config->PDBAltPath); + } } // Set default image base if /base is not given. @@ -1181,11 +1425,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { Symtab->addAbsolute("___safe_se_handler_count", 0); } - // We do not support /guard:cf (control flow protection) yet. - // Define CFG symbols anyway so that we can link MSVC 2015 CRT. Symtab->addAbsolute(mangle("__guard_fids_count"), 0); Symtab->addAbsolute(mangle("__guard_fids_table"), 0); - Symtab->addAbsolute(mangle("__guard_flags"), 0x100); + Symtab->addAbsolute(mangle("__guard_flags"), 0); Symtab->addAbsolute(mangle("__guard_iat_count"), 0); Symtab->addAbsolute(mangle("__guard_iat_table"), 0); Symtab->addAbsolute(mangle("__guard_longjmp_count"), 0); @@ -1260,7 +1502,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // Handle /safeseh. if (Args.hasFlag(OPT_safeseh, OPT_safeseh_no, false)) { for (ObjFile *File : ObjFile::Instances) - if (!File->SEHCompat) + if (!File->hasSafeSEH()) error("/safeseh: " + File->getName() + " is not compatible with SEH"); if (errorCount()) return; @@ -1280,7 +1522,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { E.Name = Def->getName(); E.Sym = Def; if (Def->getChunk() && - !(Def->getChunk()->getPermissions() & IMAGE_SCN_MEM_EXECUTE)) + !(Def->getChunk()->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)) E.Data = true; Config->Exports.push_back(E); }); @@ -1323,6 +1565,12 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { if (Config->Manifest == Configuration::SideBySide) createSideBySideManifest(); + // Handle /order. We want to do this at this moment because we + // need a complete list of comdat sections to warn on nonexistent + // functions. + if (auto *Arg = Args.getLastArg(OPT_order)) + parseOrderFile(Arg->getValue()); + // Identify unreferenced COMDAT sections. if (Config->DoGC) markLive(Symtab->getChunks()); @@ -1333,6 +1581,11 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // Write the result. writeResult(); + + // Stop early so we can print the results. + Timer::root().stop(); + if (Config->ShowTiming) + Timer::root().print(); } } // namespace coff diff --git a/contrib/llvm/tools/lld/COFF/Driver.h b/contrib/llvm/tools/lld/COFF/Driver.h index 3f7fad1038f3..627e991a9028 100644 --- a/contrib/llvm/tools/lld/COFF/Driver.h +++ b/contrib/llvm/tools/lld/COFF/Driver.h @@ -21,6 +21,7 @@ #include "llvm/Object/COFF.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/TarWriter.h" #include <memory> #include <set> @@ -36,12 +37,6 @@ using llvm::COFF::MachineTypes; using llvm::COFF::WindowsSubsystem; using llvm::Optional; -// Implemented in MarkLive.cpp. -void markLive(ArrayRef<Chunk *> Chunks); - -// Implemented in ICF.cpp. -void doICF(ArrayRef<Chunk *> Chunks); - class COFFOptTable : public llvm::opt::OptTable { public: COFFOptTable(); @@ -56,8 +51,10 @@ public: llvm::opt::InputArgList parse(StringRef S) { return parse(tokenize(S)); } // Tokenizes a given string and then parses as command line options in - // .drectve section. - llvm::opt::InputArgList parseDirectives(StringRef S); + // .drectve section. /EXPORT options are returned in second element + // to be processed in fastpath. + std::pair<llvm::opt::InputArgList, std::vector<StringRef>> + parseDirectives(StringRef S); private: // Parses command line options. @@ -98,7 +95,11 @@ private: // Library search path. The first element is always "" (current directory). std::vector<StringRef> SearchPaths; - std::set<std::string> VisitedFiles; + + // We don't want to add the same file more than once. + // Files are uniquified by their filesystem and file number. + std::set<llvm::sys::fs::UniqueID> VisitedFiles; + std::set<std::string> VisitedLibs; Symbol *addUndefined(StringRef Sym); @@ -143,6 +144,8 @@ StringRef machineToStr(MachineTypes MT); // Parses a string in the form of "<integer>[,<integer>]". void parseNumbers(StringRef Arg, uint64_t *Addr, uint64_t *Size = nullptr); +void parseGuard(StringRef Arg); + // Parses a string in the form of "<integer>[.<integer>]". // Minor's default value is 0. void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor); diff --git a/contrib/llvm/tools/lld/COFF/DriverUtils.cpp b/contrib/llvm/tools/lld/COFF/DriverUtils.cpp index 4b3b6d5e09d6..c12e791f9507 100644 --- a/contrib/llvm/tools/lld/COFF/DriverUtils.cpp +++ b/contrib/llvm/tools/lld/COFF/DriverUtils.cpp @@ -61,12 +61,7 @@ public: StringRef Exe = Saver.save(*ExeOrErr); Args.insert(Args.begin(), Exe); - std::vector<const char *> Vec; - for (StringRef S : Args) - Vec.push_back(S.data()); - Vec.push_back(nullptr); - - if (sys::ExecuteAndWait(Args[0], Vec.data()) != 0) + if (sys::ExecuteAndWait(Args[0], Args) != 0) fatal("ExecuteAndWait failed: " + llvm::join(Args.begin(), Args.end(), " ")); } @@ -128,6 +123,21 @@ void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor) { fatal("invalid number: " + S2); } +void parseGuard(StringRef FullArg) { + SmallVector<StringRef, 1> SplitArgs; + FullArg.split(SplitArgs, ","); + for (StringRef Arg : SplitArgs) { + if (Arg.equals_lower("no")) + Config->GuardCF = GuardCFLevel::Off; + else if (Arg.equals_lower("nolongjmp")) + Config->GuardCF = GuardCFLevel::NoLongJmp; + else if (Arg.equals_lower("cf") || Arg.equals_lower("longjmp")) + Config->GuardCF = GuardCFLevel::Full; + else + fatal("invalid argument to /guard: " + Arg); + } +} + // Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]". void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major, uint32_t *Minor) { @@ -170,6 +180,10 @@ void parseMerge(StringRef S) { std::tie(From, To) = S.split('='); if (From.empty() || To.empty()) fatal("/merge: invalid argument: " + S); + if (From == ".rsrc" || To == ".rsrc") + fatal("/merge: cannot merge '.rsrc' with any section"); + if (From == ".reloc" || To == ".reloc") + fatal("/merge: cannot merge '.reloc' with any section"); auto Pair = Config->Merge.insert(std::make_pair(From, To)); bool Inserted = Pair.second; if (!Inserted) { @@ -418,15 +432,15 @@ static std::string createManifestXml() { return createManifestXmlWithExternalMt(DefaultXml); } -static std::unique_ptr<MemoryBuffer> +static std::unique_ptr<WritableMemoryBuffer> createMemoryBufferForManifestRes(size_t ManifestSize) { size_t ResSize = alignTo( object::WIN_RES_MAGIC_SIZE + object::WIN_RES_NULL_ENTRY_SIZE + sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) + sizeof(object::WinResHeaderSuffix) + ManifestSize, object::WIN_RES_DATA_ALIGNMENT); - return MemoryBuffer::getNewMemBuffer(ResSize, - Config->OutputFile + ".manifest.res"); + return WritableMemoryBuffer::getNewMemBuffer(ResSize, Config->OutputFile + + ".manifest.res"); } static void writeResFileHeader(char *&Buf) { @@ -465,16 +479,16 @@ static void writeResEntryHeader(char *&Buf, size_t ManifestSize) { std::unique_ptr<MemoryBuffer> createManifestRes() { std::string Manifest = createManifestXml(); - std::unique_ptr<MemoryBuffer> Res = + std::unique_ptr<WritableMemoryBuffer> Res = createMemoryBufferForManifestRes(Manifest.size()); - char *Buf = const_cast<char *>(Res->getBufferStart()); + char *Buf = Res->getBufferStart(); writeResFileHeader(Buf); writeResEntryHeader(Buf, Manifest.size()); // Copy the manifest data into the .res file. std::copy(Manifest.begin(), Manifest.end(), Buf); - return Res; + return std::move(Res); } void createSideBySideManifest() { @@ -558,6 +572,12 @@ err: static StringRef undecorate(StringRef Sym) { if (Config->Machine != I386) return Sym; + // In MSVC mode, a fully decorated stdcall function is exported + // as-is with the leading underscore (with type IMPORT_NAME). + // In MinGW mode, a decorated stdcall function gets the underscore + // removed, just like normal cdecl functions. + if (Sym.startswith("_") && Sym.contains('@') && !Config->MinGW) + return Sym; return Sym.startswith("_") ? Sym.substr(1) : Sym; } @@ -731,6 +751,28 @@ static const llvm::opt::OptTable::Info InfoTable[] = { COFFOptTable::COFFOptTable() : OptTable(InfoTable, true) {} +// Set color diagnostics according to --color-diagnostics={auto,always,never} +// or --no-color-diagnostics flags. +static void handleColorDiagnostics(opt::InputArgList &Args) { + auto *Arg = Args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq, + OPT_no_color_diagnostics); + if (!Arg) + return; + if (Arg->getOption().getID() == OPT_color_diagnostics) { + errorHandler().ColorDiagnostics = true; + } else if (Arg->getOption().getID() == OPT_no_color_diagnostics) { + errorHandler().ColorDiagnostics = false; + } else { + StringRef S = Arg->getValue(); + if (S == "always") + errorHandler().ColorDiagnostics = true; + else if (S == "never") + errorHandler().ColorDiagnostics = false; + else if (S != "auto") + error("unknown option: --color-diagnostics=" + S); + } +} + static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) { if (auto *Arg = Args.getLastArg(OPT_rsp_quoting)) { StringRef S = Arg->getValue(); @@ -749,50 +791,73 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> Argv) { // Make InputArgList from string vectors. unsigned MissingIndex; unsigned MissingCount; - SmallVector<const char *, 256> Vec(Argv.data(), Argv.data() + Argv.size()); // We need to get the quoting style for response files before parsing all // options so we parse here before and ignore all the options but // --rsp-quoting. - opt::InputArgList Args = Table.ParseArgs(Vec, MissingIndex, MissingCount); + opt::InputArgList Args = Table.ParseArgs(Argv, MissingIndex, MissingCount); // Expand response files (arguments in the form of @<filename>) // and then parse the argument again. - cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), Vec); - Args = Table.ParseArgs(Vec, MissingIndex, MissingCount); + SmallVector<const char *, 256> ExpandedArgv(Argv.data(), Argv.data() + Argv.size()); + cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), ExpandedArgv); + Args = Table.ParseArgs(makeArrayRef(ExpandedArgv).drop_front(), MissingIndex, + MissingCount); // Print the real command line if response files are expanded. - if (Args.hasArg(OPT_verbose) && Argv.size() != Vec.size()) { + if (Args.hasArg(OPT_verbose) && Argv.size() != ExpandedArgv.size()) { std::string Msg = "Command line:"; - for (const char *S : Vec) + for (const char *S : ExpandedArgv) Msg += " " + std::string(S); message(Msg); } + // Save the command line after response file expansion so we can write it to + // the PDB if necessary. + Config->Argv = {ExpandedArgv.begin(), ExpandedArgv.end()}; + // Handle /WX early since it converts missing argument warnings to errors. errorHandler().FatalWarnings = Args.hasFlag(OPT_WX, OPT_WX_no, false); if (MissingCount) fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument"); + + handleColorDiagnostics(Args); + for (auto *Arg : Args.filtered(OPT_UNKNOWN)) warn("ignoring unknown argument: " + Arg->getSpelling()); + + if (Args.hasArg(OPT_lib)) + warn("ignoring /lib since it's not the first argument"); + return Args; } // Tokenizes and parses a given string as command line in .drective section. -opt::InputArgList ArgParser::parseDirectives(StringRef S) { - // Make InputArgList from string vectors. +// /EXPORT options are processed in fastpath. +std::pair<opt::InputArgList, std::vector<StringRef>> +ArgParser::parseDirectives(StringRef S) { + std::vector<StringRef> Exports; + SmallVector<const char *, 16> Rest; + + for (StringRef Tok : tokenize(S)) { + if (Tok.startswith_lower("/export:") || Tok.startswith_lower("-export:")) + Exports.push_back(Tok.substr(strlen("/export:"))); + else + Rest.push_back(Tok.data()); + } + + // Make InputArgList from unparsed string vectors. unsigned MissingIndex; unsigned MissingCount; - opt::InputArgList Args = - Table.ParseArgs(tokenize(S), MissingIndex, MissingCount); + opt::InputArgList Args = Table.ParseArgs(Rest, MissingIndex, MissingCount); if (MissingCount) fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument"); for (auto *Arg : Args.filtered(OPT_UNKNOWN)) warn("ignoring unknown argument: " + Arg->getSpelling()); - return Args; + return {std::move(Args), std::move(Exports)}; } // link.exe has an interesting feature. If LINK or _LINK_ environment @@ -802,11 +867,11 @@ opt::InputArgList ArgParser::parseLINK(std::vector<const char *> Argv) { // Concatenate LINK env and command line arguments, and then parse them. if (Optional<std::string> S = Process::GetEnv("LINK")) { std::vector<const char *> V = tokenize(*S); - Argv.insert(Argv.begin(), V.begin(), V.end()); + Argv.insert(std::next(Argv.begin()), V.begin(), V.end()); } if (Optional<std::string> S = Process::GetEnv("_LINK_")) { std::vector<const char *> V = tokenize(*S); - Argv.insert(Argv.begin(), V.begin(), V.end()); + Argv.insert(std::next(Argv.begin()), V.begin(), V.end()); } return parse(Argv); } diff --git a/contrib/llvm/tools/lld/COFF/ICF.cpp b/contrib/llvm/tools/lld/COFF/ICF.cpp index 48895c34886c..7feb3c4e0b0c 100644 --- a/contrib/llvm/tools/lld/COFF/ICF.cpp +++ b/contrib/llvm/tools/lld/COFF/ICF.cpp @@ -18,13 +18,16 @@ // //===----------------------------------------------------------------------===// +#include "ICF.h" #include "Chunks.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" +#include "lld/Common/Timer.h" #include "llvm/ADT/Hashing.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Parallel.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/xxhash.h" #include <algorithm> #include <atomic> #include <vector> @@ -34,6 +37,8 @@ using namespace llvm; namespace lld { namespace coff { +static Timer ICFTimer("ICF", Timer::root()); + class ICF { public: void run(ArrayRef<Chunk *> V); @@ -41,6 +46,8 @@ public: private: void segregate(size_t Begin, size_t End, bool Constant); + bool assocEquals(const SectionChunk *A, const SectionChunk *B); + bool equalsConstant(const SectionChunk *A, const SectionChunk *B); bool equalsVariable(const SectionChunk *A, const SectionChunk *B); @@ -59,13 +66,6 @@ private: std::atomic<bool> Repeat = {false}; }; -// Returns a hash value for S. -uint32_t ICF::getHash(SectionChunk *C) { - return hash_combine(C->getPermissions(), C->SectionName, C->NumRelocs, - C->Alignment, uint32_t(C->Header->SizeOfRawData), - C->Checksum, C->getContents()); -} - // Returns true if section S is subject of ICF. // // Microsoft's documentation @@ -73,21 +73,27 @@ uint32_t ICF::getHash(SectionChunk *C) { // 2017) says that /opt:icf folds both functions and read-only data. // Despite that, the MSVC linker folds only functions. We found // a few instances of programs that are not safe for data merging. -// Therefore, we merge only functions just like the MSVC tool. However, we merge -// identical .xdata sections, because the address of unwind information is -// insignificant to the user program and the Visual C++ linker does this. +// Therefore, we merge only functions just like the MSVC tool. However, we also +// merge read-only sections in a couple of cases where the address of the +// section is insignificant to the user program and the behaviour matches that +// of the Visual C++ linker. bool ICF::isEligible(SectionChunk *C) { // Non-comdat chunks, dead chunks, and writable chunks are not elegible. - bool Writable = C->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_WRITE; + bool Writable = C->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE; if (!C->isCOMDAT() || !C->isLive() || Writable) return false; // Code sections are eligible. - if (C->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE) + if (C->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE) return true; - // .xdata unwind info sections are eligble. - return C->getSectionName().split('$').first == ".xdata"; + // .pdata and .xdata unwind info sections are eligible. + StringRef OutSecName = C->getSectionName().split('$').first; + if (OutSecName == ".pdata" || OutSecName == ".xdata") + return true; + + // So are vtables. + return C->Sym && C->Sym->getName().startswith("??_7"); } // Split an equivalence class into smaller classes. @@ -116,10 +122,23 @@ void ICF::segregate(size_t Begin, size_t End, bool Constant) { } } +// Returns true if two sections' associative children are equal. +bool ICF::assocEquals(const SectionChunk *A, const SectionChunk *B) { + auto ChildClasses = [&](const SectionChunk *SC) { + std::vector<uint32_t> Classes; + for (const SectionChunk *C : SC->children()) + if (!C->SectionName.startswith(".debug") && + C->SectionName != ".gfids$y" && C->SectionName != ".gljmp$y") + Classes.push_back(C->Class[Cnt % 2]); + return Classes; + }; + return ChildClasses(A) == ChildClasses(B); +} + // Compare "non-moving" part of two sections, namely everything // except relocation targets. bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) { - if (A->NumRelocs != B->NumRelocs) + if (A->Relocs.size() != B->Relocs.size()) return false; // Compare relocations. @@ -142,10 +161,11 @@ bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) { return false; // Compare section attributes and contents. - return A->getPermissions() == B->getPermissions() && - A->SectionName == B->SectionName && A->Alignment == B->Alignment && + return A->getOutputCharacteristics() == B->getOutputCharacteristics() && + A->SectionName == B->SectionName && A->Header->SizeOfRawData == B->Header->SizeOfRawData && - A->Checksum == B->Checksum && A->getContents() == B->getContents(); + A->Checksum == B->Checksum && A->getContents() == B->getContents() && + assocEquals(A, B); } // Compare "moving" part of two sections, namely relocation targets. @@ -161,9 +181,12 @@ bool ICF::equalsVariable(const SectionChunk *A, const SectionChunk *B) { return D1->getChunk()->Class[Cnt % 2] == D2->getChunk()->Class[Cnt % 2]; return false; }; - return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq); + return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), + Eq) && + assocEquals(A, B); } +// Find the first Chunk after Begin that has a different class from Begin. size_t ICF::findBoundary(size_t Begin, size_t End) { for (size_t I = Begin + 1; I < End; ++I) if (Chunks[Begin]->Class[Cnt % 2] != Chunks[I]->Class[Cnt % 2]) @@ -173,11 +196,8 @@ size_t ICF::findBoundary(size_t Begin, size_t End) { void ICF::forEachClassRange(size_t Begin, size_t End, std::function<void(size_t, size_t)> Fn) { - if (Begin > 0) - Begin = findBoundary(Begin - 1, End); - while (Begin < End) { - size_t Mid = findBoundary(Begin, Chunks.size()); + size_t Mid = findBoundary(Begin, End); Fn(Begin, Mid); Begin = Mid; } @@ -193,12 +213,22 @@ void ICF::forEachClass(std::function<void(size_t, size_t)> Fn) { return; } - // Split sections into 256 shards and call Fn in parallel. - size_t NumShards = 256; + // Shard into non-overlapping intervals, and call Fn in parallel. + // The sharding must be completed before any calls to Fn are made + // so that Fn can modify the Chunks in its shard without causing data + // races. + const size_t NumShards = 256; size_t Step = Chunks.size() / NumShards; - for_each_n(parallel::par, size_t(0), NumShards, [&](size_t I) { - size_t End = (I == NumShards - 1) ? Chunks.size() : (I + 1) * Step; - forEachClassRange(I * Step, End, Fn); + size_t Boundaries[NumShards + 1]; + Boundaries[0] = 0; + Boundaries[NumShards] = Chunks.size(); + for_each_n(parallel::par, size_t(1), NumShards, [&](size_t I) { + Boundaries[I] = findBoundary((I - 1) * Step, Chunks.size()); + }); + for_each_n(parallel::par, size_t(1), NumShards + 1, [&](size_t I) { + if (Boundaries[I - 1] < Boundaries[I]) { + forEachClassRange(Boundaries[I - 1], Boundaries[I], Fn); + } }); ++Cnt; } @@ -207,6 +237,8 @@ void ICF::forEachClass(std::function<void(size_t, size_t)> Fn) { // Two sections are considered the same if their section headers, // contents and relocations are all the same. void ICF::run(ArrayRef<Chunk *> Vec) { + ScopedTimer T(ICFTimer); + // Collect only mergeable sections and group by hash value. uint32_t NextId = 1; for (Chunk *C : Vec) { @@ -218,10 +250,16 @@ void ICF::run(ArrayRef<Chunk *> Vec) { } } + // Make sure that ICF doesn't merge sections that are being handled by string + // tail merging. + for (auto &P : MergeChunk::Instances) + for (SectionChunk *SC : P.second->Sections) + SC->Class[0] = NextId++; + // Initially, we use hash values to partition sections. for_each(parallel::par, Chunks.begin(), Chunks.end(), [&](SectionChunk *SC) { // Set MSB to 1 to avoid collisions with non-hash classs. - SC->Class[0] = getHash(SC) | (1 << 31); + SC->Class[0] = xxHash64(SC->getContents()) | (1 << 31); }); // From now on, sections in Chunks are ordered so that sections in diff --git a/contrib/llvm/tools/lld/COFF/Strings.h b/contrib/llvm/tools/lld/COFF/ICF.h index 67fc1c773c66..9c54e0c9ec2d 100644 --- a/contrib/llvm/tools/lld/COFF/Strings.h +++ b/contrib/llvm/tools/lld/COFF/ICF.h @@ -1,4 +1,4 @@ -//===- Strings.h ------------------------------------------------*- C++ -*-===// +//===- ICF.h --------------------------------------------------------------===// // // The LLVM Linker // @@ -7,17 +7,20 @@ // //===----------------------------------------------------------------------===// -#ifndef LLD_COFF_STRINGS_H -#define LLD_COFF_STRINGS_H +#ifndef LLD_COFF_ICF_H +#define LLD_COFF_ICF_H -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/StringRef.h" -#include <string> +#include "lld/Common/LLVM.h" +#include "llvm/ADT/ArrayRef.h" namespace lld { namespace coff { -llvm::Optional<std::string> demangleMSVC(llvm::StringRef S); -} -} + +class Chunk; + +void doICF(ArrayRef<Chunk *> Chunks); + +} // namespace coff +} // namespace lld #endif diff --git a/contrib/llvm/tools/lld/COFF/InputFiles.cpp b/contrib/llvm/tools/lld/COFF/InputFiles.cpp index a8f52e0391f7..2b3e65fae04b 100644 --- a/contrib/llvm/tools/lld/COFF/InputFiles.cpp +++ b/contrib/llvm/tools/lld/COFF/InputFiles.cpp @@ -27,6 +27,7 @@ #include "llvm/Support/Error.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" #include "llvm/Target/TargetOptions.h" #include <cstring> #include <system_error> @@ -138,12 +139,13 @@ void ObjFile::initializeChunks() { if (Sec->Characteristics & IMAGE_SCN_LNK_COMDAT) SparseChunks[I] = PendingComdat; else - SparseChunks[I] = readSection(I, nullptr); + SparseChunks[I] = readSection(I, nullptr, ""); } } SectionChunk *ObjFile::readSection(uint32_t SectionNumber, - const coff_aux_section_definition *Def) { + const coff_aux_section_definition *Def, + StringRef LeaderName) { const coff_section *Sec; StringRef Name; if (auto EC = COFFObj->getSection(SectionNumber, Sec)) @@ -151,15 +153,7 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber, if (auto EC = COFFObj->getSectionName(Sec, Name)) fatal("getSectionName failed: #" + Twine(SectionNumber) + ": " + EC.message()); - if (Name == ".sxdata") { - ArrayRef<uint8_t> Data; - COFFObj->getSectionContents(Sec, Data); - if (Data.size() % 4 != 0) - fatal(".sxdata must be an array of symbol table indices"); - SXData = {reinterpret_cast<const ulittle32_t *>(Data.data()), - Data.size() / 4}; - return nullptr; - } + if (Name == ".drectve") { ArrayRef<uint8_t> Data; COFFObj->getSectionContents(Sec, Data); @@ -177,8 +171,8 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber, // CodeView needs a linker support. We need to interpret and debug // info, and then write it to a separate .pdb file. - // Ignore debug info unless /debug is given. - if (!Config->Debug && Name.startswith(".debug")) + // Ignore DWARF debug info unless /debug is given. + if (!Config->Debug && Name.startswith(".debug_")) return nullptr; if (Sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE) @@ -191,6 +185,18 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber, // linked in the regular manner. if (C->isCodeView()) DebugChunks.push_back(C); + else if (Config->GuardCF != GuardCFLevel::Off && Name == ".gfids$y") + GuardFidChunks.push_back(C); + else if (Config->GuardCF != GuardCFLevel::Off && Name == ".gljmp$y") + GuardLJmpChunks.push_back(C); + else if (Name == ".sxdata") + SXDataChunks.push_back(C); + else if (Config->TailMerge && Sec->NumberOfRelocations == 0 && + Name == ".rdata" && LeaderName.startswith("??_C@")) + // COFF sections that look like string literal sections (i.e. no + // relocations, in .rdata, leader symbol name matches the MSVC name mangling + // for string literals) are subject to string tail merging. + MergeChunk::addSection(C); else Chunks.push_back(C); @@ -211,7 +217,7 @@ void ObjFile::readAssociativeDefinition( // the section; otherwise mark it as discarded. int32_t SectionNumber = Sym.getSectionNumber(); if (Parent) { - SparseChunks[SectionNumber] = readSection(SectionNumber, Def); + SparseChunks[SectionNumber] = readSection(SectionNumber, Def, ""); if (SparseChunks[SectionNumber]) Parent->addAssociative(SparseChunks[SectionNumber]); } else { @@ -275,6 +281,13 @@ void ObjFile::initializeSymbols() { if (auto *Def = Sym.getSectionDefinition()) if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) readAssociativeDefinition(Sym, Def); + if (SparseChunks[Sym.getSectionNumber()] == PendingComdat) { + StringRef Name; + COFFObj->getSymbolName(Sym, Name); + log("comdat section " + Name + + " without leader and unassociated, discarding"); + continue; + } Symbols[I] = createRegular(Sym); } @@ -294,43 +307,46 @@ Symbol *ObjFile::createUndefined(COFFSymbolRef Sym) { Optional<Symbol *> ObjFile::createDefined( COFFSymbolRef Sym, std::vector<const coff_aux_section_definition *> &ComdatDefs) { - StringRef Name; + auto GetName = [&]() { + StringRef S; + COFFObj->getSymbolName(Sym, S); + return S; + }; + if (Sym.isCommon()) { auto *C = make<CommonChunk>(Sym); Chunks.push_back(C); - COFFObj->getSymbolName(Sym, Name); - Symbol *S = - Symtab->addCommon(this, Name, Sym.getValue(), Sym.getGeneric(), C); - return S; + return Symtab->addCommon(this, GetName(), Sym.getValue(), Sym.getGeneric(), + C); } + if (Sym.isAbsolute()) { - COFFObj->getSymbolName(Sym, Name); + StringRef Name = GetName(); + // Skip special symbols. if (Name == "@comp.id") return nullptr; - // COFF spec 5.10.1. The .sxdata section. if (Name == "@feat.00") { - if (Sym.getValue() & 1) - SEHCompat = true; + Feat00Flags = Sym.getValue(); return nullptr; } + if (Sym.isExternal()) return Symtab->addAbsolute(Name, Sym); - else - return make<DefinedAbsolute>(Name, Sym); + return make<DefinedAbsolute>(Name, Sym); } + int32_t SectionNumber = Sym.getSectionNumber(); if (SectionNumber == llvm::COFF::IMAGE_SYM_DEBUG) return nullptr; - // Reserved sections numbers don't have contents. if (llvm::COFF::isReservedSectionNumber(SectionNumber)) - fatal("broken object file: " + toString(this)); + fatal(toString(this) + ": " + GetName() + + " should not refer to special section " + Twine(SectionNumber)); - // This symbol references a section which is not present in the section - // header. if ((uint32_t)SectionNumber >= SparseChunks.size()) - fatal("broken object file: " + toString(this)); + fatal(toString(this) + ": " + GetName() + + " should not refer to non-existent section " + Twine(SectionNumber)); // Handle comdat leader symbols. if (const coff_aux_section_definition *Def = ComdatDefs[SectionNumber]) { @@ -338,16 +354,16 @@ Optional<Symbol *> ObjFile::createDefined( Symbol *Leader; bool Prevailing; if (Sym.isExternal()) { - COFFObj->getSymbolName(Sym, Name); std::tie(Leader, Prevailing) = - Symtab->addComdat(this, Name, Sym.getGeneric()); + Symtab->addComdat(this, GetName(), Sym.getGeneric()); } else { Leader = make<DefinedRegular>(this, /*Name*/ "", false, /*IsExternal*/ false, Sym.getGeneric()); Prevailing = true; } + if (Prevailing) { - SectionChunk *C = readSection(SectionNumber, Def); + SectionChunk *C = readSection(SectionNumber, Def, GetName()); SparseChunks[SectionNumber] = C; C->Sym = cast<DefinedRegular>(Leader); cast<DefinedRegular>(Leader)->Data = &C->Repl; @@ -429,7 +445,8 @@ void ImportFile::parse() { // address pointed by the __imp_ symbol. (This allows you to call // DLL functions just like regular non-DLL functions.) if (Hdr->getType() == llvm::COFF::IMPORT_CODE) - ThunkSym = Symtab->addImportThunk(Name, ImpSym, Hdr->Machine); + ThunkSym = Symtab->addImportThunk( + Name, cast_or_null<DefinedImportData>(ImpSym), Hdr->Machine); } void BitcodeFile::parse() { @@ -462,7 +479,7 @@ void BitcodeFile::parse() { } else { Sym = Symtab->addRegular(this, SymName); } - SymbolBodies.push_back(Sym); + Symbols.push_back(Sym); } Directives = Obj->getCOFFLinkerOpts(); } @@ -486,10 +503,7 @@ MachineTypes BitcodeFile::getMachineType() { // Returns the last element of a path, which is supposed to be a filename. static StringRef getBasename(StringRef Path) { - size_t Pos = Path.find_last_of("\\/"); - if (Pos == StringRef::npos) - return Path; - return Path.substr(Pos + 1); + return sys::path::filename(Path, sys::path::Style::windows); } // Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)". diff --git a/contrib/llvm/tools/lld/COFF/InputFiles.h b/contrib/llvm/tools/lld/COFF/InputFiles.h index adedbc2ad7a8..4ee4b363886f 100644 --- a/contrib/llvm/tools/lld/COFF/InputFiles.h +++ b/contrib/llvm/tools/lld/COFF/InputFiles.h @@ -110,6 +110,9 @@ public: MachineTypes getMachineType() override; ArrayRef<Chunk *> getChunks() { return Chunks; } ArrayRef<SectionChunk *> getDebugChunks() { return DebugChunks; } + ArrayRef<SectionChunk *> getSXDataChunks() { return SXDataChunks; } + ArrayRef<SectionChunk *> getGuardFidChunks() { return GuardFidChunks; } + ArrayRef<SectionChunk *> getGuardLJmpChunks() { return GuardLJmpChunks; } ArrayRef<Symbol *> getSymbols() { return Symbols; } // Returns a Symbol object for the SymbolIndex'th symbol in the @@ -123,13 +126,17 @@ public: static std::vector<ObjFile *> Instances; - // True if this object file is compatible with SEH. - // COFF-specific and x86-only. - bool SEHCompat = false; + // Flags in the absolute @feat.00 symbol if it is present. These usually + // indicate if an object was compiled with certain security features enabled + // like stack guard, safeseh, /guard:cf, or other things. + uint32_t Feat00Flags = 0; - // The symbol table indexes of the safe exception handlers. - // COFF-specific and x86-only. - ArrayRef<llvm::support::ulittle32_t> SXData; + // True if this object file is compatible with SEH. COFF-specific and + // x86-only. COFF spec 5.10.1. The .sxdata section. + bool hasSafeSEH() { return Feat00Flags & 0x1; } + + // True if this file was compiled with /guard:cf. + bool hasGuardCF() { return Feat00Flags & 0x800; } // Pointer to the PDB module descriptor builder. Various debug info records // will reference object files by "module index", which is here. Things like @@ -143,7 +150,8 @@ private: SectionChunk * readSection(uint32_t SectionNumber, - const llvm::object::coff_aux_section_definition *Def); + const llvm::object::coff_aux_section_definition *Def, + StringRef LeaderName); void readAssociativeDefinition( COFFSymbolRef COFFSym, @@ -165,6 +173,15 @@ private: // CodeView debug info sections. std::vector<SectionChunk *> DebugChunks; + // Chunks containing symbol table indices of exception handlers. Only used for + // 32-bit x86. + std::vector<SectionChunk *> SXDataChunks; + + // Chunks containing symbol table indices of address taken symbols and longjmp + // targets. These are not linked into the final binary when /guard:cf is set. + std::vector<SectionChunk *> GuardFidChunks; + std::vector<SectionChunk *> GuardLJmpChunks; + // This vector contains the same chunks as Chunks, but they are // indexed such that you can get a SectionChunk by section index. // Nonexistent section indices are filled with null pointers. @@ -184,15 +201,14 @@ private: // for details about the format. class ImportFile : public InputFile { public: - explicit ImportFile(MemoryBufferRef M) - : InputFile(ImportKind, M), Live(!Config->DoGC) {} + explicit ImportFile(MemoryBufferRef M) : InputFile(ImportKind, M) {} static bool classof(const InputFile *F) { return F->kind() == ImportKind; } static std::vector<ImportFile *> Instances; - DefinedImportData *ImpSym = nullptr; - DefinedImportThunk *ThunkSym = nullptr; + Symbol *ImpSym = nullptr; + Symbol *ThunkSym = nullptr; std::string DLLName; private: @@ -204,12 +220,15 @@ public: Chunk *Location = nullptr; // We want to eliminate dllimported symbols if no one actually refers them. - // This "Live" bit is used to keep track of which import library members + // These "Live" bits are used to keep track of which import library members // are actually in use. // // If the Live bit is turned off by MarkLive, Writer will ignore dllimported - // symbols provided by this import library member. - bool Live; + // symbols provided by this import library member. We also track whether the + // imported symbol is used separately from whether the thunk is used in order + // to avoid creating unnecessary thunks. + bool Live = !Config->DoGC; + bool ThunkLive = !Config->DoGC; }; // Used for LTO. @@ -217,7 +236,7 @@ class BitcodeFile : public InputFile { public: explicit BitcodeFile(MemoryBufferRef M) : InputFile(BitcodeKind, M) {} static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; } - ArrayRef<Symbol *> getSymbols() { return SymbolBodies; } + ArrayRef<Symbol *> getSymbols() { return Symbols; } MachineTypes getMachineType() override; static std::vector<BitcodeFile *> Instances; std::unique_ptr<llvm::lto::InputFile> Obj; @@ -225,7 +244,7 @@ public: private: void parse() override; - std::vector<Symbol *> SymbolBodies; + std::vector<Symbol *> Symbols; }; } // namespace coff diff --git a/contrib/llvm/tools/lld/COFF/LTO.cpp b/contrib/llvm/tools/lld/COFF/LTO.cpp index fa2a54b61841..93f7ba3f9e4c 100644 --- a/contrib/llvm/tools/lld/COFF/LTO.cpp +++ b/contrib/llvm/tools/lld/COFF/LTO.cpp @@ -12,6 +12,7 @@ #include "InputFiles.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" +#include "lld/Common/Strings.h" #include "lld/Common/TargetOptionsCommandFlags.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" @@ -40,47 +41,32 @@ using namespace llvm::object; using namespace lld; using namespace lld::coff; -static void diagnosticHandler(const DiagnosticInfo &DI) { - SmallString<128> ErrStorage; - raw_svector_ostream OS(ErrStorage); - DiagnosticPrinterRawOStream DP(OS); - DI.print(DP); - warn(ErrStorage); -} - -static void checkError(Error E) { - handleAllErrors(std::move(E), - [&](ErrorInfoBase &EIB) { error(EIB.message()); }); -} +static std::unique_ptr<lto::LTO> createLTO() { + lto::Config C; + C.Options = InitTargetOptionsFromCodeGenFlags(); -static void saveBuffer(StringRef Buffer, const Twine &Path) { - std::error_code EC; - raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None); - if (EC) - error("cannot create " + Path + ": " + EC.message()); - OS << Buffer; -} + // Always emit a section per function/datum with LTO. LLVM LTO should get most + // of the benefit of linker GC, but there are still opportunities for ICF. + C.Options.FunctionSections = true; + C.Options.DataSections = true; -static std::unique_ptr<lto::LTO> createLTO() { - lto::Config Conf; - Conf.Options = InitTargetOptionsFromCodeGenFlags(); // Use static reloc model on 32-bit x86 because it usually results in more // compact code, and because there are also known code generation bugs when // using the PIC model (see PR34306). if (Config->Machine == COFF::IMAGE_FILE_MACHINE_I386) - Conf.RelocModel = Reloc::Static; + C.RelocModel = Reloc::Static; else - Conf.RelocModel = Reloc::PIC_; - Conf.DisableVerify = true; - Conf.DiagHandler = diagnosticHandler; - Conf.OptLevel = Config->LTOOptLevel; + C.RelocModel = Reloc::PIC_; + C.DisableVerify = true; + C.DiagHandler = diagnosticHandler; + C.OptLevel = Config->LTOO; if (Config->SaveTemps) - checkError(Conf.addSaveTemps(std::string(Config->OutputFile) + ".", - /*UseInputModulePath*/ true)); + checkError(C.addSaveTemps(std::string(Config->OutputFile) + ".", + /*UseInputModulePath*/ true)); lto::ThinBackend Backend; - if (Config->LTOJobs != 0) - Backend = lto::createInProcessThinBackend(Config->LTOJobs); - return llvm::make_unique<lto::LTO>(std::move(Conf), Backend, + if (Config->ThinLTOJobs != 0) + Backend = lto::createInProcessThinBackend(Config->ThinLTOJobs); + return llvm::make_unique<lto::LTO>(std::move(C), Backend, Config->LTOPartitions); } @@ -119,7 +105,7 @@ void BitcodeCompiler::add(BitcodeFile &F) { // and return the resulting objects. std::vector<StringRef> BitcodeCompiler::compile() { unsigned MaxTasks = LTOObj->getMaxTasks(); - Buff.resize(MaxTasks); + Buf.resize(MaxTasks); Files.resize(MaxTasks); // The /lldltocache option specifies the path to a directory in which to cache @@ -127,15 +113,15 @@ std::vector<StringRef> BitcodeCompiler::compile() { // specified, configure LTO to use it as the cache directory. lto::NativeObjectCache Cache; if (!Config->LTOCache.empty()) - Cache = check( - lto::localCache(Config->LTOCache, - [&](size_t Task, std::unique_ptr<MemoryBuffer> MB, - StringRef Path) { Files[Task] = std::move(MB); })); + Cache = check(lto::localCache( + Config->LTOCache, [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) { + Files[Task] = std::move(MB); + })); checkError(LTOObj->run( [&](size_t Task) { return llvm::make_unique<lto::NativeObjectStream>( - llvm::make_unique<raw_svector_ostream>(Buff[Task])); + llvm::make_unique<raw_svector_ostream>(Buf[Task])); }, Cache)); @@ -144,15 +130,15 @@ std::vector<StringRef> BitcodeCompiler::compile() { std::vector<StringRef> Ret; for (unsigned I = 0; I != MaxTasks; ++I) { - if (Buff[I].empty()) + if (Buf[I].empty()) continue; if (Config->SaveTemps) { if (I == 0) - saveBuffer(Buff[I], Config->OutputFile + ".lto.obj"); + saveBuffer(Buf[I], Config->OutputFile + ".lto.obj"); else - saveBuffer(Buff[I], Config->OutputFile + Twine(I) + ".lto.obj"); + saveBuffer(Buf[I], Config->OutputFile + Twine(I) + ".lto.obj"); } - Ret.emplace_back(Buff[I].data(), Buff[I].size()); + Ret.emplace_back(Buf[I].data(), Buf[I].size()); } for (std::unique_ptr<MemoryBuffer> &File : Files) diff --git a/contrib/llvm/tools/lld/COFF/LTO.h b/contrib/llvm/tools/lld/COFF/LTO.h index a444aa7ac4fe..f00924654780 100644 --- a/contrib/llvm/tools/lld/COFF/LTO.h +++ b/contrib/llvm/tools/lld/COFF/LTO.h @@ -48,7 +48,7 @@ public: private: std::unique_ptr<llvm::lto::LTO> LTOObj; - std::vector<SmallString<0>> Buff; + std::vector<SmallString<0>> Buf; std::vector<std::unique_ptr<MemoryBuffer>> Files; }; } diff --git a/contrib/llvm/tools/lld/COFF/MapFile.cpp b/contrib/llvm/tools/lld/COFF/MapFile.cpp index 717ed3419ea5..6ca1b6647bd7 100644 --- a/contrib/llvm/tools/lld/COFF/MapFile.cpp +++ b/contrib/llvm/tools/lld/COFF/MapFile.cpp @@ -23,7 +23,6 @@ #include "SymbolTable.h" #include "Symbols.h" #include "Writer.h" - #include "lld/Common/ErrorHandler.h" #include "llvm/Support/Parallel.h" #include "llvm/Support/raw_ostream.h" @@ -37,14 +36,15 @@ using namespace lld::coff; typedef DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>> SymbolMapTy; +static const std::string Indent8 = " "; // 8 spaces +static const std::string Indent16 = " "; // 16 spaces + // Print out the first three columns of a line. static void writeHeader(raw_ostream &OS, uint64_t Addr, uint64_t Size, uint64_t Align) { OS << format("%08llx %08llx %5lld ", Addr, Size, Align); } -static std::string indent(int Depth) { return std::string(Depth * 8, ' '); } - // Returns a list of all symbols that we want to print out. static std::vector<DefinedRegular *> getSymbols() { std::vector<DefinedRegular *> V; @@ -79,7 +79,7 @@ getSymbolStrings(ArrayRef<DefinedRegular *> Syms) { for_each_n(parallel::par, (size_t)0, Syms.size(), [&](size_t I) { raw_string_ostream OS(Str[I]); writeHeader(OS, Syms[I]->getRVA(), 0, 0); - OS << indent(2) << toString(*Syms[I]); + OS << Indent16 << toString(*Syms[I]); }); DenseMap<DefinedRegular *, std::string> Ret; @@ -108,7 +108,7 @@ void coff::writeMapFile(ArrayRef<OutputSection *> OutputSections) { // Print out file contents. for (OutputSection *Sec : OutputSections) { writeHeader(OS, Sec->getRVA(), Sec->getVirtualSize(), /*Align=*/PageSize); - OS << Sec->getName() << '\n'; + OS << Sec->Name << '\n'; for (Chunk *C : Sec->getChunks()) { auto *SC = dyn_cast<SectionChunk>(C); @@ -116,7 +116,7 @@ void coff::writeMapFile(ArrayRef<OutputSection *> OutputSections) { continue; writeHeader(OS, SC->getRVA(), SC->getSize(), SC->Alignment); - OS << indent(1) << SC->File->getName() << ":(" << SC->getSectionName() + OS << Indent8 << SC->File->getName() << ":(" << SC->getSectionName() << ")\n"; for (DefinedRegular *Sym : SectionSyms[SC]) OS << SymStr[Sym] << '\n'; diff --git a/contrib/llvm/tools/lld/COFF/MarkLive.cpp b/contrib/llvm/tools/lld/COFF/MarkLive.cpp index 01be60d12d82..57ae450a9138 100644 --- a/contrib/llvm/tools/lld/COFF/MarkLive.cpp +++ b/contrib/llvm/tools/lld/COFF/MarkLive.cpp @@ -9,16 +9,21 @@ #include "Chunks.h" #include "Symbols.h" +#include "lld/Common/Timer.h" #include "llvm/ADT/STLExtras.h" #include <vector> namespace lld { namespace coff { +static Timer GCTimer("GC", Timer::root()); + // Set live bit on for each reachable chunk. Unmarked (unreachable) // COMDAT chunks will be ignored by Writer, so they will be excluded // from the final output. void markLive(ArrayRef<Chunk *> Chunks) { + ScopedTimer T(GCTimer); + // We build up a worklist of sections which have been marked as live. We only // push into the worklist when we discover an unmarked section, and we mark // as we push, so sections never appear twice in the list. @@ -43,7 +48,7 @@ void markLive(ArrayRef<Chunk *> Chunks) { else if (auto *Sym = dyn_cast<DefinedImportData>(B)) Sym->File->Live = true; else if (auto *Sym = dyn_cast<DefinedImportThunk>(B)) - Sym->WrappedSym->File->Live = true; + Sym->WrappedSym->File->Live = Sym->WrappedSym->File->ThunkLive = true; }; // Add GC root chunks. diff --git a/contrib/llvm/tools/lld/COFF/MarkLive.h b/contrib/llvm/tools/lld/COFF/MarkLive.h new file mode 100644 index 000000000000..5b652dd48196 --- /dev/null +++ b/contrib/llvm/tools/lld/COFF/MarkLive.h @@ -0,0 +1,24 @@ +//===- MarkLive.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_COFF_MARKLIVE_H +#define LLD_COFF_MARKLIVE_H + +#include "lld/Common/LLVM.h" +#include "llvm/ADT/ArrayRef.h" + +namespace lld { +namespace coff { + +void markLive(ArrayRef<Chunk *> Chunks); + +} // namespace coff +} // namespace lld + +#endif // LLD_COFF_MARKLIVE_H diff --git a/contrib/llvm/tools/lld/COFF/MinGW.cpp b/contrib/llvm/tools/lld/COFF/MinGW.cpp index b7a47165640d..2ca00587331f 100644 --- a/contrib/llvm/tools/lld/COFF/MinGW.cpp +++ b/contrib/llvm/tools/lld/COFF/MinGW.cpp @@ -138,7 +138,7 @@ void coff::writeDefFile(StringRef Name) { << "@" << E.Ordinal; if (auto *Def = dyn_cast_or_null<Defined>(E.Sym)) { if (Def && Def->getChunk() && - !(Def->getChunk()->getPermissions() & IMAGE_SCN_MEM_EXECUTE)) + !(Def->getChunk()->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)) OS << " DATA"; } OS << "\n"; diff --git a/contrib/llvm/tools/lld/COFF/Options.td b/contrib/llvm/tools/lld/COFF/Options.td index 2a1de14657c1..871bad8bd655 100644 --- a/contrib/llvm/tools/lld/COFF/Options.td +++ b/contrib/llvm/tools/lld/COFF/Options.td @@ -20,6 +20,10 @@ def align : P<"align", "Section alignment">; def aligncomm : P<"aligncomm", "Set common symbol alignment">; def alternatename : P<"alternatename", "Define weak alias">; def base : P<"base", "Base address of the program">; +def color_diagnostics: Flag<["--"], "color-diagnostics">, + HelpText<"Use colors in diagnostics">; +def color_diagnostics_eq: Joined<["--"], "color-diagnostics=">, + HelpText<"Use colors in diagnostics; one of 'always', 'never', 'auto'">; def defaultlib : P<"defaultlib", "Add the library to the list of input files">; def delayload : P<"delayload", "Delay loaded DLL name">; def entry : P<"entry", "Name of entry point symbol">; @@ -28,9 +32,12 @@ def errorlimit : P<"errorlimit", def export : P<"export", "Export a function">; // No help text because /failifmismatch is not intended to be used by the user. def failifmismatch : P<"failifmismatch", "">; +def guard : P<"guard", "Control flow guard">; def heap : P<"heap", "Size of the heap">; def ignore : P<"ignore", "Specify warning codes to ignore">; def implib : P<"implib", "Import library name">; +def lib : F<"lib">, + HelpText<"Act like lib.exe; must be first argument if present">; def libpath : P<"libpath", "Additional library search path">; def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">; def lldltocache : P<"lldltocache", "Path to ThinLTO cached object file directory">; @@ -42,12 +49,18 @@ def merge : P<"merge", "Combine sections">; def mllvm : P<"mllvm", "Options to pass to LLVM">; def nodefaultlib : P<"nodefaultlib", "Remove a default library">; def opt : P<"opt", "Control optimizations">; +def order : P<"order", "Put functions in order">; def out : P<"out", "Path to file to write output">; +def natvis : P<"natvis", "Path to natvis file to embed in the PDB">; +def no_color_diagnostics: F<"no-color-diagnostics">, + HelpText<"Do not use colors in diagnostics">; def pdb : P<"pdb", "PDB file path">; +def pdbaltpath : P<"pdbaltpath", "PDB file path to embed in the image">; def section : P<"section", "Specify section attributes">; def stack : P<"stack", "Size of the stack">; def stub : P<"stub", "Specify DOS stub file">; def subsystem : P<"subsystem", "Specify subsystem">; +def timestamp : P<"timestamp", "Specify the PE header timestamp">; def version : P<"version", "Specify a version number in the PE header">; def wholearchive_file : P<"wholearchive", "Include all object files from this archive">; @@ -72,12 +85,14 @@ def deffile : Joined<["/", "-"], "def:">, HelpText<"Use module-definition file">; def debug : F<"debug">, HelpText<"Embed a symbol table in the image">; +def debug_full : F<"debug:full">, Alias<debug>; def debugtype : P<"debugtype", "Debug Info Options">; def dll : F<"dll">, HelpText<"Create a DLL">; def driver : P<"driver", "Generate a Windows NT Kernel Mode Driver">; def nodefaultlib_all : F<"nodefaultlib">; def noentry : F<"noentry">; def profile : F<"profile">; +def repro : F<"Brepro">, HelpText<"Use a hash of the executable as the PE header timestamp">; def swaprun_cd : F<"swaprun:cd">; def swaprun_net : F<"swaprun:net">; def verbose : F<"verbose">; @@ -102,6 +117,12 @@ defm fixed : B<"fixed", "Disable base relocations", defm highentropyva : B<"highentropyva", "Enable 64-bit ASLR (default on 64-bit)", "Disable 64-bit ASLR">; +defm incremental : B<"incremental", + "Keep original import library if contents are unchanged", + "Overwrite import library even if contents are unchanged">; +defm integritycheck : B<"integritycheck", + "Set FORCE_INTEGRITY bit in PE header", + "No effect (default)">; defm largeaddressaware : B<"largeaddressaware", "Enable large addresses (default on 64-bit)", "Disable large addresses (default on 32-bit)">; @@ -120,11 +141,14 @@ def help_q : Flag<["/?", "-?"], "">, Alias<help>; // LLD extensions def debug_ghash : F<"debug:ghash">; def debug_dwarf : F<"debug:dwarf">; +def debug_symtab : F<"debug:symtab">; def export_all_symbols : F<"export-all-symbols">; def kill_at : F<"kill-at">; def lldmingw : F<"lldmingw">; def msvclto : F<"msvclto">; def output_def : Joined<["/", "-"], "output-def:">; +def pdb_source_path : P<"pdbsourcepath", + "Base path used to make relative source file path absolute in PDB">; def rsp_quoting : Joined<["--"], "rsp-quoting=">, HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">; def dash_dash_version : Flag<["--"], "version">, @@ -133,6 +157,7 @@ def dash_dash_version : Flag<["--"], "version">, // Flags for debugging def lldmap : F<"lldmap">; def lldmap_file : Joined<["/", "-"], "lldmap:">; +def show_timing : F<"time">; //============================================================================== // The flags below do nothing. They are defined only for link.exe compatibility. @@ -147,8 +172,6 @@ multiclass QB<string name> { def functionpadmin : F<"functionpadmin">; def ignoreidl : F<"ignoreidl">; -def incremental : F<"incremental">; -def no_incremental : F<"incremental:no">; def nologo : F<"nologo">; def throwingnew : F<"throwingnew">; def editandcontinue : F<"editandcontinue">; @@ -158,8 +181,6 @@ def delay : QF<"delay">; def errorreport : QF<"errorreport">; def idlout : QF<"idlout">; def maxilksize : QF<"maxilksize">; -def natvis : QF<"natvis">; -def pdbaltpath : QF<"pdbaltpath">; def tlbid : QF<"tlbid">; def tlbout : QF<"tlbout">; def verbose_all : QF<"verbose">; diff --git a/contrib/llvm/tools/lld/COFF/PDB.cpp b/contrib/llvm/tools/lld/COFF/PDB.cpp index 8ca52556ae58..766bf3f6b456 100644 --- a/contrib/llvm/tools/lld/COFF/PDB.cpp +++ b/contrib/llvm/tools/lld/COFF/PDB.cpp @@ -15,7 +15,7 @@ #include "Symbols.h" #include "Writer.h" #include "lld/Common/ErrorHandler.h" -#include "llvm/DebugInfo/CodeView/CVDebugRecord.h" +#include "lld/Common/Timer.h" #include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" #include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" @@ -45,8 +45,10 @@ #include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h" #include "llvm/DebugInfo/PDB/PDB.h" #include "llvm/Object/COFF.h" +#include "llvm/Object/CVDebugRecord.h" #include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/Endian.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/JamCRC.h" #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" @@ -61,6 +63,15 @@ using llvm::object::coff_section; static ExitOnError ExitOnErr; +static Timer TotalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root()); + +static Timer AddObjectsTimer("Add Objects", TotalPdbLinkTimer); +static Timer TypeMergingTimer("Type Merging", AddObjectsTimer); +static Timer SymbolMergingTimer("Symbol Merging", AddObjectsTimer); +static Timer GlobalsLayoutTimer("Globals Stream Layout", TotalPdbLinkTimer); +static Timer TpiStreamLayoutTimer("TPI Stream Layout", TotalPdbLinkTimer); +static Timer DiskCommitTimer("Commit to Disk", TotalPdbLinkTimer); + namespace { /// Map from type index and item index in a type server PDB to the /// corresponding index in the destination PDB. @@ -74,11 +85,19 @@ class PDBLinker { public: PDBLinker(SymbolTable *Symtab) : Alloc(), Symtab(Symtab), Builder(Alloc), TypeTable(Alloc), - IDTable(Alloc), GlobalTypeTable(Alloc), GlobalIDTable(Alloc) {} + IDTable(Alloc), GlobalTypeTable(Alloc), GlobalIDTable(Alloc) { + // This isn't strictly necessary, but link.exe usually puts an empty string + // as the first "valid" string in the string table, so we do the same in + // order to maintain as much byte-for-byte compatibility as possible. + PDBStrTab.insert(""); + } /// Emit the basic PDB structure: initial streams, headers, etc. void initialize(const llvm::codeview::DebugInfo &BuildId); + /// Add natvis files specified on the command line. + void addNatvisFiles(); + /// Link CodeView from each object file in the symbol table into the PDB. void addObjectsToPDB(); @@ -106,9 +125,6 @@ public: void addSections(ArrayRef<OutputSection *> OutputSections, ArrayRef<uint8_t> SectionTable); - void addSectionContrib(pdb::DbiModuleDescriptorBuilder &LinkerModule, - OutputSection *OS, Chunk *C); - /// Write the PDB to disk. void commit(); @@ -137,6 +153,11 @@ private: llvm::SmallString<128> NativePath; + /// A list of other PDBs which are loaded during the linking process and which + /// we need to keep around since the linking operation may reference pointers + /// inside of these PDBs. + llvm::SmallVector<std::unique_ptr<pdb::NativeSession>, 2> LoadedPDBs; + std::vector<pdb::SecMapEntry> SectionMap; /// Type index mappings of type server PDBs that we've loaded so far. @@ -184,8 +205,8 @@ static bool canUseDebugH(ArrayRef<uint8_t> DebugH) { DebugH = DebugH.drop_front(sizeof(object::debug_h_header)); return Header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC && Header->Version == 0 && - Header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1) && - (DebugH.size() % 20 == 0); + Header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1_8) && + (DebugH.size() % 8 == 0); } static Optional<ArrayRef<uint8_t>> getDebugH(ObjFile *File) { @@ -237,6 +258,8 @@ maybeReadTypeServerRecord(CVTypeArray &Types) { Expected<const CVIndexMap&> PDBLinker::mergeDebugT(ObjFile *File, CVIndexMap &ObjectIndexMap) { + ScopedTimer T(TypeMergingTimer); + ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$T"); if (Data.empty()) return ObjectIndexMap; @@ -348,10 +371,16 @@ Expected<const CVIndexMap&> PDBLinker::maybeMergeTypeServerPDB(ObjFile *File, return std::move(E); } - auto ExpectedTpi = (*ExpectedSession)->getPDBFile().getPDBTpiStream(); + pdb::NativeSession *Session = ExpectedSession->get(); + + // Keep a strong reference to this PDB, so that it's safe to hold pointers + // into the file. + LoadedPDBs.push_back(std::move(*ExpectedSession)); + + auto ExpectedTpi = Session->getPDBFile().getPDBTpiStream(); if (auto E = ExpectedTpi.takeError()) fatal("Type server does not have TPI stream: " + toString(std::move(E))); - auto ExpectedIpi = (*ExpectedSession)->getPDBFile().getPDBIpiStream(); + auto ExpectedIpi = Session->getPDBFile().getPDBIpiStream(); if (auto E = ExpectedIpi.takeError()) fatal("Type server does not have TPI stream: " + toString(std::move(E))); @@ -428,6 +457,38 @@ static void remapTypesInSymbolRecord(ObjFile *File, SymbolKind SymKind, } } +static void +recordStringTableReferenceAtOffset(MutableArrayRef<uint8_t> Contents, + uint32_t Offset, + std::vector<ulittle32_t *> &StrTableRefs) { + Contents = + Contents.drop_front(Offset).take_front(sizeof(support::ulittle32_t)); + ulittle32_t *Index = reinterpret_cast<ulittle32_t *>(Contents.data()); + StrTableRefs.push_back(Index); +} + +static void +recordStringTableReferences(SymbolKind Kind, MutableArrayRef<uint8_t> Contents, + std::vector<ulittle32_t *> &StrTableRefs) { + // For now we only handle S_FILESTATIC, but we may need the same logic for + // S_DEFRANGE and S_DEFRANGE_SUBFIELD. However, I cannot seem to generate any + // PDBs that contain these types of records, so because of the uncertainty + // they are omitted here until we can prove that it's necessary. + switch (Kind) { + case SymbolKind::S_FILESTATIC: + // FileStaticSym::ModFileOffset + recordStringTableReferenceAtOffset(Contents, 4, StrTableRefs); + break; + case SymbolKind::S_DEFRANGE: + case SymbolKind::S_DEFRANGE_SUBFIELD: + log("Not fixing up string table reference in S_DEFRANGE / " + "S_DEFRANGE_SUBFIELD record"); + break; + default: + break; + } +} + static SymbolKind symbolKind(ArrayRef<uint8_t> RecordData) { const RecordPrefix *Prefix = reinterpret_cast<const RecordPrefix *>(RecordData.data()); @@ -644,53 +705,65 @@ static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjFile *File, pdb::GSIStreamBuilder &GsiBuilder, const CVIndexMap &IndexMap, TypeCollection &IDTable, + std::vector<ulittle32_t *> &StringTableRefs, BinaryStreamRef SymData) { // FIXME: Improve error recovery by warning and skipping records when // possible. - CVSymbolArray Syms; - BinaryStreamReader Reader(SymData); - ExitOnErr(Reader.readArray(Syms, Reader.getLength())); + ArrayRef<uint8_t> SymsBuffer; + cantFail(SymData.readBytes(0, SymData.getLength(), SymsBuffer)); SmallVector<SymbolScope, 4> Scopes; - for (CVSymbol Sym : Syms) { - // Discover type index references in the record. Skip it if we don't know - // where they are. - SmallVector<TiReference, 32> TypeRefs; - if (!discoverTypeIndicesInSymbol(Sym, TypeRefs)) { - log("ignoring unknown symbol record with kind 0x" + utohexstr(Sym.kind())); - continue; - } - - // Copy the symbol record so we can mutate it. - MutableArrayRef<uint8_t> NewData = copySymbolForPdb(Sym, Alloc); - - // Re-map all the type index references. - MutableArrayRef<uint8_t> Contents = - NewData.drop_front(sizeof(RecordPrefix)); - remapTypesInSymbolRecord(File, Sym.kind(), Contents, IndexMap, TypeRefs); - // An object file may have S_xxx_ID symbols, but these get converted to - // "real" symbols in a PDB. - translateIdSymbols(NewData, IDTable); - - SymbolKind NewKind = symbolKind(NewData); - - // Fill in "Parent" and "End" fields by maintaining a stack of scopes. - CVSymbol NewSym(NewKind, NewData); - if (symbolOpensScope(NewKind)) - scopeStackOpen(Scopes, File->ModuleDBI->getNextSymbolOffset(), NewSym); - else if (symbolEndsScope(NewKind)) - scopeStackClose(Scopes, File->ModuleDBI->getNextSymbolOffset(), File); - - // Add the symbol to the globals stream if necessary. Do this before adding - // the symbol to the module since we may need to get the next symbol offset, - // and writing to the module's symbol stream will update that offset. - if (symbolGoesInGlobalsStream(NewSym)) - addGlobalSymbol(GsiBuilder, *File, NewSym); + auto EC = forEachCodeViewRecord<CVSymbol>( + SymsBuffer, [&](const CVSymbol &Sym) -> llvm::Error { + // Discover type index references in the record. Skip it if we don't + // know where they are. + SmallVector<TiReference, 32> TypeRefs; + if (!discoverTypeIndicesInSymbol(Sym, TypeRefs)) { + log("ignoring unknown symbol record with kind 0x" + + utohexstr(Sym.kind())); + return Error::success(); + } - // Add the symbol to the module. - if (symbolGoesInModuleStream(NewSym)) - File->ModuleDBI->addSymbol(NewSym); - } + // Copy the symbol record so we can mutate it. + MutableArrayRef<uint8_t> NewData = copySymbolForPdb(Sym, Alloc); + + // Re-map all the type index references. + MutableArrayRef<uint8_t> Contents = + NewData.drop_front(sizeof(RecordPrefix)); + remapTypesInSymbolRecord(File, Sym.kind(), Contents, IndexMap, + TypeRefs); + + // An object file may have S_xxx_ID symbols, but these get converted to + // "real" symbols in a PDB. + translateIdSymbols(NewData, IDTable); + + // If this record refers to an offset in the object file's string table, + // add that item to the global PDB string table and re-write the index. + recordStringTableReferences(Sym.kind(), Contents, StringTableRefs); + + SymbolKind NewKind = symbolKind(NewData); + + // Fill in "Parent" and "End" fields by maintaining a stack of scopes. + CVSymbol NewSym(NewKind, NewData); + if (symbolOpensScope(NewKind)) + scopeStackOpen(Scopes, File->ModuleDBI->getNextSymbolOffset(), + NewSym); + else if (symbolEndsScope(NewKind)) + scopeStackClose(Scopes, File->ModuleDBI->getNextSymbolOffset(), File); + + // Add the symbol to the globals stream if necessary. Do this before + // adding the symbol to the module since we may need to get the next + // symbol offset, and writing to the module's symbol stream will update + // that offset. + if (symbolGoesInGlobalsStream(NewSym)) + addGlobalSymbol(GsiBuilder, *File, NewSym); + + // Add the symbol to the module. + if (symbolGoesInModuleStream(NewSym)) + File->ModuleDBI->addSymbol(NewSym); + return Error::success(); + }); + cantFail(std::move(EC)); } // Allocate memory for a .debug$S section and relocate it. @@ -704,6 +777,32 @@ static ArrayRef<uint8_t> relocateDebugChunk(BumpPtrAllocator &Alloc, ".debug$S"); } +static pdb::SectionContrib createSectionContrib(const Chunk *C, uint32_t Modi) { + OutputSection *OS = C->getOutputSection(); + pdb::SectionContrib SC; + memset(&SC, 0, sizeof(SC)); + SC.ISect = OS->SectionIndex; + SC.Off = C->getRVA() - OS->getRVA(); + SC.Size = C->getSize(); + if (auto *SecChunk = dyn_cast<SectionChunk>(C)) { + SC.Characteristics = SecChunk->Header->Characteristics; + SC.Imod = SecChunk->File->ModuleDBI->getModuleIndex(); + ArrayRef<uint8_t> Contents = SecChunk->getContents(); + JamCRC CRC(0); + ArrayRef<char> CharContents = makeArrayRef( + reinterpret_cast<const char *>(Contents.data()), Contents.size()); + CRC.update(CharContents); + SC.DataCrc = CRC.getCRC(); + } else { + SC.Characteristics = OS->Header.Characteristics; + // FIXME: When we start creating DBI for import libraries, use those here. + SC.Imod = Modi; + } + SC.RelocCrc = 0; // FIXME + + return SC; +} + void PDBLinker::addObjFile(ObjFile *File) { // Add a module descriptor for every object file. We need to put an absolute // path to the object into the PDB. If this is a plain object, we make its @@ -718,6 +817,17 @@ void PDBLinker::addObjFile(ObjFile *File) { File->ModuleDBI = &ExitOnErr(Builder.getDbiBuilder().addModuleInfo(Name)); File->ModuleDBI->setObjFileName(Path); + auto Chunks = File->getChunks(); + uint32_t Modi = File->ModuleDBI->getModuleIndex(); + for (Chunk *C : Chunks) { + auto *SecChunk = dyn_cast<SectionChunk>(C); + if (!SecChunk || !SecChunk->isLive()) + continue; + pdb::SectionContrib SC = createSectionContrib(SecChunk, Modi); + File->ModuleDBI->setFirstSectionContrib(SC); + break; + } + // Before we can process symbol substreams from .debug$S, we need to process // type information, file checksums, and the string table. Add type info to // the PDB first, so that we can get the map from object file type and item @@ -734,7 +844,12 @@ void PDBLinker::addObjFile(ObjFile *File) { const CVIndexMap &IndexMap = *IndexMapResult; + ScopedTimer T(SymbolMergingTimer); + // Now do all live .debug$S sections. + DebugStringTableSubsectionRef CVStrTab; + DebugChecksumsSubsectionRef Checksums; + std::vector<ulittle32_t *> StringTableReferences; for (SectionChunk *DebugChunk : File->getDebugChunks()) { if (!DebugChunk->isLive() || DebugChunk->getSectionName() != ".debug$S") continue; @@ -748,14 +863,17 @@ void PDBLinker::addObjFile(ObjFile *File) { BinaryStreamReader Reader(RelocatedDebugContents, support::little); ExitOnErr(Reader.readArray(Subsections, RelocatedDebugContents.size())); - DebugStringTableSubsectionRef CVStrTab; - DebugChecksumsSubsectionRef Checksums; for (const DebugSubsectionRecord &SS : Subsections) { switch (SS.kind()) { - case DebugSubsectionKind::StringTable: + case DebugSubsectionKind::StringTable: { + assert(!CVStrTab.valid() && + "Encountered multiple string table subsections!"); ExitOnErr(CVStrTab.initialize(SS.getRecordData())); break; + } case DebugSubsectionKind::FileChecksums: + assert(!Checksums.valid() && + "Encountered multiple checksum subsections!"); ExitOnErr(Checksums.initialize(SS.getRecordData())); break; case DebugSubsectionKind::Lines: @@ -766,10 +884,12 @@ void PDBLinker::addObjFile(ObjFile *File) { case DebugSubsectionKind::Symbols: if (Config->DebugGHashes) { mergeSymbolRecords(Alloc, File, Builder.getGsiBuilder(), IndexMap, - GlobalIDTable, SS.getRecordData()); + GlobalIDTable, StringTableReferences, + SS.getRecordData()); } else { mergeSymbolRecords(Alloc, File, Builder.getGsiBuilder(), IndexMap, - IDTable, SS.getRecordData()); + IDTable, StringTableReferences, + SS.getRecordData()); } break; default: @@ -777,25 +897,55 @@ void PDBLinker::addObjFile(ObjFile *File) { break; } } + } - if (Checksums.valid()) { - // Make a new file checksum table that refers to offsets in the PDB-wide - // string table. Generally the string table subsection appears after the - // checksum table, so we have to do this after looping over all the - // subsections. - if (!CVStrTab.valid()) - fatal(".debug$S sections must have both a string table subsection " - "and a checksum subsection table or neither"); - auto NewChecksums = make_unique<DebugChecksumsSubsection>(PDBStrTab); - for (FileChecksumEntry &FC : Checksums) { - StringRef FileName = ExitOnErr(CVStrTab.getString(FC.FileNameOffset)); - ExitOnErr(Builder.getDbiBuilder().addModuleSourceFile(*File->ModuleDBI, - FileName)); - NewChecksums->addChecksum(FileName, FC.Kind, FC.Checksum); - } - File->ModuleDBI->addDebugSubsection(std::move(NewChecksums)); + // We should have seen all debug subsections across the entire object file now + // which means that if a StringTable subsection and Checksums subsection were + // present, now is the time to handle them. + if (!CVStrTab.valid()) { + if (Checksums.valid()) + fatal(".debug$S sections with a checksums subsection must also contain a " + "string table subsection"); + + if (!StringTableReferences.empty()) + warn("No StringTable subsection was encountered, but there are string " + "table references"); + return; + } + + // Rewrite each string table reference based on the value that the string + // assumes in the final PDB. + for (ulittle32_t *Ref : StringTableReferences) { + auto ExpectedString = CVStrTab.getString(*Ref); + if (!ExpectedString) { + warn("Invalid string table reference"); + consumeError(ExpectedString.takeError()); + continue; } + + *Ref = PDBStrTab.insert(*ExpectedString); } + + // Make a new file checksum table that refers to offsets in the PDB-wide + // string table. Generally the string table subsection appears after the + // checksum table, so we have to do this after looping over all the + // subsections. + auto NewChecksums = make_unique<DebugChecksumsSubsection>(PDBStrTab); + for (FileChecksumEntry &FC : Checksums) { + SmallString<128> FileName = ExitOnErr(CVStrTab.getString(FC.FileNameOffset)); + if (!sys::path::is_absolute(FileName) && + !Config->PDBSourcePath.empty()) { + SmallString<128> AbsoluteFileName = Config->PDBSourcePath; + sys::path::append(AbsoluteFileName, FileName); + sys::path::native(AbsoluteFileName); + sys::path::remove_dots(AbsoluteFileName, /*remove_dot_dots=*/true); + FileName = std::move(AbsoluteFileName); + } + ExitOnErr(Builder.getDbiBuilder().addModuleSourceFile(*File->ModuleDBI, + FileName)); + NewChecksums->addChecksum(FileName, FC.Kind, FC.Checksum); + } + File->ModuleDBI->addDebugSubsection(std::move(NewChecksums)); } static PublicSym32 createPublic(Defined *Def) { @@ -818,12 +968,15 @@ static PublicSym32 createPublic(Defined *Def) { // Add all object files to the PDB. Merge .debug$T sections into IpiData and // TpiData. void PDBLinker::addObjectsToPDB() { + ScopedTimer T1(AddObjectsTimer); for (ObjFile *File : ObjFile::Instances) addObjFile(File); Builder.getStringTableBuilder().setStrings(PDBStrTab); + T1.stop(); // Construct TPI and IPI stream contents. + ScopedTimer T2(TpiStreamLayoutTimer); if (Config->DebugGHashes) { addTypeInfo(Builder.getTpiBuilder(), GlobalTypeTable); addTypeInfo(Builder.getIpiBuilder(), GlobalIDTable); @@ -831,7 +984,9 @@ void PDBLinker::addObjectsToPDB() { addTypeInfo(Builder.getTpiBuilder(), TypeTable); addTypeInfo(Builder.getIpiBuilder(), IDTable); } + T2.stop(); + ScopedTimer T3(GlobalsLayoutTimer); // Compute the public and global symbols. auto &GsiBuilder = Builder.getGsiBuilder(); std::vector<PublicSym32> Publics; @@ -853,6 +1008,35 @@ void PDBLinker::addObjectsToPDB() { } } +void PDBLinker::addNatvisFiles() { + for (StringRef File : Config->NatvisFiles) { + ErrorOr<std::unique_ptr<MemoryBuffer>> DataOrErr = + MemoryBuffer::getFile(File); + if (!DataOrErr) { + warn("Cannot open input file: " + File); + continue; + } + Builder.addInjectedSource(File, std::move(*DataOrErr)); + } +} + +static codeview::CPUType toCodeViewMachine(COFF::MachineTypes Machine) { + switch (Machine) { + case COFF::IMAGE_FILE_MACHINE_AMD64: + return codeview::CPUType::X64; + case COFF::IMAGE_FILE_MACHINE_ARM: + return codeview::CPUType::ARM7; + case COFF::IMAGE_FILE_MACHINE_ARM64: + return codeview::CPUType::ARM64; + case COFF::IMAGE_FILE_MACHINE_ARMNT: + return codeview::CPUType::ARMNT; + case COFF::IMAGE_FILE_MACHINE_I386: + return codeview::CPUType::Intel80386; + default: + llvm_unreachable("Unsupported CPU Type"); + } +} + static void addCommonLinkerModuleSymbols(StringRef Path, pdb::DbiModuleDescriptorBuilder &Mod, BumpPtrAllocator &Allocator) { @@ -863,7 +1047,7 @@ static void addCommonLinkerModuleSymbols(StringRef Path, ONS.Name = "* Linker *"; ONS.Signature = 0; - CS.Machine = Config->is64() ? CPUType::X64 : CPUType::Intel80386; + CS.Machine = toCodeViewMachine(Config->Machine); // Interestingly, if we set the string to 0.0.0.0, then when trying to view // local variables WinDbg emits an error that private symbols are not present. // By setting this to a valid MSVC linker version string, local variables are @@ -914,9 +1098,9 @@ static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &Mod, BumpPtrAllocator &Allocator) { SectionSym Sym(SymbolRecordKind::SectionSym); Sym.Alignment = 12; // 2^12 = 4KB - Sym.Characteristics = OS.getCharacteristics(); + Sym.Characteristics = OS.Header.Characteristics; Sym.Length = OS.getVirtualSize(); - Sym.Name = OS.getName(); + Sym.Name = OS.Name; Sym.Rva = OS.getRVA(); Sym.SectionNumber = OS.SectionIndex; Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( @@ -928,10 +1112,15 @@ void coff::createPDB(SymbolTable *Symtab, ArrayRef<OutputSection *> OutputSections, ArrayRef<uint8_t> SectionTable, const llvm::codeview::DebugInfo &BuildId) { + ScopedTimer T1(TotalPdbLinkTimer); PDBLinker PDB(Symtab); + PDB.initialize(BuildId); PDB.addObjectsToPDB(); PDB.addSections(OutputSections, SectionTable); + PDB.addNatvisFiles(); + + ScopedTimer T2(DiskCommitTimer); PDB.commit(); } @@ -945,44 +1134,22 @@ void PDBLinker::initialize(const llvm::codeview::DebugInfo &BuildId) { // Add an Info stream. auto &InfoBuilder = Builder.getInfoBuilder(); - InfoBuilder.setAge(BuildId.PDB70.Age); - GUID uuid; memcpy(&uuid, &BuildId.PDB70.Signature, sizeof(uuid)); + InfoBuilder.setAge(BuildId.PDB70.Age); InfoBuilder.setGuid(uuid); - InfoBuilder.setSignature(time(nullptr)); InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70); // Add an empty DBI stream. pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder(); DbiBuilder.setAge(BuildId.PDB70.Age); DbiBuilder.setVersionHeader(pdb::PdbDbiV70); - ExitOnErr(DbiBuilder.addDbgStream(pdb::DbgHeaderType::NewFPO, {})); -} - -void PDBLinker::addSectionContrib(pdb::DbiModuleDescriptorBuilder &LinkerModule, - OutputSection *OS, Chunk *C) { - pdb::SectionContrib SC; - memset(&SC, 0, sizeof(SC)); - SC.ISect = OS->SectionIndex; - SC.Off = C->getRVA() - OS->getRVA(); - SC.Size = C->getSize(); - if (auto *SecChunk = dyn_cast<SectionChunk>(C)) { - SC.Characteristics = SecChunk->Header->Characteristics; - SC.Imod = SecChunk->File->ModuleDBI->getModuleIndex(); - ArrayRef<uint8_t> Contents = SecChunk->getContents(); - JamCRC CRC(0); - ArrayRef<char> CharContents = makeArrayRef( - reinterpret_cast<const char *>(Contents.data()), Contents.size()); - CRC.update(CharContents); - SC.DataCrc = CRC.getCRC(); - } else { - SC.Characteristics = OS->getCharacteristics(); - // FIXME: When we start creating DBI for import libraries, use those here. - SC.Imod = LinkerModule.getModuleIndex(); - } - SC.RelocCrc = 0; // FIXME - Builder.getDbiBuilder().addSectionContrib(SC); + DbiBuilder.setMachineType(Config->Machine); + // Technically we are not link.exe 14.11, but there are known cases where + // debugging tools on Windows expect Microsoft-specific version numbers or + // they fail to work at all. Since we know we produce PDBs that are + // compatible with LINK 14.11, we set that version number here. + DbiBuilder.setBuildNumber(14, 11); } void PDBLinker::addSections(ArrayRef<OutputSection *> OutputSections, @@ -1000,8 +1167,11 @@ void PDBLinker::addSections(ArrayRef<OutputSection *> OutputSections, // Add section contributions. They must be ordered by ascending RVA. for (OutputSection *OS : OutputSections) { addLinkerModuleSectionSymbol(LinkerModule, *OS, Alloc); - for (Chunk *C : OS->getChunks()) - addSectionContrib(LinkerModule, OS, C); + for (Chunk *C : OS->getChunks()) { + pdb::SectionContrib SC = + createSectionContrib(C, LinkerModule.getModuleIndex()); + Builder.getDbiBuilder().addSectionContrib(SC); + } } // Add Section Map stream. @@ -1020,3 +1190,145 @@ void PDBLinker::commit() { // Write to a file. ExitOnErr(Builder.commit(Config->PDBPath)); } + +static Expected<StringRef> +getFileName(const DebugStringTableSubsectionRef &Strings, + const DebugChecksumsSubsectionRef &Checksums, uint32_t FileID) { + auto Iter = Checksums.getArray().at(FileID); + if (Iter == Checksums.getArray().end()) + return make_error<CodeViewError>(cv_error_code::no_records); + uint32_t Offset = Iter->FileNameOffset; + return Strings.getString(Offset); +} + +static uint32_t getSecrelReloc() { + switch (Config->Machine) { + case AMD64: + return COFF::IMAGE_REL_AMD64_SECREL; + case I386: + return COFF::IMAGE_REL_I386_SECREL; + case ARMNT: + return COFF::IMAGE_REL_ARM_SECREL; + case ARM64: + return COFF::IMAGE_REL_ARM64_SECREL; + default: + llvm_unreachable("unknown machine type"); + } +} + +// Try to find a line table for the given offset Addr into the given chunk C. +// If a line table was found, the line table, the string and checksum tables +// that are used to interpret the line table, and the offset of Addr in the line +// table are stored in the output arguments. Returns whether a line table was +// found. +static bool findLineTable(const SectionChunk *C, uint32_t Addr, + DebugStringTableSubsectionRef &CVStrTab, + DebugChecksumsSubsectionRef &Checksums, + DebugLinesSubsectionRef &Lines, + uint32_t &OffsetInLinetable) { + ExitOnError ExitOnErr; + uint32_t SecrelReloc = getSecrelReloc(); + + for (SectionChunk *DbgC : C->File->getDebugChunks()) { + if (DbgC->getSectionName() != ".debug$S") + continue; + + // Build a mapping of SECREL relocations in DbgC that refer to C. + DenseMap<uint32_t, uint32_t> Secrels; + for (const coff_relocation &R : DbgC->Relocs) { + if (R.Type != SecrelReloc) + continue; + + if (auto *S = dyn_cast_or_null<DefinedRegular>( + C->File->getSymbols()[R.SymbolTableIndex])) + if (S->getChunk() == C) + Secrels[R.VirtualAddress] = S->getValue(); + } + + ArrayRef<uint8_t> Contents = + consumeDebugMagic(DbgC->getContents(), ".debug$S"); + DebugSubsectionArray Subsections; + BinaryStreamReader Reader(Contents, support::little); + ExitOnErr(Reader.readArray(Subsections, Contents.size())); + + for (const DebugSubsectionRecord &SS : Subsections) { + switch (SS.kind()) { + case DebugSubsectionKind::StringTable: { + assert(!CVStrTab.valid() && + "Encountered multiple string table subsections!"); + ExitOnErr(CVStrTab.initialize(SS.getRecordData())); + break; + } + case DebugSubsectionKind::FileChecksums: + assert(!Checksums.valid() && + "Encountered multiple checksum subsections!"); + ExitOnErr(Checksums.initialize(SS.getRecordData())); + break; + case DebugSubsectionKind::Lines: { + ArrayRef<uint8_t> Bytes; + auto Ref = SS.getRecordData(); + ExitOnErr(Ref.readLongestContiguousChunk(0, Bytes)); + size_t OffsetInDbgC = Bytes.data() - DbgC->getContents().data(); + + // Check whether this line table refers to C. + auto I = Secrels.find(OffsetInDbgC); + if (I == Secrels.end()) + break; + + // Check whether this line table covers Addr in C. + DebugLinesSubsectionRef LinesTmp; + ExitOnErr(LinesTmp.initialize(BinaryStreamReader(Ref))); + uint32_t OffsetInC = I->second + LinesTmp.header()->RelocOffset; + if (Addr < OffsetInC || Addr >= OffsetInC + LinesTmp.header()->CodeSize) + break; + + assert(!Lines.header() && + "Encountered multiple line tables for function!"); + ExitOnErr(Lines.initialize(BinaryStreamReader(Ref))); + OffsetInLinetable = Addr - OffsetInC; + break; + } + default: + break; + } + + if (CVStrTab.valid() && Checksums.valid() && Lines.header()) + return true; + } + } + + return false; +} + +// Use CodeView line tables to resolve a file and line number for the given +// offset into the given chunk and return them, or {"", 0} if a line table was +// not found. +std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *C, + uint32_t Addr) { + ExitOnError ExitOnErr; + + DebugStringTableSubsectionRef CVStrTab; + DebugChecksumsSubsectionRef Checksums; + DebugLinesSubsectionRef Lines; + uint32_t OffsetInLinetable; + + if (!findLineTable(C, Addr, CVStrTab, Checksums, Lines, OffsetInLinetable)) + return {"", 0}; + + uint32_t NameIndex; + uint32_t LineNumber; + for (LineColumnEntry &Entry : Lines) { + for (const LineNumberEntry &LN : Entry.LineNumbers) { + if (LN.Offset > OffsetInLinetable) { + StringRef Filename = + ExitOnErr(getFileName(CVStrTab, Checksums, NameIndex)); + return {Filename, LineNumber}; + } + LineInfo LI(LN.Flags); + NameIndex = Entry.NameIndex; + LineNumber = LI.getStartLine(); + } + } + StringRef Filename = ExitOnErr(getFileName(CVStrTab, Checksums, NameIndex)); + return {Filename, LineNumber}; +} diff --git a/contrib/llvm/tools/lld/COFF/PDB.h b/contrib/llvm/tools/lld/COFF/PDB.h index defd7d236790..a98d129a633b 100644 --- a/contrib/llvm/tools/lld/COFF/PDB.h +++ b/contrib/llvm/tools/lld/COFF/PDB.h @@ -22,12 +22,16 @@ union DebugInfo; namespace lld { namespace coff { class OutputSection; +class SectionChunk; class SymbolTable; void createPDB(SymbolTable *Symtab, llvm::ArrayRef<OutputSection *> OutputSections, llvm::ArrayRef<uint8_t> SectionTable, const llvm::codeview::DebugInfo &BuildId); + +std::pair<llvm::StringRef, uint32_t> getFileLine(const SectionChunk *C, + uint32_t Addr); } } diff --git a/contrib/llvm/tools/lld/COFF/Strings.cpp b/contrib/llvm/tools/lld/COFF/Strings.cpp deleted file mode 100644 index 89b9c5186fd1..000000000000 --- a/contrib/llvm/tools/lld/COFF/Strings.cpp +++ /dev/null @@ -1,35 +0,0 @@ -//===- Strings.cpp -------------------------------------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "Strings.h" -#include <mutex> - -#if defined(_MSC_VER) -#include <Windows.h> -#include <DbgHelp.h> -#pragma comment(lib, "dbghelp.lib") -#endif - -using namespace lld; -using namespace lld::coff; -using namespace llvm; - -Optional<std::string> coff::demangleMSVC(StringRef S) { -#if defined(_MSC_VER) - // UnDecorateSymbolName is not thread-safe, so we need a mutex. - static std::mutex Mu; - std::lock_guard<std::mutex> Lock(Mu); - - char Buf[4096]; - if (S.startswith("?")) - if (size_t Len = UnDecorateSymbolName(S.str().c_str(), Buf, sizeof(Buf), 0)) - return std::string(Buf, Len); -#endif - return None; -} diff --git a/contrib/llvm/tools/lld/COFF/SymbolTable.cpp b/contrib/llvm/tools/lld/COFF/SymbolTable.cpp index df76679535cb..b286d865caaf 100644 --- a/contrib/llvm/tools/lld/COFF/SymbolTable.cpp +++ b/contrib/llvm/tools/lld/COFF/SymbolTable.cpp @@ -11,9 +11,11 @@ #include "Config.h" #include "Driver.h" #include "LTO.h" +#include "PDB.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" +#include "lld/Common/Timer.h" #include "llvm/IR/LLVMContext.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" @@ -24,6 +26,8 @@ using namespace llvm; namespace lld { namespace coff { +static Timer LTOTimer("LTO", Timer::root()); + SymbolTable *Symtab; void SymbolTable::addFile(InputFile *File) { @@ -34,8 +38,9 @@ void SymbolTable::addFile(InputFile *File) { if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) { Config->Machine = MT; } else if (MT != IMAGE_FILE_MACHINE_UNKNOWN && Config->Machine != MT) { - fatal(toString(File) + ": machine type " + machineToStr(MT) + + error(toString(File) + ": machine type " + machineToStr(MT) + " conflicts with " + machineToStr(Config->Machine)); + return; } if (auto *F = dyn_cast<ObjFile>(File)) { @@ -61,6 +66,66 @@ static void errorOrWarn(const Twine &S) { error(S); } +// Returns the name of the symbol in SC whose value is <= Addr that is closest +// to Addr. This is generally the name of the global variable or function whose +// definition contains Addr. +static StringRef getSymbolName(SectionChunk *SC, uint32_t Addr) { + DefinedRegular *Candidate = nullptr; + + for (Symbol *S : SC->File->getSymbols()) { + auto *D = dyn_cast_or_null<DefinedRegular>(S); + if (!D || D->getChunk() != SC || D->getValue() > Addr || + (Candidate && D->getValue() < Candidate->getValue())) + continue; + + Candidate = D; + } + + if (!Candidate) + return ""; + return Candidate->getName(); +} + +static std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex) { + struct Location { + StringRef SymName; + std::pair<StringRef, uint32_t> FileLine; + }; + std::vector<Location> Locations; + + for (Chunk *C : File->getChunks()) { + auto *SC = dyn_cast<SectionChunk>(C); + if (!SC) + continue; + for (const coff_relocation &R : SC->Relocs) { + if (R.SymbolTableIndex != SymIndex) + continue; + std::pair<StringRef, uint32_t> FileLine = + getFileLine(SC, R.VirtualAddress); + StringRef SymName = getSymbolName(SC, R.VirtualAddress); + if (!FileLine.first.empty() || !SymName.empty()) + Locations.push_back({SymName, FileLine}); + } + } + + if (Locations.empty()) + return "\n>>> referenced by " + toString(File) + "\n"; + + std::string Out; + llvm::raw_string_ostream OS(Out); + for (Location Loc : Locations) { + OS << "\n>>> referenced by "; + if (!Loc.FileLine.first.empty()) + OS << Loc.FileLine.first << ":" << Loc.FileLine.second + << "\n>>> "; + OS << toString(File); + if (!Loc.SymName.empty()) + OS << ":(" << Loc.SymName << ')'; + } + OS << '\n'; + return OS.str(); +} + void SymbolTable::reportRemainingUndefines() { SmallPtrSet<Symbol *, 8> Undefs; DenseMap<Symbol *, Symbol *> LocalImports; @@ -120,20 +185,23 @@ void SymbolTable::reportRemainingUndefines() { if (Config->WarnLocallyDefinedImported) if (Symbol *Imp = LocalImports.lookup(B)) warn("<root>: locally defined symbol imported: " + Imp->getName() + - " (defined in " + toString(Imp->getFile()) + ")"); + " (defined in " + toString(Imp->getFile()) + ") [LNK4217]"); } for (ObjFile *File : ObjFile::Instances) { + size_t SymIndex = (size_t)-1; for (Symbol *Sym : File->getSymbols()) { + ++SymIndex; if (!Sym) continue; if (Undefs.count(Sym)) - errorOrWarn(toString(File) + ": undefined symbol: " + Sym->getName()); + errorOrWarn("undefined symbol: " + Sym->getName() + + getSymbolLocations(File, SymIndex)); if (Config->WarnLocallyDefinedImported) if (Symbol *Imp = LocalImports.lookup(Sym)) warn(toString(File) + ": locally defined symbol imported: " + Imp->getName() + " (defined in " + toString(Imp->getFile()) + - ")"); + ") [LNK4217]"); } } } @@ -142,7 +210,7 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) { Symbol *&Sym = SymMap[CachedHashStringRef(Name)]; if (Sym) return {Sym, false}; - Sym = (Symbol *)make<SymbolUnion>(); + Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>()); Sym->IsUsedInRegularObj = false; Sym->PendingArchiveLoad = false; return {Sym, true}; @@ -274,30 +342,29 @@ Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size, return S; } -DefinedImportData *SymbolTable::addImportData(StringRef N, ImportFile *F) { +Symbol *SymbolTable::addImportData(StringRef N, ImportFile *F) { Symbol *S; bool WasInserted; std::tie(S, WasInserted) = insert(N); S->IsUsedInRegularObj = true; if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) { replaceSymbol<DefinedImportData>(S, N, F); - return cast<DefinedImportData>(S); + return S; } reportDuplicate(S, F); return nullptr; } -DefinedImportThunk *SymbolTable::addImportThunk(StringRef Name, - DefinedImportData *ID, - uint16_t Machine) { +Symbol *SymbolTable::addImportThunk(StringRef Name, DefinedImportData *ID, + uint16_t Machine) { Symbol *S; bool WasInserted; std::tie(S, WasInserted) = insert(Name); S->IsUsedInRegularObj = true; if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) { replaceSymbol<DefinedImportThunk>(S, Name, ID, Machine); - return cast<DefinedImportThunk>(S); + return S; } reportDuplicate(S, ID->File); @@ -314,10 +381,7 @@ std::vector<Chunk *> SymbolTable::getChunks() { } Symbol *SymbolTable::find(StringRef Name) { - auto It = SymMap.find(CachedHashStringRef(Name)); - if (It == SymMap.end()) - return nullptr; - return It->second; + return SymMap.lookup(CachedHashStringRef(Name)); } Symbol *SymbolTable::findUnderscore(StringRef Name) { @@ -384,6 +448,8 @@ std::vector<StringRef> SymbolTable::compileBitcodeFiles() { void SymbolTable::addCombinedLTOObjects() { if (BitcodeFile::Instances.empty()) return; + + ScopedTimer T(LTOTimer); for (StringRef Object : compileBitcodeFiles()) { auto *Obj = make<ObjFile>(MemoryBufferRef(Object, "lto.tmp")); Obj->parse(); diff --git a/contrib/llvm/tools/lld/COFF/SymbolTable.h b/contrib/llvm/tools/lld/COFF/SymbolTable.h index 55481e6475bb..30cb1a5410c3 100644 --- a/contrib/llvm/tools/lld/COFF/SymbolTable.h +++ b/contrib/llvm/tools/lld/COFF/SymbolTable.h @@ -92,9 +92,9 @@ public: Symbol *addCommon(InputFile *F, StringRef N, uint64_t Size, const llvm::object::coff_symbol_generic *S = nullptr, CommonChunk *C = nullptr); - DefinedImportData *addImportData(StringRef N, ImportFile *F); - DefinedImportThunk *addImportThunk(StringRef Name, DefinedImportData *S, - uint16_t Machine); + Symbol *addImportData(StringRef N, ImportFile *F); + Symbol *addImportThunk(StringRef Name, DefinedImportData *S, + uint16_t Machine); void reportDuplicate(Symbol *Existing, InputFile *NewFile); diff --git a/contrib/llvm/tools/lld/COFF/Symbols.cpp b/contrib/llvm/tools/lld/COFF/Symbols.cpp index 4c5ab48c7565..7c8b7d5e8fc5 100644 --- a/contrib/llvm/tools/lld/COFF/Symbols.cpp +++ b/contrib/llvm/tools/lld/COFF/Symbols.cpp @@ -9,9 +9,9 @@ #include "Symbols.h" #include "InputFiles.h" -#include "Strings.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" +#include "lld/Common/Strings.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" @@ -21,7 +21,7 @@ using namespace llvm::object; // Returns a symbol name for an error message. std::string lld::toString(coff::Symbol &B) { - if (Optional<std::string> S = coff::demangleMSVC(B.getName())) + if (Optional<std::string> S = lld::demangleMSVC(B.getName())) return ("\"" + *S + "\" (" + B.getName() + ")").str(); return B.getName(); } @@ -58,7 +58,7 @@ bool Symbol::isLive() const { if (auto *Imp = dyn_cast<DefinedImportData>(this)) return Imp->File->Live; if (auto *Imp = dyn_cast<DefinedImportThunk>(this)) - return Imp->WrappedSym->File->Live; + return Imp->WrappedSym->File->ThunkLive; // Assume any other kind of symbol is live. return true; } @@ -71,7 +71,7 @@ COFFSymbolRef DefinedCOFF::getCOFFSymbol() { return COFFSymbolRef(reinterpret_cast<const coff_symbol32 *>(Sym)); } -uint16_t DefinedAbsolute::OutputSectionIndex = 0; +uint16_t DefinedAbsolute::NumOutputSections; static Chunk *makeImportThunk(DefinedImportData *S, uint16_t Machine) { if (Machine == AMD64) diff --git a/contrib/llvm/tools/lld/COFF/Symbols.h b/contrib/llvm/tools/lld/COFF/Symbols.h index d8a030705e27..783965adbd9a 100644 --- a/contrib/llvm/tools/lld/COFF/Symbols.h +++ b/contrib/llvm/tools/lld/COFF/Symbols.h @@ -213,11 +213,10 @@ public: uint64_t getRVA() { return VA - Config->ImageBase; } void setVA(uint64_t V) { VA = V; } - // The sentinel absolute symbol section index. Section index relocations - // against absolute symbols resolve to this 16 bit number, and it is the - // largest valid section index plus one. This is written by the Writer. - static uint16_t OutputSectionIndex; - uint16_t getSecIdx() { return OutputSectionIndex; } + // Section index relocations against absolute symbols resolve to + // this 16 bit number, and it is the largest valid section index + // plus one. This variable keeps it. + static uint16_t NumOutputSections; private: uint64_t VA; @@ -416,6 +415,8 @@ union SymbolUnion { template <typename T, typename... ArgT> void replaceSymbol(Symbol *S, ArgT &&... Arg) { + static_assert(std::is_trivially_destructible<T>(), + "Symbol types must be trivially destructible"); static_assert(sizeof(T) <= sizeof(SymbolUnion), "Symbol too small"); static_assert(alignof(T) <= alignof(SymbolUnion), "SymbolUnion not aligned enough"); diff --git a/contrib/llvm/tools/lld/COFF/Writer.cpp b/contrib/llvm/tools/lld/COFF/Writer.cpp index 584f0621bea3..d17405ec26ab 100644 --- a/contrib/llvm/tools/lld/COFF/Writer.cpp +++ b/contrib/llvm/tools/lld/COFF/Writer.cpp @@ -17,6 +17,7 @@ #include "Symbols.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" +#include "lld/Common/Timer.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSwitch.h" @@ -25,7 +26,9 @@ #include "llvm/Support/Endian.h" #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/Parallel.h" +#include "llvm/Support/Path.h" #include "llvm/Support/RandomNumberGenerator.h" +#include "llvm/Support/xxhash.h" #include <algorithm> #include <cstdio> #include <map> @@ -40,8 +43,40 @@ using namespace llvm::support::endian; using namespace lld; using namespace lld::coff; +/* To re-generate DOSProgram: +$ cat > /tmp/DOSProgram.asm +org 0 + ; Copy cs to ds. + push cs + pop ds + ; Point ds:dx at the $-terminated string. + mov dx, str + ; Int 21/AH=09h: Write string to standard output. + mov ah, 0x9 + int 0x21 + ; Int 21/AH=4Ch: Exit with return code (in AL). + mov ax, 0x4C01 + int 0x21 +str: + db 'This program cannot be run in DOS mode.$' +align 8, db 0 +$ nasm -fbin /tmp/DOSProgram.asm -o /tmp/DOSProgram.bin +$ xxd -i /tmp/DOSProgram.bin +*/ +static unsigned char DOSProgram[] = { + 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, + 0xcd, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, + 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, + 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, + 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x24, 0x00, 0x00 +}; +static_assert(sizeof(DOSProgram) % 8 == 0, + "DOSProgram size must be multiple of 8"); + static const int SectorSize = 512; -static const int DOSStubSize = 64; +static const int DOSStubSize = sizeof(dos_header) + sizeof(DOSProgram); +static_assert(DOSStubSize % 8 == 0, "DOSStub size must be multiple of 8"); + static const int NumberfOfDataDirectory = 16; namespace { @@ -69,24 +104,25 @@ public: uint64_t Offs = OS->getFileOff() + (Record->getRVA() - OS->getRVA()); D->PointerToRawData = Offs; + TimeDateStamps.push_back(&D->TimeDateStamp); ++D; } } + void setTimeDateStamp(uint32_t TimeDateStamp) { + for (support::ulittle32_t *TDS : TimeDateStamps) + *TDS = TimeDateStamp; + } + private: + mutable std::vector<support::ulittle32_t *> TimeDateStamps; const std::vector<Chunk *> &Records; }; class CVDebugRecordChunk : public Chunk { public: - CVDebugRecordChunk() { - PDBAbsPath = Config->PDBPath; - if (!PDBAbsPath.empty()) - llvm::sys::fs::make_absolute(PDBAbsPath); - } - size_t getSize() const override { - return sizeof(codeview::DebugInfo) + PDBAbsPath.size() + 1; + return sizeof(codeview::DebugInfo) + Config->PDBAltPath.size() + 1; } void writeTo(uint8_t *B) const override { @@ -96,12 +132,11 @@ public: // variable sized field (PDB Path) char *P = reinterpret_cast<char *>(B + OutputSectionOff + sizeof(*BuildId)); - if (!PDBAbsPath.empty()) - memcpy(P, PDBAbsPath.data(), PDBAbsPath.size()); - P[PDBAbsPath.size()] = '\0'; + if (!Config->PDBAltPath.empty()) + memcpy(P, Config->PDBAltPath.data(), Config->PDBAltPath.size()); + P[Config->PDBAltPath.size()] = '\0'; } - SmallString<128> PDBAbsPath; mutable codeview::DebugInfo *BuildId = nullptr; }; @@ -116,12 +151,19 @@ private: void createMiscChunks(); void createImportTables(); void createExportTable(); + void mergeSections(); void assignAddresses(); void removeEmptySections(); void createSymbolAndStringTable(); void openFile(StringRef OutputPath); template <typename PEHeaderTy> void writeHeader(); - void createSEHTable(OutputSection *RData); + void createSEHTable(); + void createGuardCFTables(); + void markSymbolsForRVATable(ObjFile *File, + ArrayRef<SectionChunk *> SymIdxChunks, + SymbolRVASet &TableSymbols); + void maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym, + StringRef CountSym); void setSectionPermissions(); void writeSections(); void writeBuildId(); @@ -131,9 +173,8 @@ private: size_t addEntryToStringTable(StringRef Str); OutputSection *findSection(StringRef Name); - OutputSection *createSection(StringRef Name); - void addBaserels(OutputSection *Dest); - void addBaserelBlocks(OutputSection *Dest, std::vector<Baserel> &V); + void addBaserels(); + void addBaserelBlocks(std::vector<Baserel> &V); uint32_t getSizeOfInitializedData(); std::map<StringRef, std::vector<DefinedImportData *>> binImports(); @@ -145,9 +186,9 @@ private: IdataContents Idata; DelayLoadContents DelayIdata; EdataContents Edata; - SEHTableChunk *SEHTable = nullptr; + bool SetNoSEHCharacteristic = false; - Chunk *DebugDirectory = nullptr; + DebugDirectoryChunk *DebugDirectory = nullptr; std::vector<Chunk *> DebugRecords; CVDebugRecordChunk *BuildId = nullptr; Optional<codeview::DebugInfo> PreviousBuildId; @@ -157,50 +198,55 @@ private: uint32_t PointerToSymbolTable = 0; uint64_t SizeOfImage; uint64_t SizeOfHeaders; + + OutputSection *TextSec; + OutputSection *RdataSec; + OutputSection *BuildidSec; + OutputSection *DataSec; + OutputSection *PdataSec; + OutputSection *IdataSec; + OutputSection *EdataSec; + OutputSection *DidatSec; + OutputSection *RsrcSec; + OutputSection *RelocSec; + + // The first and last .pdata sections in the output file. + // + // We need to keep track of the location of .pdata in whichever section it + // gets merged into so that we can sort its contents and emit a correct data + // directory entry for the exception table. This is also the case for some + // other sections (such as .edata) but because the contents of those sections + // are entirely linker-generated we can keep track of their locations using + // the chunks that the linker creates. All .pdata chunks come from input + // files, so we need to keep track of them separately. + Chunk *FirstPdata = nullptr; + Chunk *LastPdata; }; } // anonymous namespace namespace lld { namespace coff { -void writeResult() { Writer().run(); } - -void OutputSection::setRVA(uint64_t RVA) { - Header.VirtualAddress = RVA; - for (Chunk *C : Chunks) - C->setRVA(C->getRVA() + RVA); -} +static Timer CodeLayoutTimer("Code Layout", Timer::root()); +static Timer DiskCommitTimer("Commit Output File", Timer::root()); -void OutputSection::setFileOffset(uint64_t Off) { - // If a section has no actual data (i.e. BSS section), we want to - // set 0 to its PointerToRawData. Otherwise the output is rejected - // by the loader. - if (Header.SizeOfRawData == 0) - return; - Header.PointerToRawData = Off; -} +void writeResult() { Writer().run(); } void OutputSection::addChunk(Chunk *C) { Chunks.push_back(C); C->setOutputSection(this); - uint64_t Off = Header.VirtualSize; - Off = alignTo(Off, C->Alignment); - C->setRVA(Off); - C->OutputSectionOff = Off; - Off += C->getSize(); - if (Off > UINT32_MAX) - error("section larger than 4 GiB: " + Name); - Header.VirtualSize = Off; - if (C->hasData()) - Header.SizeOfRawData = alignTo(Off, SectorSize); } -void OutputSection::addPermissions(uint32_t C) { - Header.Characteristics |= C & PermMask; +void OutputSection::setPermissions(uint32_t C) { + Header.Characteristics &= ~PermMask; + Header.Characteristics |= C; } -void OutputSection::setPermissions(uint32_t C) { - Header.Characteristics = C & PermMask; +void OutputSection::merge(OutputSection *Other) { + for (Chunk *C : Other->Chunks) + C->setOutputSection(this); + Chunks.insert(Chunks.end(), Other->Chunks.begin(), Other->Chunks.end()); + Other->Chunks.clear(); } // Write the section header to a given buffer. @@ -284,17 +330,22 @@ static Optional<codeview::DebugInfo> loadExistingBuildId(StringRef Path) { // The main function of the writer. void Writer::run() { + ScopedTimer T1(CodeLayoutTimer); + createSections(); createMiscChunks(); createImportTables(); createExportTable(); - if (Config->Relocatable) - createSection(".reloc"); + mergeSections(); assignAddresses(); removeEmptySections(); setSectionPermissions(); createSymbolAndStringTable(); + if (FileSize > UINT32_MAX) + fatal("image size (" + Twine(FileSize) + ") " + + "exceeds maximum allowable size (" + Twine(UINT32_MAX) + ")"); + // We must do this before opening the output file, as it depends on being able // to read the contents of the existing output file. PreviousBuildId = loadExistingBuildId(Config->OutputFile); @@ -308,35 +359,79 @@ void Writer::run() { sortExceptionTable(); writeBuildId(); - if (!Config->PDBPath.empty() && Config->Debug) { + T1.stop(); + if (!Config->PDBPath.empty() && Config->Debug) { assert(BuildId); createPDB(Symtab, OutputSections, SectionTable, *BuildId->BuildId); } writeMapFile(OutputSections); + ScopedTimer T2(DiskCommitTimer); if (auto E = Buffer->commit()) fatal("failed to write the output file: " + toString(std::move(E))); } -static StringRef getOutputSection(StringRef Name) { +static StringRef getOutputSectionName(StringRef Name) { StringRef S = Name.split('$').first; // Treat a later period as a separator for MinGW, for sections like // ".ctors.01234". - S = S.substr(0, S.find('.', 1)); + return S.substr(0, S.find('.', 1)); +} - auto It = Config->Merge.find(S); - if (It == Config->Merge.end()) - return S; - return It->second; +// For /order. +static void sortBySectionOrder(std::vector<Chunk *> &Chunks) { + auto GetPriority = [](const Chunk *C) { + if (auto *Sec = dyn_cast<SectionChunk>(C)) + if (Sec->Sym) + return Config->Order.lookup(Sec->Sym->getName()); + return 0; + }; + + std::stable_sort(Chunks.begin(), Chunks.end(), + [=](const Chunk *A, const Chunk *B) { + return GetPriority(A) < GetPriority(B); + }); } // Create output section objects and add them to OutputSections. void Writer::createSections() { - // First, bin chunks by name. - std::map<StringRef, std::vector<Chunk *>> Map; + // First, create the builtin sections. + const uint32_t DATA = IMAGE_SCN_CNT_INITIALIZED_DATA; + const uint32_t BSS = IMAGE_SCN_CNT_UNINITIALIZED_DATA; + const uint32_t CODE = IMAGE_SCN_CNT_CODE; + const uint32_t DISCARDABLE = IMAGE_SCN_MEM_DISCARDABLE; + const uint32_t R = IMAGE_SCN_MEM_READ; + const uint32_t W = IMAGE_SCN_MEM_WRITE; + const uint32_t X = IMAGE_SCN_MEM_EXECUTE; + + SmallDenseMap<std::pair<StringRef, uint32_t>, OutputSection *> Sections; + auto CreateSection = [&](StringRef Name, uint32_t OutChars) { + OutputSection *&Sec = Sections[{Name, OutChars}]; + if (!Sec) { + Sec = make<OutputSection>(Name, OutChars); + OutputSections.push_back(Sec); + } + return Sec; + }; + + // Try to match the section order used by link.exe. + TextSec = CreateSection(".text", CODE | R | X); + CreateSection(".bss", BSS | R | W); + RdataSec = CreateSection(".rdata", DATA | R); + BuildidSec = CreateSection(".buildid", DATA | R); + DataSec = CreateSection(".data", DATA | R | W); + PdataSec = CreateSection(".pdata", DATA | R); + IdataSec = CreateSection(".idata", DATA | R); + EdataSec = CreateSection(".edata", DATA | R); + DidatSec = CreateSection(".didat", DATA | R); + RsrcSec = CreateSection(".rsrc", DATA | R); + RelocSec = CreateSection(".reloc", DATA | DISCARDABLE | R); + + // Then bin chunks by name and output characteristics. + std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> Map; for (Chunk *C : Symtab->getChunks()) { auto *SC = dyn_cast<SectionChunk>(C); if (SC && !SC->isLive()) { @@ -344,42 +439,71 @@ void Writer::createSections() { SC->printDiscardedMessage(); continue; } - Map[C->getSectionName()].push_back(C); + Map[{C->getSectionName(), C->getOutputCharacteristics()}].push_back(C); } + // Process an /order option. + if (!Config->Order.empty()) + for (auto &Pair : Map) + sortBySectionOrder(Pair.second); + // Then create an OutputSection for each section. // '$' and all following characters in input section names are // discarded when determining output section. So, .text$foo // contributes to .text, for example. See PE/COFF spec 3.2. - SmallDenseMap<StringRef, OutputSection *> Sections; for (auto Pair : Map) { - StringRef Name = getOutputSection(Pair.first); - OutputSection *&Sec = Sections[Name]; - if (!Sec) { - Sec = make<OutputSection>(Name); - OutputSections.push_back(Sec); - } + StringRef Name = getOutputSectionName(Pair.first.first); + uint32_t OutChars = Pair.first.second; + + // In link.exe, there is a special case for the I386 target where .CRT + // sections are treated as if they have output characteristics DATA | R if + // their characteristics are DATA | R | W. This implements the same special + // case for all architectures. + if (Name == ".CRT") + OutChars = DATA | R; + + OutputSection *Sec = CreateSection(Name, OutChars); std::vector<Chunk *> &Chunks = Pair.second; - for (Chunk *C : Chunks) { + for (Chunk *C : Chunks) Sec->addChunk(C); - Sec->addPermissions(C->getPermissions()); - } } + + // Finally, move some output sections to the end. + auto SectionOrder = [&](OutputSection *S) { + // Move DISCARDABLE (or non-memory-mapped) sections to the end of file because + // the loader cannot handle holes. Stripping can remove other discardable ones + // than .reloc, which is first of them (created early). + if (S->Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) + return 2; + // .rsrc should come at the end of the non-discardable sections because its + // size may change by the Win32 UpdateResources() function, causing + // subsequent sections to move (see https://crbug.com/827082). + if (S == RsrcSec) + return 1; + return 0; + }; + std::stable_sort(OutputSections.begin(), OutputSections.end(), + [&](OutputSection *S, OutputSection *T) { + return SectionOrder(S) < SectionOrder(T); + }); } void Writer::createMiscChunks() { - OutputSection *RData = createSection(".rdata"); + for (auto &P : MergeChunk::Instances) + RdataSec->addChunk(P.second); // Create thunks for locally-dllimported symbols. if (!Symtab->LocalImportChunks.empty()) { for (Chunk *C : Symtab->LocalImportChunks) - RData->addChunk(C); + RdataSec->addChunk(C); } // Create Debug Information Chunks if (Config->Debug) { DebugDirectory = make<DebugDirectoryChunk>(DebugRecords); + OutputSection *DebugInfoSec = Config->MinGW ? BuildidSec : RdataSec; + // Make a CVDebugRecordChunk even when /DEBUG:CV is not specified. We // output a PDB no matter what, and this chunk provides the only means of // allowing a debugger to match a PDB and an executable. So we need it even @@ -388,12 +512,18 @@ void Writer::createMiscChunks() { BuildId = CVChunk; DebugRecords.push_back(CVChunk); - RData->addChunk(DebugDirectory); + DebugInfoSec->addChunk(DebugDirectory); for (Chunk *C : DebugRecords) - RData->addChunk(C); + DebugInfoSec->addChunk(C); } - createSEHTable(RData); + // Create SEH table. x86-only. + if (Config->Machine == I386) + createSEHTable(); + + // Create /guard:cf tables if requested. + if (Config->GuardCF != GuardCFLevel::Off) + createGuardCFTables(); } // Create .idata section for the DLL-imported symbol table. @@ -414,53 +544,49 @@ void Writer::createImportTables() { std::string DLL = StringRef(File->DLLName).lower(); if (Config->DLLOrder.count(DLL) == 0) Config->DLLOrder[DLL] = Config->DLLOrder.size(); - } - OutputSection *Text = createSection(".text"); - for (ImportFile *File : ImportFile::Instances) { - if (!File->Live) - continue; - - if (DefinedImportThunk *Thunk = File->ThunkSym) - Text->addChunk(Thunk->getChunk()); + if (File->ThunkSym) { + if (!isa<DefinedImportThunk>(File->ThunkSym)) + fatal(toString(*File->ThunkSym) + " was replaced"); + DefinedImportThunk *Thunk = cast<DefinedImportThunk>(File->ThunkSym); + if (File->ThunkLive) + TextSec->addChunk(Thunk->getChunk()); + } + if (File->ImpSym && !isa<DefinedImportData>(File->ImpSym)) + fatal(toString(*File->ImpSym) + " was replaced"); + DefinedImportData *ImpSym = cast_or_null<DefinedImportData>(File->ImpSym); if (Config->DelayLoads.count(StringRef(File->DLLName).lower())) { if (!File->ThunkSym) fatal("cannot delay-load " + toString(File) + - " due to import of data: " + toString(*File->ImpSym)); - DelayIdata.add(File->ImpSym); + " due to import of data: " + toString(*ImpSym)); + DelayIdata.add(ImpSym); } else { - Idata.add(File->ImpSym); + Idata.add(ImpSym); } } - if (!Idata.empty()) { - OutputSection *Sec = createSection(".idata"); + if (!Idata.empty()) for (Chunk *C : Idata.getChunks()) - Sec->addChunk(C); - } + IdataSec->addChunk(C); if (!DelayIdata.empty()) { Defined *Helper = cast<Defined>(Config->DelayLoadHelper); DelayIdata.create(Helper); - OutputSection *Sec = createSection(".didat"); for (Chunk *C : DelayIdata.getChunks()) - Sec->addChunk(C); - Sec = createSection(".data"); + DidatSec->addChunk(C); for (Chunk *C : DelayIdata.getDataChunks()) - Sec->addChunk(C); - Sec = createSection(".text"); + DataSec->addChunk(C); for (Chunk *C : DelayIdata.getCodeChunks()) - Sec->addChunk(C); + TextSec->addChunk(C); } } void Writer::createExportTable() { if (Config->Exports.empty()) return; - OutputSection *Sec = createSection(".edata"); for (Chunk *C : Edata.Chunks) - Sec->addChunk(C); + EdataSec->addChunk(C); } // The Windows loader doesn't seem to like empty sections, @@ -484,19 +610,31 @@ size_t Writer::addEntryToStringTable(StringRef Str) { } Optional<coff_symbol16> Writer::createSymbol(Defined *Def) { - // Relative symbols are unrepresentable in a COFF symbol table. - if (isa<DefinedSynthetic>(Def)) - return None; - - // Don't write dead symbols or symbols in codeview sections to the symbol - // table. - if (!Def->isLive()) + coff_symbol16 Sym; + switch (Def->kind()) { + case Symbol::DefinedAbsoluteKind: + Sym.Value = Def->getRVA(); + Sym.SectionNumber = IMAGE_SYM_ABSOLUTE; + break; + case Symbol::DefinedSyntheticKind: + // Relative symbols are unrepresentable in a COFF symbol table. return None; - if (auto *D = dyn_cast<DefinedRegular>(Def)) - if (D->getChunk()->isCodeView()) + default: { + // Don't write symbols that won't be written to the output to the symbol + // table. + Chunk *C = Def->getChunk(); + if (!C) + return None; + OutputSection *OS = C->getOutputSection(); + if (!OS) return None; - coff_symbol16 Sym; + Sym.Value = Def->getRVA() - OS->getRVA(); + Sym.SectionNumber = OS->SectionIndex; + break; + } + } + StringRef Name = Def->getName(); if (Name.size() > COFF::NameSize) { Sym.Name.Offset.Zeroes = 0; @@ -515,46 +653,27 @@ Optional<coff_symbol16> Writer::createSymbol(Defined *Def) { Sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; } Sym.NumberOfAuxSymbols = 0; - - switch (Def->kind()) { - case Symbol::DefinedAbsoluteKind: - Sym.Value = Def->getRVA(); - Sym.SectionNumber = IMAGE_SYM_ABSOLUTE; - break; - default: { - uint64_t RVA = Def->getRVA(); - OutputSection *Sec = nullptr; - for (OutputSection *S : OutputSections) { - if (S->getRVA() > RVA) - break; - Sec = S; - } - Sym.Value = RVA - Sec->getRVA(); - Sym.SectionNumber = Sec->SectionIndex; - break; - } - } return Sym; } void Writer::createSymbolAndStringTable() { - // Name field in the section table is 8 byte long. Longer names need - // to be written to the string table. First, construct string table. + // PE/COFF images are limited to 8 byte section names. Longer names can be + // supported by writing a non-standard string table, but this string table is + // not mapped at runtime and the long names will therefore be inaccessible. + // link.exe always truncates section names to 8 bytes, whereas binutils always + // preserves long section names via the string table. LLD adopts a hybrid + // solution where discardable sections have long names preserved and + // non-discardable sections have their names truncated, to ensure that any + // section which is mapped at runtime also has its name mapped at runtime. for (OutputSection *Sec : OutputSections) { - StringRef Name = Sec->getName(); - if (Name.size() <= COFF::NameSize) + if (Sec->Name.size() <= COFF::NameSize) continue; - // If a section isn't discardable (i.e. will be mapped at runtime), - // prefer a truncated section name over a long section name in - // the string table that is unavailable at runtime. This is different from - // what link.exe does, but finding ".eh_fram" instead of "/4" is useful - // to libunwind. - if ((Sec->getPermissions() & IMAGE_SCN_MEM_DISCARDABLE) == 0) + if ((Sec->Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) continue; - Sec->setStringTableOff(addEntryToStringTable(Name)); + Sec->setStringTableOff(addEntryToStringTable(Sec->Name)); } - if (Config->DebugDwarf) { + if (Config->DebugDwarf || Config->DebugSymtab) { for (ObjFile *File : ObjFile::Instances) { for (Symbol *B : File->getSymbols()) { auto *D = dyn_cast_or_null<Defined>(B); @@ -571,16 +690,45 @@ void Writer::createSymbolAndStringTable() { if (OutputSymtab.empty() && Strtab.empty()) return; - OutputSection *LastSection = OutputSections.back(); // We position the symbol table to be adjacent to the end of the last section. - uint64_t FileOff = LastSection->getFileOff() + - alignTo(LastSection->getRawSize(), SectorSize); + uint64_t FileOff = FileSize; PointerToSymbolTable = FileOff; FileOff += OutputSymtab.size() * sizeof(coff_symbol16); FileOff += 4 + Strtab.size(); FileSize = alignTo(FileOff, SectorSize); } +void Writer::mergeSections() { + if (!PdataSec->getChunks().empty()) { + FirstPdata = PdataSec->getChunks().front(); + LastPdata = PdataSec->getChunks().back(); + } + + for (auto &P : Config->Merge) { + StringRef ToName = P.second; + if (P.first == ToName) + continue; + StringSet<> Names; + while (1) { + if (!Names.insert(ToName).second) + fatal("/merge: cycle found for section '" + P.first + "'"); + auto I = Config->Merge.find(ToName); + if (I == Config->Merge.end()) + break; + ToName = I->second; + } + OutputSection *From = findSection(P.first); + OutputSection *To = findSection(ToName); + if (!From) + continue; + if (!To) { + From->Name = ToName; + continue; + } + To->merge(From); + } +} + // Visits all sections to assign incremental, non-overlapping RVAs and // file offsets. void Writer::assignAddresses() { @@ -590,35 +738,57 @@ void Writer::assignAddresses() { SizeOfHeaders += Config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header); SizeOfHeaders = alignTo(SizeOfHeaders, SectorSize); - uint64_t RVA = 0x1000; // The first page is kept unmapped. + uint64_t RVA = PageSize; // The first page is kept unmapped. FileSize = SizeOfHeaders; - // Move DISCARDABLE (or non-memory-mapped) sections to the end of file because - // the loader cannot handle holes. - std::stable_partition( - OutputSections.begin(), OutputSections.end(), [](OutputSection *S) { - return (S->getPermissions() & IMAGE_SCN_MEM_DISCARDABLE) == 0; - }); + for (OutputSection *Sec : OutputSections) { - if (Sec->getName() == ".reloc") - addBaserels(Sec); - Sec->setRVA(RVA); - Sec->setFileOffset(FileSize); - RVA += alignTo(Sec->getVirtualSize(), PageSize); - FileSize += alignTo(Sec->getRawSize(), SectorSize); + if (Sec == RelocSec) + addBaserels(); + uint64_t RawSize = 0, VirtualSize = 0; + Sec->Header.VirtualAddress = RVA; + for (Chunk *C : Sec->getChunks()) { + VirtualSize = alignTo(VirtualSize, C->Alignment); + C->setRVA(RVA + VirtualSize); + C->OutputSectionOff = VirtualSize; + C->finalizeContents(); + VirtualSize += C->getSize(); + if (C->hasData()) + RawSize = alignTo(VirtualSize, SectorSize); + } + if (VirtualSize > UINT32_MAX) + error("section larger than 4 GiB: " + Sec->Name); + Sec->Header.VirtualSize = VirtualSize; + Sec->Header.SizeOfRawData = RawSize; + if (RawSize != 0) + Sec->Header.PointerToRawData = FileSize; + RVA += alignTo(VirtualSize, PageSize); + FileSize += alignTo(RawSize, SectorSize); } SizeOfImage = alignTo(RVA, PageSize); } template <typename PEHeaderTy> void Writer::writeHeader() { - // Write DOS stub + // Write DOS header. For backwards compatibility, the first part of a PE/COFF + // executable consists of an MS-DOS MZ executable. If the executable is run + // under DOS, that program gets run (usually to just print an error message). + // When run under Windows, the loader looks at AddressOfNewExeHeader and uses + // the PE header instead. uint8_t *Buf = Buffer->getBufferStart(); auto *DOS = reinterpret_cast<dos_header *>(Buf); - Buf += DOSStubSize; + Buf += sizeof(dos_header); DOS->Magic[0] = 'M'; DOS->Magic[1] = 'Z'; + DOS->UsedBytesInTheLastPage = DOSStubSize % 512; + DOS->FileSizeInPages = divideCeil(DOSStubSize, 512); + DOS->HeaderSizeInParagraphs = sizeof(dos_header) / 16; + DOS->AddressOfRelocationTable = sizeof(dos_header); DOS->AddressOfNewExeHeader = DOSStubSize; + // Write DOS program. + memcpy(Buf, DOSProgram, sizeof(DOSProgram)); + Buf += sizeof(DOSProgram); + // Write PE magic memcpy(Buf, PEMagic, sizeof(PEMagic)); Buf += sizeof(PEMagic); @@ -688,24 +858,27 @@ template <typename PEHeaderTy> void Writer::writeHeader() { PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT; if (!Config->AllowIsolation) PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION; - if (Config->Machine == I386 && !SEHTable && - !Symtab->findUnderscore("_load_config_used")) + if (Config->GuardCF != GuardCFLevel::Off) + PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_GUARD_CF; + if (Config->IntegrityCheck) + PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY; + if (SetNoSEHCharacteristic) PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH; if (Config->TerminalServerAware) PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE; PE->NumberOfRvaAndSize = NumberfOfDataDirectory; - if (OutputSection *Text = findSection(".text")) { - PE->BaseOfCode = Text->getRVA(); - PE->SizeOfCode = Text->getRawSize(); + if (TextSec->getVirtualSize()) { + PE->BaseOfCode = TextSec->getRVA(); + PE->SizeOfCode = TextSec->getRawSize(); } PE->SizeOfInitializedData = getSizeOfInitializedData(); // Write data directory auto *Dir = reinterpret_cast<data_directory *>(Buf); Buf += sizeof(*Dir) * NumberfOfDataDirectory; - if (OutputSection *Sec = findSection(".edata")) { - Dir[EXPORT_TABLE].RelativeVirtualAddress = Sec->getRVA(); - Dir[EXPORT_TABLE].Size = Sec->getVirtualSize(); + if (!Config->Exports.empty()) { + Dir[EXPORT_TABLE].RelativeVirtualAddress = Edata.getRVA(); + Dir[EXPORT_TABLE].Size = Edata.getSize(); } if (!Idata.empty()) { Dir[IMPORT_TABLE].RelativeVirtualAddress = Idata.getDirRVA(); @@ -713,17 +886,18 @@ template <typename PEHeaderTy> void Writer::writeHeader() { Dir[IAT].RelativeVirtualAddress = Idata.getIATRVA(); Dir[IAT].Size = Idata.getIATSize(); } - if (OutputSection *Sec = findSection(".rsrc")) { - Dir[RESOURCE_TABLE].RelativeVirtualAddress = Sec->getRVA(); - Dir[RESOURCE_TABLE].Size = Sec->getVirtualSize(); + if (RsrcSec->getVirtualSize()) { + Dir[RESOURCE_TABLE].RelativeVirtualAddress = RsrcSec->getRVA(); + Dir[RESOURCE_TABLE].Size = RsrcSec->getVirtualSize(); } - if (OutputSection *Sec = findSection(".pdata")) { - Dir[EXCEPTION_TABLE].RelativeVirtualAddress = Sec->getRVA(); - Dir[EXCEPTION_TABLE].Size = Sec->getVirtualSize(); + if (FirstPdata) { + Dir[EXCEPTION_TABLE].RelativeVirtualAddress = FirstPdata->getRVA(); + Dir[EXCEPTION_TABLE].Size = + LastPdata->getRVA() + LastPdata->getSize() - FirstPdata->getRVA(); } - if (OutputSection *Sec = findSection(".reloc")) { - Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = Sec->getRVA(); - Dir[BASE_RELOCATION_TABLE].Size = Sec->getVirtualSize(); + if (RelocSec->getVirtualSize()) { + Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = RelocSec->getRVA(); + Dir[BASE_RELOCATION_TABLE].Size = RelocSec->getVirtualSize(); } if (Symbol *Sym = Symtab->findUnderscore("_tls_used")) { if (Defined *B = dyn_cast<Defined>(Sym)) { @@ -792,35 +966,172 @@ void Writer::openFile(StringRef Path) { "failed to open " + Path); } -void Writer::createSEHTable(OutputSection *RData) { - // Create SEH table. x86-only. - if (Config->Machine != I386) - return; - - std::set<Defined *> Handlers; +void Writer::createSEHTable() { + // Set the no SEH characteristic on x86 binaries unless we find exception + // handlers. + SetNoSEHCharacteristic = true; + SymbolRVASet Handlers; for (ObjFile *File : ObjFile::Instances) { - if (!File->SEHCompat) + // FIXME: We should error here instead of earlier unless /safeseh:no was + // passed. + if (!File->hasSafeSEH()) return; - for (uint32_t I : File->SXData) - if (Symbol *B = File->getSymbol(I)) - if (B->isLive()) - Handlers.insert(cast<Defined>(B)); + + markSymbolsForRVATable(File, File->getSXDataChunks(), Handlers); + } + + // Remove the "no SEH" characteristic if all object files were built with + // safeseh, we found some exception handlers, and there is a load config in + // the object. + SetNoSEHCharacteristic = + Handlers.empty() || !Symtab->findUnderscore("_load_config_used"); + + maybeAddRVATable(std::move(Handlers), "__safe_se_handler_table", + "__safe_se_handler_count"); +} + +// Add a symbol to an RVA set. Two symbols may have the same RVA, but an RVA set +// cannot contain duplicates. Therefore, the set is uniqued by Chunk and the +// symbol's offset into that Chunk. +static void addSymbolToRVASet(SymbolRVASet &RVASet, Defined *S) { + Chunk *C = S->getChunk(); + if (auto *SC = dyn_cast<SectionChunk>(C)) + C = SC->Repl; // Look through ICF replacement. + uint32_t Off = S->getRVA() - (C ? C->getRVA() : 0); + RVASet.insert({C, Off}); +} + +// Visit all relocations from all section contributions of this object file and +// mark the relocation target as address-taken. +static void markSymbolsWithRelocations(ObjFile *File, + SymbolRVASet &UsedSymbols) { + for (Chunk *C : File->getChunks()) { + // We only care about live section chunks. Common chunks and other chunks + // don't generally contain relocations. + SectionChunk *SC = dyn_cast<SectionChunk>(C); + if (!SC || !SC->isLive()) + continue; + + // Look for relocations in this section against symbols in executable output + // sections. + for (Symbol *Ref : SC->symbols()) { + // FIXME: Do further testing to see if the relocation type matters, + // especially for 32-bit where taking the address of something usually + // uses an absolute relocation instead of a relative one. + if (auto *D = dyn_cast_or_null<Defined>(Ref)) { + Chunk *RefChunk = D->getChunk(); + OutputSection *OS = RefChunk ? RefChunk->getOutputSection() : nullptr; + if (OS && OS->Header.Characteristics & IMAGE_SCN_MEM_EXECUTE) + addSymbolToRVASet(UsedSymbols, D); + } + } + } +} + +// Create the guard function id table. This is a table of RVAs of all +// address-taken functions. It is sorted and uniqued, just like the safe SEH +// table. +void Writer::createGuardCFTables() { + SymbolRVASet AddressTakenSyms; + SymbolRVASet LongJmpTargets; + for (ObjFile *File : ObjFile::Instances) { + // If the object was compiled with /guard:cf, the address taken symbols + // are in .gfids$y sections, and the longjmp targets are in .gljmp$y + // sections. If the object was not compiled with /guard:cf, we assume there + // were no setjmp targets, and that all code symbols with relocations are + // possibly address-taken. + if (File->hasGuardCF()) { + markSymbolsForRVATable(File, File->getGuardFidChunks(), AddressTakenSyms); + markSymbolsForRVATable(File, File->getGuardLJmpChunks(), LongJmpTargets); + } else { + markSymbolsWithRelocations(File, AddressTakenSyms); + } + } + + // Mark the image entry as address-taken. + if (Config->Entry) + addSymbolToRVASet(AddressTakenSyms, cast<Defined>(Config->Entry)); + + // Ensure sections referenced in the gfid table are 16-byte aligned. + for (const ChunkAndOffset &C : AddressTakenSyms) + if (C.InputChunk->Alignment < 16) + C.InputChunk->Alignment = 16; + + maybeAddRVATable(std::move(AddressTakenSyms), "__guard_fids_table", + "__guard_fids_count"); + + // Add the longjmp target table unless the user told us not to. + if (Config->GuardCF == GuardCFLevel::Full) + maybeAddRVATable(std::move(LongJmpTargets), "__guard_longjmp_table", + "__guard_longjmp_count"); + + // Set __guard_flags, which will be used in the load config to indicate that + // /guard:cf was enabled. + uint32_t GuardFlags = uint32_t(coff_guard_flags::CFInstrumented) | + uint32_t(coff_guard_flags::HasFidTable); + if (Config->GuardCF == GuardCFLevel::Full) + GuardFlags |= uint32_t(coff_guard_flags::HasLongJmpTable); + Symbol *FlagSym = Symtab->findUnderscore("__guard_flags"); + cast<DefinedAbsolute>(FlagSym)->setVA(GuardFlags); +} + +// Take a list of input sections containing symbol table indices and add those +// symbols to an RVA table. The challenge is that symbol RVAs are not known and +// depend on the table size, so we can't directly build a set of integers. +void Writer::markSymbolsForRVATable(ObjFile *File, + ArrayRef<SectionChunk *> SymIdxChunks, + SymbolRVASet &TableSymbols) { + for (SectionChunk *C : SymIdxChunks) { + // Skip sections discarded by linker GC. This comes up when a .gfids section + // is associated with something like a vtable and the vtable is discarded. + // In this case, the associated gfids section is discarded, and we don't + // mark the virtual member functions as address-taken by the vtable. + if (!C->isLive()) + continue; + + // Validate that the contents look like symbol table indices. + ArrayRef<uint8_t> Data = C->getContents(); + if (Data.size() % 4 != 0) { + warn("ignoring " + C->getSectionName() + + " symbol table index section in object " + toString(File)); + continue; + } + + // Read each symbol table index and check if that symbol was included in the + // final link. If so, add it to the table symbol set. + ArrayRef<ulittle32_t> SymIndices( + reinterpret_cast<const ulittle32_t *>(Data.data()), Data.size() / 4); + ArrayRef<Symbol *> ObjSymbols = File->getSymbols(); + for (uint32_t SymIndex : SymIndices) { + if (SymIndex >= ObjSymbols.size()) { + warn("ignoring invalid symbol table index in section " + + C->getSectionName() + " in object " + toString(File)); + continue; + } + if (Symbol *S = ObjSymbols[SymIndex]) { + if (S->isLive()) + addSymbolToRVASet(TableSymbols, cast<Defined>(S)); + } + } } +} - if (Handlers.empty()) +// Replace the absolute table symbol with a synthetic symbol pointing to +// TableChunk so that we can emit base relocations for it and resolve section +// relative relocations. +void Writer::maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym, + StringRef CountSym) { + if (TableSymbols.empty()) return; - SEHTable = make<SEHTableChunk>(Handlers); - RData->addChunk(SEHTable); + RVATableChunk *TableChunk = make<RVATableChunk>(std::move(TableSymbols)); + RdataSec->addChunk(TableChunk); - // Replace the absolute table symbol with a synthetic symbol pointing to the - // SEHTable chunk so that we can emit base relocations for it and resolve - // section relative relocations. - Symbol *T = Symtab->find("___safe_se_handler_table"); - Symbol *C = Symtab->find("___safe_se_handler_count"); - replaceSymbol<DefinedSynthetic>(T, T->getName(), SEHTable); - cast<DefinedAbsolute>(C)->setVA(SEHTable->getSize() / 4); + Symbol *T = Symtab->findUnderscore(TableSym); + Symbol *C = Symtab->findUnderscore(CountSym); + replaceSymbol<DefinedSynthetic>(T, T->getName(), TableChunk); + cast<DefinedAbsolute>(C)->setVA(TableChunk->getSize() / 4); } // Handles /section options to allow users to overwrite @@ -829,16 +1140,17 @@ void Writer::setSectionPermissions() { for (auto &P : Config->Section) { StringRef Name = P.first; uint32_t Perm = P.second; - if (auto *Sec = findSection(Name)) - Sec->setPermissions(Perm); + for (OutputSection *Sec : OutputSections) + if (Sec->Name == Name) + Sec->setPermissions(Perm); } } // Write section contents to a mmap'ed file. void Writer::writeSections() { - // Record the section index that should be used when resolving a section - // relocation against an absolute symbol. - DefinedAbsolute::OutputSectionIndex = OutputSections.size() + 1; + // Record the number of sections to apply section index relocations + // against absolute symbols. See applySecIdx in Chunks.cpp.. + DefinedAbsolute::NumOutputSections = OutputSections.size(); uint8_t *Buf = Buffer->getBufferStart(); for (OutputSection *Sec : OutputSections) { @@ -846,7 +1158,7 @@ void Writer::writeSections() { // Fill gaps between functions in .text with INT3 instructions // instead of leaving as NUL bytes (which can be interpreted as // ADD instructions). - if (Sec->getPermissions() & IMAGE_SCN_CNT_CODE) + if (Sec->Header.Characteristics & IMAGE_SCN_CNT_CODE) memset(SecBuf, 0xCC, Sec->getRawSize()); for_each(parallel::par, Sec->getChunks().begin(), Sec->getChunks().end(), [&](Chunk *C) { C->writeTo(SecBuf); }); @@ -854,32 +1166,65 @@ void Writer::writeSections() { } void Writer::writeBuildId() { - // If we're not writing a build id (e.g. because /debug is not specified), - // then just return; - if (!Config->Debug) - return; + // There are two important parts to the build ID. + // 1) If building with debug info, the COFF debug directory contains a + // timestamp as well as a Guid and Age of the PDB. + // 2) In all cases, the PE COFF file header also contains a timestamp. + // For reproducibility, instead of a timestamp we want to use a hash of the + // binary, however when building with debug info the hash needs to take into + // account the debug info, since it's possible to add blank lines to a file + // which causes the debug info to change but not the generated code. + // + // To handle this, we first set the Guid and Age in the debug directory (but + // only if we're doing a debug build). Then, we hash the binary (thus causing + // the hash to change if only the debug info changes, since the Age will be + // different). Finally, we write that hash into the debug directory (if + // present) as well as the COFF file header (always). + if (Config->Debug) { + assert(BuildId && "BuildId is not set!"); + if (PreviousBuildId.hasValue()) { + *BuildId->BuildId = *PreviousBuildId; + BuildId->BuildId->PDB70.Age = BuildId->BuildId->PDB70.Age + 1; + } else { + BuildId->BuildId->Signature.CVSignature = OMF::Signature::PDB70; + BuildId->BuildId->PDB70.Age = 1; + llvm::getRandomBytes(BuildId->BuildId->PDB70.Signature, 16); + } + } - assert(BuildId && "BuildId is not set!"); + // At this point the only fields in the COFF file which remain unset are the + // "timestamp" in the COFF file header, and the ones in the coff debug + // directory. Now we can hash the file and write that hash to the various + // timestamp fields in the file. + StringRef OutputFileData( + reinterpret_cast<const char *>(Buffer->getBufferStart()), + Buffer->getBufferSize()); - if (PreviousBuildId.hasValue()) { - *BuildId->BuildId = *PreviousBuildId; - BuildId->BuildId->PDB70.Age = BuildId->BuildId->PDB70.Age + 1; - return; - } + uint32_t Timestamp = Config->Timestamp; + if (Config->Repro) + Timestamp = static_cast<uint32_t>(xxHash64(OutputFileData)); + + if (DebugDirectory) + DebugDirectory->setTimeDateStamp(Timestamp); - BuildId->BuildId->Signature.CVSignature = OMF::Signature::PDB70; - BuildId->BuildId->PDB70.Age = 1; - llvm::getRandomBytes(BuildId->BuildId->PDB70.Signature, 16); + uint8_t *Buf = Buffer->getBufferStart(); + Buf += DOSStubSize + sizeof(PEMagic); + object::coff_file_header *CoffHeader = + reinterpret_cast<coff_file_header *>(Buf); + CoffHeader->TimeDateStamp = Timestamp; } // Sort .pdata section contents according to PE/COFF spec 5.5. void Writer::sortExceptionTable() { - OutputSection *Sec = findSection(".pdata"); - if (!Sec) + if (!FirstPdata) return; // We assume .pdata contains function table entries only. - uint8_t *Begin = Buffer->getBufferStart() + Sec->getFileOff(); - uint8_t *End = Begin + Sec->getVirtualSize(); + auto BufAddr = [&](Chunk *C) { + return Buffer->getBufferStart() + C->getOutputSection()->getFileOff() + + C->getRVA() - C->getOutputSection()->getRVA(); + }; + uint8_t *Begin = BufAddr(FirstPdata); + uint8_t *End = BufAddr(LastPdata) + LastPdata->getSize(); if (Config->Machine == AMD64) { struct Entry { ulittle32_t Begin, End, Unwind; }; sort(parallel::par, (Entry *)Begin, (Entry *)End, @@ -897,7 +1242,7 @@ void Writer::sortExceptionTable() { OutputSection *Writer::findSection(StringRef Name) { for (OutputSection *Sec : OutputSections) - if (Sec->getName() == Name) + if (Sec->Name == Name) return Sec; return nullptr; } @@ -905,55 +1250,31 @@ OutputSection *Writer::findSection(StringRef Name) { uint32_t Writer::getSizeOfInitializedData() { uint32_t Res = 0; for (OutputSection *S : OutputSections) - if (S->getPermissions() & IMAGE_SCN_CNT_INITIALIZED_DATA) + if (S->Header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) Res += S->getRawSize(); return Res; } -// Returns an existing section or create a new one if not found. -OutputSection *Writer::createSection(StringRef Name) { - if (auto *Sec = findSection(Name)) - return Sec; - const auto DATA = IMAGE_SCN_CNT_INITIALIZED_DATA; - const auto BSS = IMAGE_SCN_CNT_UNINITIALIZED_DATA; - const auto CODE = IMAGE_SCN_CNT_CODE; - const auto DISCARDABLE = IMAGE_SCN_MEM_DISCARDABLE; - const auto R = IMAGE_SCN_MEM_READ; - const auto W = IMAGE_SCN_MEM_WRITE; - const auto X = IMAGE_SCN_MEM_EXECUTE; - uint32_t Perms = StringSwitch<uint32_t>(Name) - .Case(".bss", BSS | R | W) - .Case(".data", DATA | R | W) - .Cases(".didat", ".edata", ".idata", ".rdata", DATA | R) - .Case(".reloc", DATA | DISCARDABLE | R) - .Case(".text", CODE | R | X) - .Default(0); - if (!Perms) - llvm_unreachable("unknown section name"); - auto Sec = make<OutputSection>(Name); - Sec->addPermissions(Perms); - OutputSections.push_back(Sec); - return Sec; -} - -// Dest is .reloc section. Add contents to that section. -void Writer::addBaserels(OutputSection *Dest) { +// Add base relocations to .reloc section. +void Writer::addBaserels() { + if (!Config->Relocatable) + return; std::vector<Baserel> V; for (OutputSection *Sec : OutputSections) { - if (Sec == Dest) + if (Sec->Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) continue; // Collect all locations for base relocations. for (Chunk *C : Sec->getChunks()) C->getBaserels(&V); // Add the addresses to .reloc section. if (!V.empty()) - addBaserelBlocks(Dest, V); + addBaserelBlocks(V); V.clear(); } } // Add addresses to .reloc section. Note that addresses are grouped by page. -void Writer::addBaserelBlocks(OutputSection *Dest, std::vector<Baserel> &V) { +void Writer::addBaserelBlocks(std::vector<Baserel> &V) { const uint32_t Mask = ~uint32_t(PageSize - 1); uint32_t Page = V[0].RVA & Mask; size_t I = 0, J = 1; @@ -961,11 +1282,11 @@ void Writer::addBaserelBlocks(OutputSection *Dest, std::vector<Baserel> &V) { uint32_t P = V[J].RVA & Mask; if (P == Page) continue; - Dest->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J)); + RelocSec->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J)); I = J; Page = P; } if (I == J) return; - Dest->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J)); + RelocSec->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J)); } diff --git a/contrib/llvm/tools/lld/COFF/Writer.h b/contrib/llvm/tools/lld/COFF/Writer.h index 21be1be6e92a..d37276cb6d91 100644 --- a/contrib/llvm/tools/lld/COFF/Writer.h +++ b/contrib/llvm/tools/lld/COFF/Writer.h @@ -13,6 +13,7 @@ #include "Chunks.h" #include "llvm/ADT/StringRef.h" #include "llvm/Object/COFF.h" +#include <chrono> #include <cstdint> #include <vector> @@ -29,16 +30,14 @@ void writeResult(); // non-overlapping file offsets and RVAs. class OutputSection { public: - OutputSection(llvm::StringRef N) : Name(N), Header({}) {} - void setRVA(uint64_t); - void setFileOffset(uint64_t); + OutputSection(llvm::StringRef N, uint32_t Chars) : Name(N) { + Header.Characteristics = Chars; + } void addChunk(Chunk *C); - llvm::StringRef getName() { return Name; } + void merge(OutputSection *Other); ArrayRef<Chunk *> getChunks() { return Chunks; } void addPermissions(uint32_t C); void setPermissions(uint32_t C); - uint32_t getPermissions() { return Header.Characteristics & PermMask; } - uint32_t getCharacteristics() { return Header.Characteristics; } uint64_t getRVA() { return Header.VirtualAddress; } uint64_t getFileOff() { return Header.PointerToRawData; } void writeHeaderTo(uint8_t *Buf); @@ -60,9 +59,10 @@ public: // N.B. The section index is one based. uint32_t SectionIndex = 0; -private: llvm::StringRef Name; - llvm::object::coff_section Header; + llvm::object::coff_section Header = {}; + +private: uint32_t StringTableOff = 0; std::vector<Chunk *> Chunks; }; diff --git a/contrib/llvm/tools/lld/Common/Args.cpp b/contrib/llvm/tools/lld/Common/Args.cpp index 680cf5bd0a6e..ff77bfcc3b76 100644 --- a/contrib/llvm/tools/lld/Common/Args.cpp +++ b/contrib/llvm/tools/lld/Common/Args.cpp @@ -18,13 +18,17 @@ using namespace llvm; using namespace lld; int lld::args::getInteger(opt::InputArgList &Args, unsigned Key, int Default) { - int V = Default; - if (auto *Arg = Args.getLastArg(Key)) { - StringRef S = Arg->getValue(); - if (!to_integer(S, V, 10)) - error(Arg->getSpelling() + ": number expected, but got '" + S + "'"); - } - return V; + auto *A = Args.getLastArg(Key); + if (!A) + return Default; + + int V; + if (to_integer(A->getValue(), V, 10)) + return V; + + StringRef Spelling = Args.getArgString(A->getIndex()); + error(Spelling + ": number expected, but got '" + A->getValue() + "'"); + return 0; } std::vector<StringRef> lld::args::getStrings(opt::InputArgList &Args, int Id) { diff --git a/contrib/llvm/tools/lld/Common/CMakeLists.txt b/contrib/llvm/tools/lld/Common/CMakeLists.txt index b376893f35a4..a45fe209f06f 100644 --- a/contrib/llvm/tools/lld/Common/CMakeLists.txt +++ b/contrib/llvm/tools/lld/Common/CMakeLists.txt @@ -10,6 +10,7 @@ add_lld_library(lldCommon Strings.cpp TargetOptionsCommandFlags.cpp Threads.cpp + Timer.cpp Version.cpp ADDITIONAL_HEADER_DIRS diff --git a/contrib/llvm/tools/lld/Common/ErrorHandler.cpp b/contrib/llvm/tools/lld/Common/ErrorHandler.cpp index 18affce4d5a6..d1cb3dbbe03c 100644 --- a/contrib/llvm/tools/lld/Common/ErrorHandler.cpp +++ b/contrib/llvm/tools/lld/Common/ErrorHandler.cpp @@ -12,7 +12,8 @@ #include "lld/Common/Threads.h" #include "llvm/ADT/Twine.h" -#include "llvm/Support/Error.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/DiagnosticPrinter.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/raw_ostream.h" #include <mutex> @@ -59,6 +60,30 @@ void lld::exitLld(int Val) { _exit(Val); } +void lld::diagnosticHandler(const DiagnosticInfo &DI) { + SmallString<128> S; + raw_svector_ostream OS(S); + DiagnosticPrinterRawOStream DP(OS); + DI.print(DP); + switch (DI.getSeverity()) { + case DS_Error: + error(S); + break; + case DS_Warning: + warn(S); + break; + case DS_Remark: + case DS_Note: + message(S); + break; + } +} + +void lld::checkError(Error E) { + handleAllErrors(std::move(E), + [&](ErrorInfoBase &EIB) { error(EIB.message()); }); +} + void ErrorHandler::print(StringRef S, raw_ostream::Colors C) { *ErrorOS << LogName << ": "; if (ColorDiagnostics) { diff --git a/contrib/llvm/tools/lld/Common/Strings.cpp b/contrib/llvm/tools/lld/Common/Strings.cpp index 6cd4ad8d600a..36f4f77d8476 100644 --- a/contrib/llvm/tools/lld/Common/Strings.cpp +++ b/contrib/llvm/tools/lld/Common/Strings.cpp @@ -8,7 +8,21 @@ //===----------------------------------------------------------------------===// #include "lld/Common/Strings.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/LLVM.h" #include "llvm/Demangle/Demangle.h" +#include "llvm/Support/GlobPattern.h" +#include <algorithm> +#include <mutex> +#include <vector> + +#if defined(_MSC_VER) +#include <Windows.h> + +// DbgHelp.h must be included after Windows.h. +#include <DbgHelp.h> +#pragma comment(lib, "dbghelp.lib") +#endif using namespace llvm; using namespace lld; @@ -30,3 +44,66 @@ Optional<std::string> lld::demangleItanium(StringRef Name) { free(Buf); return S; } + +Optional<std::string> lld::demangleMSVC(StringRef S) { +#if defined(_MSC_VER) + // UnDecorateSymbolName is not thread-safe, so we need a mutex. + static std::mutex Mu; + std::lock_guard<std::mutex> Lock(Mu); + + char Buf[4096]; + if (S.startswith("?")) + if (size_t Len = UnDecorateSymbolName(S.str().c_str(), Buf, sizeof(Buf), 0)) + return std::string(Buf, Len); +#endif + return None; +} + +StringMatcher::StringMatcher(ArrayRef<StringRef> Pat) { + for (StringRef S : Pat) { + Expected<GlobPattern> Pat = GlobPattern::create(S); + if (!Pat) + error(toString(Pat.takeError())); + else + Patterns.push_back(*Pat); + } +} + +bool StringMatcher::match(StringRef S) const { + for (const GlobPattern &Pat : Patterns) + if (Pat.match(S)) + return true; + return false; +} + +// Converts a hex string (e.g. "deadbeef") to a vector. +std::vector<uint8_t> lld::parseHex(StringRef S) { + std::vector<uint8_t> Hex; + while (!S.empty()) { + StringRef B = S.substr(0, 2); + S = S.substr(2); + uint8_t H; + if (!to_integer(B, H, 16)) { + error("not a hexadecimal value: " + B); + return {}; + } + Hex.push_back(H); + } + return Hex; +} + +// Returns true if S is valid as a C language identifier. +bool lld::isValidCIdentifier(StringRef S) { + return !S.empty() && (isAlpha(S[0]) || S[0] == '_') && + std::all_of(S.begin() + 1, S.end(), + [](char C) { return C == '_' || isAlnum(C); }); +} + +// Write the contents of the a buffer to a file +void lld::saveBuffer(StringRef Buffer, const Twine &Path) { + std::error_code EC; + raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None); + if (EC) + error("cannot create " + Path + ": " + EC.message()); + OS << Buffer; +} diff --git a/contrib/llvm/tools/lld/Common/TargetOptionsCommandFlags.cpp b/contrib/llvm/tools/lld/Common/TargetOptionsCommandFlags.cpp index e8e582f4c256..b46df363c361 100644 --- a/contrib/llvm/tools/lld/Common/TargetOptionsCommandFlags.cpp +++ b/contrib/llvm/tools/lld/Common/TargetOptionsCommandFlags.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// // // This file exists as a place for global variables defined in LLVM's -// CodeGen/CommandFlags.def. By putting the resulting object file in +// CodeGen/CommandFlags.inc. By putting the resulting object file in // an archive and linking with it, the definitions will automatically be // included when needed and skipped when already present. // @@ -16,12 +16,12 @@ #include "lld/Common/TargetOptionsCommandFlags.h" -#include "llvm/CodeGen/CommandFlags.def" +#include "llvm/CodeGen/CommandFlags.inc" #include "llvm/Target/TargetOptions.h" // Define an externally visible version of // InitTargetOptionsFromCodeGenFlags, so that its functionality can be -// used without having to include llvm/CodeGen/CommandFlags.def, which +// used without having to include llvm/CodeGen/CommandFlags.inc, which // would lead to multiple definitions of the command line flags. llvm::TargetOptions lld::InitTargetOptionsFromCodeGenFlags() { return ::InitTargetOptionsFromCodeGenFlags(); @@ -30,3 +30,5 @@ llvm::TargetOptions lld::InitTargetOptionsFromCodeGenFlags() { llvm::Optional<llvm::CodeModel::Model> lld::GetCodeModelFromCMModel() { return getCodeModel(); } + +std::string lld::GetCPUStr() { return ::getCPUStr(); } diff --git a/contrib/llvm/tools/lld/Common/Timer.cpp b/contrib/llvm/tools/lld/Common/Timer.cpp new file mode 100644 index 000000000000..89f9829b47cf --- /dev/null +++ b/contrib/llvm/tools/lld/Common/Timer.cpp @@ -0,0 +1,80 @@ +//===- Timer.cpp ----------------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/Timer.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/Support/Format.h" + +using namespace lld; +using namespace llvm; + +ScopedTimer::ScopedTimer(Timer &T) : T(&T) { T.start(); } + +void ScopedTimer::stop() { + if (!T) + return; + T->stop(); + T = nullptr; +} + +ScopedTimer::~ScopedTimer() { stop(); } + +Timer::Timer(llvm::StringRef Name) : Name(Name), Parent(nullptr) {} +Timer::Timer(llvm::StringRef Name, Timer &Parent) + : Name(Name), Parent(&Parent) {} + +void Timer::start() { + if (Parent && Total.count() == 0) + Parent->Children.push_back(this); + StartTime = std::chrono::high_resolution_clock::now(); +} + +void Timer::stop() { + Total += (std::chrono::high_resolution_clock::now() - StartTime); +} + +Timer &Timer::root() { + static Timer RootTimer("Total Link Time"); + return RootTimer; +} + +void Timer::print() { + double TotalDuration = static_cast<double>(root().millis()); + + // We want to print the grand total under all the intermediate phases, so we + // print all children first, then print the total under that. + for (const auto &Child : Children) + Child->print(1, TotalDuration); + + message(std::string(49, '-')); + + root().print(0, root().millis(), false); +} + +double Timer::millis() const { + return std::chrono::duration_cast<std::chrono::duration<double, std::milli>>( + Total) + .count(); +} + +void Timer::print(int Depth, double TotalDuration, bool Recurse) const { + double P = 100.0 * millis() / TotalDuration; + + SmallString<32> Str; + llvm::raw_svector_ostream Stream(Str); + std::string S = std::string(Depth * 2, ' ') + Name + std::string(":"); + Stream << format("%-30s%5d ms (%5.1f%%)", S.c_str(), (int)millis(), P); + + message(Str); + + if (Recurse) { + for (const auto &Child : Children) + Child->print(Depth + 1, TotalDuration); + } +} diff --git a/contrib/llvm/tools/lld/ELF/AArch64ErrataFix.cpp b/contrib/llvm/tools/lld/ELF/AArch64ErrataFix.cpp index 48273d0eb398..7551919cf86f 100644 --- a/contrib/llvm/tools/lld/ELF/AArch64ErrataFix.cpp +++ b/contrib/llvm/tools/lld/ELF/AArch64ErrataFix.cpp @@ -34,12 +34,11 @@ #include "LinkerScript.h" #include "OutputSections.h" #include "Relocations.h" -#include "Strings.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/Memory.h" - +#include "lld/Common/Strings.h" #include "llvm/Support/Endian.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> @@ -342,7 +341,7 @@ static bool is843419ErratumSequence(uint32_t Instr1, uint32_t Instr2, // patch or 0 if no patch required. static uint64_t scanCortexA53Errata843419(InputSection *IS, uint64_t &Off, uint64_t Limit) { - uint64_t ISAddr = IS->getParent()->Addr + IS->OutSecOff; + uint64_t ISAddr = IS->getVA(0); // Advance Off so that (ISAddr + Off) modulo 0x1000 is at least 0xff8. uint64_t InitialPageOff = (ISAddr + Off) & 0xfff; @@ -406,7 +405,7 @@ lld::elf::Patch843419Section::Patch843419Section(InputSection *P, uint64_t Off) } uint64_t lld::elf::Patch843419Section::getLDSTAddr() const { - return Patchee->getParent()->Addr + Patchee->OutSecOff + PatcheeOffset; + return Patchee->getVA(PatcheeOffset); } void lld::elf::Patch843419Section::writeTo(uint8_t *Buf) { @@ -555,9 +554,8 @@ static void implementPatch(uint64_t AdrpAddr, uint64_t PatcheeOffset, if (RelIt != IS->Relocations.end() && RelIt->Type == R_AARCH64_JUMP26) return; - if (Config->Verbose) - message("detected cortex-a53-843419 erratum sequence starting at " + - utohexstr(AdrpAddr) + " in unpatched output."); + log("detected cortex-a53-843419 erratum sequence starting at " + + utohexstr(AdrpAddr) + " in unpatched output."); auto *PS = make<Patch843419Section>(IS, PatcheeOffset); Patches.push_back(PS); @@ -603,7 +601,7 @@ AArch64Err843419Patcher::patchInputSectionDescription( (DataSym == MapSyms.end()) ? IS->Data.size() : (*DataSym)->Value; while (Off < Limit) { - uint64_t StartAddr = IS->getParent()->Addr + IS->OutSecOff + Off; + uint64_t StartAddr = IS->getVA(Off); if (uint64_t PatcheeOffset = scanCortexA53Errata843419(IS, Off, Limit)) implementPatch(StartAddr, PatcheeOffset, IS, Patches); } diff --git a/contrib/llvm/tools/lld/ELF/AArch64ErrataFix.h b/contrib/llvm/tools/lld/ELF/AArch64ErrataFix.h index 6c100f25d8af..edd154d4cab3 100644 --- a/contrib/llvm/tools/lld/ELF/AArch64ErrataFix.h +++ b/contrib/llvm/tools/lld/ELF/AArch64ErrataFix.h @@ -11,7 +11,6 @@ #define LLD_ELF_AARCH64ERRATAFIX_H #include "lld/Common/LLVM.h" - #include <map> #include <vector> diff --git a/contrib/llvm/tools/lld/ELF/Arch/AArch64.cpp b/contrib/llvm/tools/lld/ELF/Arch/AArch64.cpp index a741cd1cb6da..ae072d4adaac 100644 --- a/contrib/llvm/tools/lld/ELF/Arch/AArch64.cpp +++ b/contrib/llvm/tools/lld/ELF/Arch/AArch64.cpp @@ -34,7 +34,7 @@ public: AArch64(); RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const override; - bool isPicRel(RelType Type) const override; + RelType getDynRel(RelType Type) const override; void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; void writePltHeader(uint8_t *Buf) const override; void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, @@ -93,6 +93,11 @@ RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S, return R_TLSDESC_CALL; case R_AARCH64_TLSLE_ADD_TPREL_HI12: case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: + case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC: + case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC: + case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC: + case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC: + case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC: return R_TLS; case R_AARCH64_CALL26: case R_AARCH64_CONDBR19: @@ -148,8 +153,10 @@ bool AArch64::usesOnlyLowPageBits(RelType Type) const { } } -bool AArch64::isPicRel(RelType Type) const { - return Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64; +RelType AArch64::getDynRel(RelType Type) const { + if (Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64) + return Type; + return R_AARCH64_NONE; } void AArch64::writeGotPlt(uint8_t *Buf, const Symbol &) const { @@ -244,12 +251,12 @@ void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { switch (Type) { case R_AARCH64_ABS16: case R_AARCH64_PREL16: - checkIntUInt<16>(Loc, Val, Type); + checkIntUInt(Loc, Val, 16, Type); write16le(Loc, Val); break; case R_AARCH64_ABS32: case R_AARCH64_PREL32: - checkIntUInt<32>(Loc, Val, Type); + checkIntUInt(Loc, Val, 32, Type); write32le(Loc, Val); break; case R_AARCH64_ABS64: @@ -264,11 +271,11 @@ void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_AARCH64_ADR_PREL_PG_HI21: case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: case R_AARCH64_TLSDESC_ADR_PAGE21: - checkInt<33>(Loc, Val, Type); + checkInt(Loc, Val, 33, Type); write32AArch64Addr(Loc, Val >> 12); break; case R_AARCH64_ADR_PREL_LO21: - checkInt<21>(Loc, Val, Type); + checkInt(Loc, Val, 21, Type); write32AArch64Addr(Loc, Val); break; case R_AARCH64_JUMP26: @@ -282,38 +289,40 @@ void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { write32le(Loc, 0x14000000); LLVM_FALLTHROUGH; case R_AARCH64_CALL26: - checkInt<28>(Loc, Val, Type); + checkInt(Loc, Val, 28, Type); or32le(Loc, (Val & 0x0FFFFFFC) >> 2); break; case R_AARCH64_CONDBR19: case R_AARCH64_LD_PREL_LO19: - checkAlignment<4>(Loc, Val, Type); - checkInt<21>(Loc, Val, Type); + checkAlignment(Loc, Val, 4, Type); + checkInt(Loc, Val, 21, Type); or32le(Loc, (Val & 0x1FFFFC) << 3); break; - case R_AARCH64_LD64_GOT_LO12_NC: - case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: - case R_AARCH64_TLSDESC_LD64_LO12: - checkAlignment<8>(Loc, Val, Type); - or32le(Loc, (Val & 0xFF8) << 7); - break; case R_AARCH64_LDST8_ABS_LO12_NC: + case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC: or32AArch64Imm(Loc, getBits(Val, 0, 11)); break; case R_AARCH64_LDST16_ABS_LO12_NC: - checkAlignment<2>(Loc, Val, Type); + case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC: + checkAlignment(Loc, Val, 2, Type); or32AArch64Imm(Loc, getBits(Val, 1, 11)); break; case R_AARCH64_LDST32_ABS_LO12_NC: - checkAlignment<4>(Loc, Val, Type); + case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC: + checkAlignment(Loc, Val, 4, Type); or32AArch64Imm(Loc, getBits(Val, 2, 11)); break; case R_AARCH64_LDST64_ABS_LO12_NC: - checkAlignment<8>(Loc, Val, Type); + case R_AARCH64_LD64_GOT_LO12_NC: + case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: + case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC: + case R_AARCH64_TLSDESC_LD64_LO12: + checkAlignment(Loc, Val, 8, Type); or32AArch64Imm(Loc, getBits(Val, 3, 11)); break; case R_AARCH64_LDST128_ABS_LO12_NC: - checkAlignment<16>(Loc, Val, Type); + case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC: + checkAlignment(Loc, Val, 16, Type); or32AArch64Imm(Loc, getBits(Val, 4, 11)); break; case R_AARCH64_MOVW_UABS_G0_NC: @@ -329,11 +338,11 @@ void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { or32le(Loc, (Val & 0xFFFF000000000000) >> 43); break; case R_AARCH64_TSTBR14: - checkInt<16>(Loc, Val, Type); + checkInt(Loc, Val, 16, Type); or32le(Loc, (Val & 0xFFFC) << 3); break; case R_AARCH64_TLSLE_ADD_TPREL_HI12: - checkInt<24>(Loc, Val, Type); + checkInt(Loc, Val, 24, Type); or32AArch64Imm(Loc, Val >> 12); break; case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: @@ -357,7 +366,7 @@ void AArch64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { // movk x0, #0x10 // nop // nop - checkUInt<32>(Loc, Val, Type); + checkUInt(Loc, Val, 32, Type); switch (Type) { case R_AARCH64_TLSDESC_ADD_LO12: @@ -407,7 +416,7 @@ void AArch64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const { } void AArch64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { - checkUInt<32>(Loc, Val, Type); + checkUInt(Loc, Val, 32, Type); if (Type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) { // Generate MOVZ. diff --git a/contrib/llvm/tools/lld/ELF/Arch/AMDGPU.cpp b/contrib/llvm/tools/lld/ELF/Arch/AMDGPU.cpp index 505e0e6ad480..48b27f23510c 100644 --- a/contrib/llvm/tools/lld/ELF/Arch/AMDGPU.cpp +++ b/contrib/llvm/tools/lld/ELF/Arch/AMDGPU.cpp @@ -66,6 +66,7 @@ void AMDGPU::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { write32le(Loc, Val); break; case R_AMDGPU_ABS64: + case R_AMDGPU_REL64: write64le(Loc, Val); break; case R_AMDGPU_GOTPCREL32_HI: @@ -86,6 +87,7 @@ RelExpr AMDGPU::getRelExpr(RelType Type, const Symbol &S, case R_AMDGPU_REL32: case R_AMDGPU_REL32_LO: case R_AMDGPU_REL32_HI: + case R_AMDGPU_REL64: return R_PC; case R_AMDGPU_GOTPCREL: case R_AMDGPU_GOTPCREL32_LO: diff --git a/contrib/llvm/tools/lld/ELF/Arch/ARM.cpp b/contrib/llvm/tools/lld/ELF/Arch/ARM.cpp index fc2ac9beef9c..acf9a615f20b 100644 --- a/contrib/llvm/tools/lld/ELF/Arch/ARM.cpp +++ b/contrib/llvm/tools/lld/ELF/Arch/ARM.cpp @@ -29,7 +29,6 @@ public: uint32_t calcEFlags() const override; RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const override; - bool isPicRel(RelType Type) const override; RelType getDynRel(RelType Type) const override; int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override; void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; @@ -55,6 +54,7 @@ ARM::ARM() { TlsGotRel = R_ARM_TLS_TPOFF32; TlsModuleIndexRel = R_ARM_TLS_DTPMOD32; TlsOffsetRel = R_ARM_TLS_DTPOFF32; + GotBaseSymInGotPlt = false; GotEntrySize = 4; GotPltEntrySize = 4; PltEntrySize = 16; @@ -170,18 +170,10 @@ RelExpr ARM::getRelExpr(RelType Type, const Symbol &S, } } -bool ARM::isPicRel(RelType Type) const { - return (Type == R_ARM_TARGET1 && !Config->Target1Rel) || - (Type == R_ARM_ABS32); -} - RelType ARM::getDynRel(RelType Type) const { - if (Type == R_ARM_TARGET1 && !Config->Target1Rel) + if ((Type == R_ARM_ABS32) || (Type == R_ARM_TARGET1 && !Config->Target1Rel)) return R_ARM_ABS32; - if (Type == R_ARM_ABS32) - return Type; - // Keep it going with a dummy value so that we can find more reloc errors. - return R_ARM_ABS32; + return R_ARM_NONE; } void ARM::writeGotPlt(uint8_t *Buf, const Symbol &) const { @@ -401,7 +393,7 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { write32le(Loc, 1); break; case R_ARM_PREL31: - checkInt<31>(Loc, Val, Type); + checkInt(Loc, Val, 31, Type); write32le(Loc, (read32le(Loc) & 0x80000000) | (Val & ~0x80000000)); break; case R_ARM_CALL: @@ -410,7 +402,7 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { if (Val & 1) { // If bit 0 of Val is 1 the target is Thumb, we must select a BLX. // The BLX encoding is 0xfa:H:imm24 where Val = imm24:H:'1' - checkInt<26>(Loc, Val, Type); + checkInt(Loc, Val, 26, Type); write32le(Loc, 0xfa000000 | // opcode ((Val & 2) << 23) | // H ((Val >> 2) & 0x00ffffff)); // imm24 @@ -425,16 +417,16 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_ARM_JUMP24: case R_ARM_PC24: case R_ARM_PLT32: - checkInt<26>(Loc, Val, Type); + checkInt(Loc, Val, 26, Type); write32le(Loc, (read32le(Loc) & ~0x00ffffff) | ((Val >> 2) & 0x00ffffff)); break; case R_ARM_THM_JUMP11: - checkInt<12>(Loc, Val, Type); + checkInt(Loc, Val, 12, Type); write16le(Loc, (read32le(Loc) & 0xf800) | ((Val >> 1) & 0x07ff)); break; case R_ARM_THM_JUMP19: // Encoding T3: Val = S:J2:J1:imm6:imm11:0 - checkInt<21>(Loc, Val, Type); + checkInt(Loc, Val, 21, Type); write16le(Loc, (read16le(Loc) & 0xfbc0) | // opcode cond ((Val >> 10) & 0x0400) | // S @@ -460,7 +452,7 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_ARM_THM_JUMP24: // Encoding B T4, BL T1, BLX T2: Val = S:I1:I2:imm10:imm11:0 // FIXME: Use of I1 and I2 require v6T2ops - checkInt<25>(Loc, Val, Type); + checkInt(Loc, Val, 25, Type); write16le(Loc, 0xf000 | // opcode ((Val >> 14) & 0x0400) | // S @@ -478,14 +470,14 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { break; case R_ARM_MOVT_ABS: case R_ARM_MOVT_PREL: - checkInt<32>(Loc, Val, Type); + checkInt(Loc, Val, 32, 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 - checkInt<32>(Loc, Val, Type); + checkInt(Loc, Val, 32, Type); write16le(Loc, 0xf2c0 | // opcode ((Val >> 17) & 0x0400) | // i diff --git a/contrib/llvm/tools/lld/ELF/Arch/Hexagon.cpp b/contrib/llvm/tools/lld/ELF/Arch/Hexagon.cpp new file mode 100644 index 000000000000..ff5e862bafa2 --- /dev/null +++ b/contrib/llvm/tools/lld/ELF/Arch/Hexagon.cpp @@ -0,0 +1,103 @@ +//===-- Hexagon.cpp -------------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Symbols.h" +#include "Target.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::support::endian; +using namespace llvm::ELF; +using namespace lld; +using namespace lld::elf; + +namespace { +class Hexagon final : public TargetInfo { +public: + uint32_t calcEFlags() const override; + RelExpr getRelExpr(RelType Type, const Symbol &S, + const uint8_t *Loc) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; +}; +} // namespace + +// Support V60 only at the moment. +uint32_t Hexagon::calcEFlags() const { return 0x60; } + +static uint32_t applyMask(uint32_t Mask, uint32_t Data) { + uint32_t Result = 0; + size_t Off = 0; + + for (size_t Bit = 0; Bit != 32; ++Bit) { + uint32_t ValBit = (Data >> Off) & 1; + uint32_t MaskBit = (Mask >> Bit) & 1; + if (MaskBit) { + Result |= (ValBit << Bit); + ++Off; + } + } + return Result; +} + +RelExpr Hexagon::getRelExpr(RelType Type, const Symbol &S, + const uint8_t *Loc) const { + switch (Type) { + case R_HEX_B15_PCREL: + case R_HEX_B15_PCREL_X: + case R_HEX_B22_PCREL: + case R_HEX_B22_PCREL_X: + case R_HEX_B32_PCREL_X: + return R_PC; + default: + return R_ABS; + } +} + +static void or32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) | V); } + +void Hexagon::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { + switch (Type) { + case R_HEX_NONE: + break; + case R_HEX_12_X: + or32le(Loc, applyMask(0x000007e0, Val)); + break; + case R_HEX_32_6_X: + or32le(Loc, applyMask(0x0fff3fff, Val >> 6)); + break; + case R_HEX_B15_PCREL: + or32le(Loc, applyMask(0x00df20fe, Val >> 2)); + break; + case R_HEX_B15_PCREL_X: + or32le(Loc, applyMask(0x00df20fe, Val & 0x3f)); + break; + case R_HEX_B22_PCREL: + or32le(Loc, applyMask(0x1ff3ffe, Val >> 2)); + break; + case R_HEX_B22_PCREL_X: + or32le(Loc, applyMask(0x1ff3ffe, Val & 0x3f)); + break; + case R_HEX_B32_PCREL_X: + or32le(Loc, applyMask(0x0fff3fff, Val >> 6)); + break; + default: + error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type)); + break; + } +} + +TargetInfo *elf::getHexagonTargetInfo() { + static Hexagon Target; + return &Target; +} diff --git a/contrib/llvm/tools/lld/ELF/Arch/Mips.cpp b/contrib/llvm/tools/lld/ELF/Arch/Mips.cpp index e8af36e6d11e..dc70401c0b0e 100644 --- a/contrib/llvm/tools/lld/ELF/Arch/Mips.cpp +++ b/contrib/llvm/tools/lld/ELF/Arch/Mips.cpp @@ -32,7 +32,6 @@ public: RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const override; int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override; - bool isPicRel(RelType Type) const override; RelType getDynRel(RelType Type) const override; void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; void writePltHeader(uint8_t *Buf) const override; @@ -50,6 +49,7 @@ template <class ELFT> MIPS<ELFT>::MIPS() { DefaultMaxPageSize = 65536; GotEntrySize = sizeof(typename ELFT::uint); GotPltEntrySize = sizeof(typename ELFT::uint); + GotBaseSymInGotPlt = false; PltEntrySize = 16; PltHeaderSize = 32; CopyRel = R_MIPS_COPY; @@ -101,8 +101,6 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S, case R_MIPS_HIGHEST: case R_MICROMIPS_HI16: case R_MICROMIPS_LO16: - case R_MICROMIPS_HIGHER: - case R_MICROMIPS_HIGHEST: // R_MIPS_HI16/R_MIPS_LO16 relocations against _gp_disp calculate // offset between start of function and 'gp' value which by default // equal to the start of .got section. In that case we consider these @@ -124,8 +122,6 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S, case R_MIPS_TLS_TPREL_LO16: case R_MIPS_TLS_TPREL32: case R_MIPS_TLS_TPREL64: - case R_MICROMIPS_GOT_OFST: - case R_MICROMIPS_SUB: case R_MICROMIPS_TLS_DTPREL_HI16: case R_MICROMIPS_TLS_DTPREL_LO16: case R_MICROMIPS_TLS_TPREL_HI16: @@ -155,7 +151,6 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S, case R_MIPS_GOT_DISP: case R_MIPS_TLS_GOTTPREL: case R_MICROMIPS_CALL16: - case R_MICROMIPS_GOT_DISP: case R_MICROMIPS_TLS_GOTTPREL: return R_MIPS_GOT_OFF; case R_MIPS_CALL_HI16: @@ -168,7 +163,6 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S, case R_MICROMIPS_GOT_LO16: return R_MIPS_GOT_OFF32; case R_MIPS_GOT_PAGE: - case R_MICROMIPS_GOT_PAGE: return R_MIPS_GOT_LOCAL_PAGE; case R_MIPS_TLS_GD: case R_MICROMIPS_TLS_GD: @@ -183,12 +177,10 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S, } } -template <class ELFT> bool MIPS<ELFT>::isPicRel(RelType Type) const { - return Type == R_MIPS_32 || Type == R_MIPS_64; -} - template <class ELFT> RelType MIPS<ELFT>::getDynRel(RelType Type) const { - return RelativeRel; + if (Type == R_MIPS_32 || Type == R_MIPS_64) + return RelativeRel; + return R_MIPS_NONE; } template <class ELFT> @@ -213,8 +205,8 @@ template <endianness E> static uint32_t readShuffle(const uint8_t *Loc) { } template <endianness E> -static void writeRelocation(uint8_t *Loc, uint64_t V, uint8_t BitsSize, - uint8_t Shift) { +static void writeValue(uint8_t *Loc, uint64_t V, uint8_t BitsSize, + uint8_t Shift) { uint32_t Instr = read32<E>(Loc); uint32_t Mask = 0xffffffff >> (32 - BitsSize); uint32_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask); @@ -222,14 +214,14 @@ static void writeRelocation(uint8_t *Loc, uint64_t V, uint8_t BitsSize, } template <endianness E> -static void writeMicroRelocation32(uint8_t *Loc, uint64_t V, uint8_t BitsSize, - uint8_t Shift) { +static void writeShuffleValue(uint8_t *Loc, uint64_t V, uint8_t BitsSize, + uint8_t Shift) { // See comments in readShuffle for purpose of this code. uint16_t *Words = (uint16_t *)Loc; if (E == support::little) std::swap(Words[0], Words[1]); - writeRelocation<E>(Loc, V, BitsSize, Shift); + writeValue<E>(Loc, V, BitsSize, Shift); if (E == support::little) std::swap(Words[0], Words[1]); @@ -301,9 +293,9 @@ template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *Buf) const { write32<E>(Buf + 28, 0x2718fffe); // subu $24, $24, 2 uint64_t GotPlt = InX::GotPlt->getVA(); - writeRelocation<E>(Buf, GotPlt + 0x8000, 16, 16); - writeRelocation<E>(Buf + 4, GotPlt, 16, 0); - writeRelocation<E>(Buf + 8, GotPlt, 16, 0); + writeValue<E>(Buf, GotPlt + 0x8000, 16, 16); + writeValue<E>(Buf + 4, GotPlt, 16, 0); + writeValue<E>(Buf + 8, GotPlt, 16, 0); } template <class ELFT> @@ -338,9 +330,9 @@ void MIPS<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, write32<E>(Buf + 4, 0x8df90000); // l[wd] $25, %lo(.got.plt entry)($15) write32<E>(Buf + 8, JrInst); // jr $25 / jr.hb $25 write32<E>(Buf + 12, 0x25f80000); // addiu $24, $15, %lo(.got.plt entry) - writeRelocation<E>(Buf, GotPltEntryAddr + 0x8000, 16, 16); - writeRelocation<E>(Buf + 4, GotPltEntryAddr, 16, 0); - writeRelocation<E>(Buf + 12, GotPltEntryAddr, 16, 0); + writeValue<E>(Buf, GotPltEntryAddr + 0x8000, 16, 16); + writeValue<E>(Buf + 4, GotPltEntryAddr, 16, 0); + writeValue<E>(Buf + 12, GotPltEntryAddr, 16, 0); } template <class ELFT> @@ -459,9 +451,6 @@ calculateMipsRelChain(uint8_t *Loc, RelType Type, uint64_t Val) { return std::make_pair(Type2, Val); if (Type2 == R_MIPS_SUB && (Type3 == R_MIPS_HI16 || Type3 == R_MIPS_LO16)) return std::make_pair(Type3, -Val); - if (Type2 == R_MICROMIPS_SUB && - (Type3 == R_MICROMIPS_HI16 || Type3 == R_MICROMIPS_LO16)) - return std::make_pair(Type3, -Val); error(getErrorLocation(Loc) + "unsupported relocations combination " + Twine(Type)); return std::make_pair(Type & 0xff, Val); @@ -471,6 +460,9 @@ template <class ELFT> void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { const endianness E = ELFT::TargetEndianness; + if (ELFT::Is64Bits || Config->MipsN32Abi) + std::tie(Type, Val) = calculateMipsRelChain(Loc, Type, Val); + // Thread pointer and DRP offsets from the start of TLS data area. // https://www.linux-mips.org/wiki/NPTL if (Type == R_MIPS_TLS_DTPREL_HI16 || Type == R_MIPS_TLS_DTPREL_LO16 || @@ -485,9 +477,6 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { Val -= 0x7000; } - if (ELFT::Is64Bits || Config->MipsN32Abi) - std::tie(Type, Val) = calculateMipsRelChain(Loc, Type, Val); - switch (Type) { case R_MIPS_32: case R_MIPS_GPREL32: @@ -501,25 +490,25 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { write64<E>(Loc, Val); break; case R_MIPS_26: - writeRelocation<E>(Loc, Val, 26, 2); + writeValue<E>(Loc, Val, 26, 2); break; case R_MIPS_GOT16: // The R_MIPS_GOT16 relocation's value in "relocatable" linking mode // is updated addend (not a GOT index). In that case write high 16 bits // to store a correct addend value. if (Config->Relocatable) { - writeRelocation<E>(Loc, Val + 0x8000, 16, 16); + writeValue<E>(Loc, Val + 0x8000, 16, 16); } else { - checkInt<16>(Loc, Val, Type); - writeRelocation<E>(Loc, Val, 16, 0); + checkInt(Loc, Val, 16, Type); + writeValue<E>(Loc, Val, 16, 0); } break; case R_MICROMIPS_GOT16: if (Config->Relocatable) { - writeMicroRelocation32<E>(Loc, Val + 0x8000, 16, 16); + writeShuffleValue<E>(Loc, Val + 0x8000, 16, 16); } else { - checkInt<16>(Loc, Val, Type); - writeMicroRelocation32<E>(Loc, Val, 16, 0); + checkInt(Loc, Val, 16, Type); + writeShuffleValue<E>(Loc, Val, 16, 0); } break; case R_MIPS_CALL16: @@ -529,7 +518,7 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_MIPS_TLS_GD: case R_MIPS_TLS_GOTTPREL: case R_MIPS_TLS_LDM: - checkInt<16>(Loc, Val, Type); + checkInt(Loc, Val, 16, Type); LLVM_FALLTHROUGH; case R_MIPS_CALL_LO16: case R_MIPS_GOT_LO16: @@ -538,28 +527,25 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_MIPS_PCLO16: case R_MIPS_TLS_DTPREL_LO16: case R_MIPS_TLS_TPREL_LO16: - writeRelocation<E>(Loc, Val, 16, 0); + writeValue<E>(Loc, Val, 16, 0); break; - case R_MICROMIPS_GOT_DISP: - case R_MICROMIPS_GOT_PAGE: case R_MICROMIPS_GPREL16: case R_MICROMIPS_TLS_GD: case R_MICROMIPS_TLS_LDM: - checkInt<16>(Loc, Val, Type); - writeMicroRelocation32<E>(Loc, Val, 16, 0); + checkInt(Loc, Val, 16, Type); + writeShuffleValue<E>(Loc, Val, 16, 0); break; case R_MICROMIPS_CALL16: case R_MICROMIPS_CALL_LO16: - case R_MICROMIPS_GOT_OFST: case R_MICROMIPS_LO16: case R_MICROMIPS_TLS_DTPREL_LO16: case R_MICROMIPS_TLS_GOTTPREL: case R_MICROMIPS_TLS_TPREL_LO16: - writeMicroRelocation32<E>(Loc, Val, 16, 0); + writeShuffleValue<E>(Loc, Val, 16, 0); break; case R_MICROMIPS_GPREL7_S2: - checkInt<7>(Loc, Val, Type); - writeMicroRelocation32<E>(Loc, Val, 7, 2); + checkInt(Loc, Val, 7, Type); + writeShuffleValue<E>(Loc, Val, 7, 2); break; case R_MIPS_CALL_HI16: case R_MIPS_GOT_HI16: @@ -567,86 +553,80 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_MIPS_PCHI16: case R_MIPS_TLS_DTPREL_HI16: case R_MIPS_TLS_TPREL_HI16: - writeRelocation<E>(Loc, Val + 0x8000, 16, 16); + writeValue<E>(Loc, Val + 0x8000, 16, 16); break; case R_MICROMIPS_CALL_HI16: case R_MICROMIPS_GOT_HI16: case R_MICROMIPS_HI16: case R_MICROMIPS_TLS_DTPREL_HI16: case R_MICROMIPS_TLS_TPREL_HI16: - writeMicroRelocation32<E>(Loc, Val + 0x8000, 16, 16); + writeShuffleValue<E>(Loc, Val + 0x8000, 16, 16); break; case R_MIPS_HIGHER: - writeRelocation<E>(Loc, Val + 0x80008000, 16, 32); + writeValue<E>(Loc, Val + 0x80008000, 16, 32); break; case R_MIPS_HIGHEST: - writeRelocation<E>(Loc, Val + 0x800080008000, 16, 48); - break; - case R_MICROMIPS_HIGHER: - writeMicroRelocation32<E>(Loc, Val + 0x80008000, 16, 32); - break; - case R_MICROMIPS_HIGHEST: - writeMicroRelocation32<E>(Loc, Val + 0x800080008000, 16, 48); + writeValue<E>(Loc, Val + 0x800080008000, 16, 48); break; case R_MIPS_JALR: case R_MICROMIPS_JALR: // Ignore this optimization relocation for now break; case R_MIPS_PC16: - checkAlignment<4>(Loc, Val, Type); - checkInt<18>(Loc, Val, Type); - writeRelocation<E>(Loc, Val, 16, 2); + checkAlignment(Loc, Val, 4, Type); + checkInt(Loc, Val, 18, Type); + writeValue<E>(Loc, Val, 16, 2); break; case R_MIPS_PC19_S2: - checkAlignment<4>(Loc, Val, Type); - checkInt<21>(Loc, Val, Type); - writeRelocation<E>(Loc, Val, 19, 2); + checkAlignment(Loc, Val, 4, Type); + checkInt(Loc, Val, 21, Type); + writeValue<E>(Loc, Val, 19, 2); break; case R_MIPS_PC21_S2: - checkAlignment<4>(Loc, Val, Type); - checkInt<23>(Loc, Val, Type); - writeRelocation<E>(Loc, Val, 21, 2); + checkAlignment(Loc, Val, 4, Type); + checkInt(Loc, Val, 23, Type); + writeValue<E>(Loc, Val, 21, 2); break; case R_MIPS_PC26_S2: - checkAlignment<4>(Loc, Val, Type); - checkInt<28>(Loc, Val, Type); - writeRelocation<E>(Loc, Val, 26, 2); + checkAlignment(Loc, Val, 4, Type); + checkInt(Loc, Val, 28, Type); + writeValue<E>(Loc, Val, 26, 2); break; case R_MIPS_PC32: - writeRelocation<E>(Loc, Val, 32, 0); + writeValue<E>(Loc, Val, 32, 0); break; case R_MICROMIPS_26_S1: case R_MICROMIPS_PC26_S1: - checkInt<27>(Loc, Val, Type); - writeMicroRelocation32<E>(Loc, Val, 26, 1); + checkInt(Loc, Val, 27, Type); + writeShuffleValue<E>(Loc, Val, 26, 1); break; case R_MICROMIPS_PC7_S1: - checkInt<8>(Loc, Val, Type); + checkInt(Loc, Val, 8, Type); writeMicroRelocation16<E>(Loc, Val, 7, 1); break; case R_MICROMIPS_PC10_S1: - checkInt<11>(Loc, Val, Type); + checkInt(Loc, Val, 11, Type); writeMicroRelocation16<E>(Loc, Val, 10, 1); break; case R_MICROMIPS_PC16_S1: - checkInt<17>(Loc, Val, Type); - writeMicroRelocation32<E>(Loc, Val, 16, 1); + checkInt(Loc, Val, 17, Type); + writeShuffleValue<E>(Loc, Val, 16, 1); break; case R_MICROMIPS_PC18_S3: - checkInt<21>(Loc, Val, Type); - writeMicroRelocation32<E>(Loc, Val, 18, 3); + checkInt(Loc, Val, 21, Type); + writeShuffleValue<E>(Loc, Val, 18, 3); break; case R_MICROMIPS_PC19_S2: - checkInt<21>(Loc, Val, Type); - writeMicroRelocation32<E>(Loc, Val, 19, 2); + checkInt(Loc, Val, 21, Type); + writeShuffleValue<E>(Loc, Val, 19, 2); break; case R_MICROMIPS_PC21_S1: - checkInt<22>(Loc, Val, Type); - writeMicroRelocation32<E>(Loc, Val, 21, 1); + checkInt(Loc, Val, 22, Type); + writeShuffleValue<E>(Loc, Val, 21, 1); break; case R_MICROMIPS_PC23_S2: - checkInt<25>(Loc, Val, Type); - writeMicroRelocation32<E>(Loc, Val, 23, 2); + checkInt(Loc, Val, 25, Type); + writeShuffleValue<E>(Loc, Val, 23, 2); break; default: error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); @@ -655,19 +635,26 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { template <class ELFT> bool MIPS<ELFT>::usesOnlyLowPageBits(RelType Type) const { return Type == R_MIPS_LO16 || Type == R_MIPS_GOT_OFST || - Type == R_MICROMIPS_LO16 || Type == R_MICROMIPS_GOT_OFST; + Type == R_MICROMIPS_LO16; } // Return true if the symbol is a PIC function. template <class ELFT> bool elf::isMipsPIC(const Defined *Sym) { - typedef typename ELFT::Ehdr Elf_Ehdr; - if (!Sym->Section || !Sym->isFunc()) + if (!Sym->isFunc()) + return false; + + if (Sym->StOther & STO_MIPS_PIC) + return true; + + if (!Sym->Section) + return false; + + ObjFile<ELFT> *File = + cast<InputSectionBase>(Sym->Section)->template getFile<ELFT>(); + if (!File) return false; - auto *Sec = cast<InputSectionBase>(Sym->Section); - const Elf_Ehdr *Hdr = Sec->template getFile<ELFT>()->getObj().getHeader(); - return (Sym->StOther & STO_MIPS_MIPS16) == STO_MIPS_PIC || - (Hdr->e_flags & EF_MIPS_PIC); + return File->getObj().getHeader()->e_flags & EF_MIPS_PIC; } template <class ELFT> TargetInfo *elf::getMipsTargetInfo() { diff --git a/contrib/llvm/tools/lld/ELF/Arch/MipsArchTree.cpp b/contrib/llvm/tools/lld/ELF/Arch/MipsArchTree.cpp index 754a47001579..98ceac3075e0 100644 --- a/contrib/llvm/tools/lld/ELF/Arch/MipsArchTree.cpp +++ b/contrib/llvm/tools/lld/ELF/Arch/MipsArchTree.cpp @@ -65,25 +65,30 @@ static StringRef getNanName(bool IsNan2008) { static StringRef getFpName(bool IsFp64) { return IsFp64 ? "64" : "32"; } static void checkFlags(ArrayRef<FileFlags> Files) { + assert(!Files.empty() && "expected non-empty file list"); + uint32_t ABI = Files[0].Flags & (EF_MIPS_ABI | EF_MIPS_ABI2); bool Nan = Files[0].Flags & EF_MIPS_NAN2008; bool Fp = Files[0].Flags & EF_MIPS_FP64; - for (const FileFlags &F : Files.slice(1)) { + for (const FileFlags &F : Files) { + if (Config->Is64 && F.Flags & EF_MIPS_MICROMIPS) + error(toString(F.File) + ": microMIPS 64-bit is not supported"); + uint32_t ABI2 = F.Flags & (EF_MIPS_ABI | EF_MIPS_ABI2); if (ABI != ABI2) - error("target ABI '" + getAbiName(ABI) + "' is incompatible with '" + - getAbiName(ABI2) + "': " + toString(F.File)); + error(toString(F.File) + ": ABI '" + getAbiName(ABI2) + + "' is incompatible with target ABI '" + getAbiName(ABI) + "'"); bool Nan2 = F.Flags & EF_MIPS_NAN2008; if (Nan != Nan2) - error("target -mnan=" + getNanName(Nan) + " is incompatible with -mnan=" + - getNanName(Nan2) + ": " + toString(F.File)); + error(toString(F.File) + ": -mnan=" + getNanName(Nan2) + + " is incompatible with target -mnan=" + getNanName(Nan)); bool Fp2 = F.Flags & EF_MIPS_FP64; if (Fp != Fp2) - error("target -mfp" + getFpName(Fp) + " is incompatible with -mfp" + - getFpName(Fp2) + ": " + toString(F.File)); + error(toString(F.File) + ": -mfp" + getFpName(Fp2) + + " is incompatible with target -mfp" + getFpName(Fp)); } } @@ -102,11 +107,13 @@ static uint32_t getPicFlags(ArrayRef<FileFlags> Files) { for (const FileFlags &F : Files.slice(1)) { bool IsPic2 = F.Flags & (EF_MIPS_PIC | EF_MIPS_CPIC); if (IsPic && !IsPic2) - warn("linking abicalls code " + toString(Files[0].File) + - " with non-abicalls file: " + toString(F.File)); + warn(toString(F.File) + + ": linking non-abicalls code with abicalls code " + + toString(Files[0].File)); if (!IsPic && IsPic2) - warn("linking non-abicalls code " + toString(Files[0].File) + - " with abicalls file: " + toString(F.File)); + warn(toString(F.File) + + ": linking abicalls code with non-abicalls code " + + toString(Files[0].File)); } // Compute the result PIC/non-PIC flag. @@ -326,7 +333,7 @@ static StringRef getMipsFpAbiName(uint8_t FpAbi) { case Mips::Val_GNU_MIPS_ABI_FP_SOFT: return "-msoft-float"; case Mips::Val_GNU_MIPS_ABI_FP_OLD_64: - return "-mips32r2 -mfp64 (old)"; + return "-mgp32 -mfp64 (old)"; case Mips::Val_GNU_MIPS_ABI_FP_XX: return "-mfpxx"; case Mips::Val_GNU_MIPS_ABI_FP_64: @@ -343,9 +350,9 @@ uint8_t elf::getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag, if (compareMipsFpAbi(NewFlag, OldFlag) >= 0) return NewFlag; if (compareMipsFpAbi(OldFlag, NewFlag) < 0) - error("target floating point ABI '" + getMipsFpAbiName(OldFlag) + - "' is incompatible with '" + getMipsFpAbiName(NewFlag) + - "': " + FileName); + error(FileName + ": floating point ABI '" + getMipsFpAbiName(NewFlag) + + "' is incompatible with target floating point ABI '" + + getMipsFpAbiName(OldFlag) + "'"); return OldFlag; } diff --git a/contrib/llvm/tools/lld/ELF/Arch/PPC.cpp b/contrib/llvm/tools/lld/ELF/Arch/PPC.cpp index 6af0df331df6..20cae0e59cf4 100644 --- a/contrib/llvm/tools/lld/ELF/Arch/PPC.cpp +++ b/contrib/llvm/tools/lld/ELF/Arch/PPC.cpp @@ -21,13 +21,18 @@ using namespace lld::elf; namespace { class PPC final : public TargetInfo { public: - PPC() { GotBaseSymOff = 0x8000; } + PPC(); void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const override; }; } // namespace +PPC::PPC() { + GotBaseSymOff = 0x8000; + GotBaseSymInGotPlt = false; +} + RelExpr PPC::getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const { switch (Type) { diff --git a/contrib/llvm/tools/lld/ELF/Arch/PPC64.cpp b/contrib/llvm/tools/lld/ELF/Arch/PPC64.cpp index ac4021b5918d..fa3bf6c62a0d 100644 --- a/contrib/llvm/tools/lld/ELF/Arch/PPC64.cpp +++ b/contrib/llvm/tools/lld/ELF/Arch/PPC64.cpp @@ -14,12 +14,14 @@ #include "llvm/Support/Endian.h" using namespace llvm; +using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; using namespace lld; using namespace lld::elf; static uint64_t PPC64TocOffset = 0x8000; +static uint64_t DynamicThreadPointerOffset = 0x8000; uint64_t elf::getPPC64TocBase() { // The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The @@ -39,11 +41,21 @@ namespace { class PPC64 final : public TargetInfo { public: PPC64(); + uint32_t calcEFlags() const override; RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const override; + void writePltHeader(uint8_t *Buf) const override; void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void writeGotHeader(uint8_t *Buf) const override; + bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, + uint64_t BranchAddr, const Symbol &S) const override; + RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, + RelExpr Expr) const override; + void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; }; } // namespace @@ -51,21 +63,35 @@ public: // #higher(value), #highera(value), #highest(value), and #highesta(value) // macros defined in section 4.5.1. Relocation Types of the PPC-elf64abi // document. -static uint16_t applyPPCLo(uint64_t V) { return V; } -static uint16_t applyPPCHi(uint64_t V) { return V >> 16; } -static uint16_t applyPPCHa(uint64_t V) { return (V + 0x8000) >> 16; } -static uint16_t applyPPCHigher(uint64_t V) { return V >> 32; } -static uint16_t applyPPCHighera(uint64_t V) { return (V + 0x8000) >> 32; } -static uint16_t applyPPCHighest(uint64_t V) { return V >> 48; } -static uint16_t applyPPCHighesta(uint64_t V) { return (V + 0x8000) >> 48; } +static uint16_t lo(uint64_t V) { return V; } +static uint16_t hi(uint64_t V) { return V >> 16; } +static uint16_t ha(uint64_t V) { return (V + 0x8000) >> 16; } +static uint16_t higher(uint64_t V) { return V >> 32; } +static uint16_t highera(uint64_t V) { return (V + 0x8000) >> 32; } +static uint16_t highest(uint64_t V) { return V >> 48; } +static uint16_t highesta(uint64_t V) { return (V + 0x8000) >> 48; } PPC64::PPC64() { - PltRel = GotRel = R_PPC64_GLOB_DAT; + GotRel = R_PPC64_GLOB_DAT; + PltRel = R_PPC64_JMP_SLOT; RelativeRel = R_PPC64_RELATIVE; + IRelativeRel = R_PPC64_IRELATIVE; GotEntrySize = 8; + PltEntrySize = 4; GotPltEntrySize = 8; - PltEntrySize = 32; - PltHeaderSize = 0; + GotBaseSymInGotPlt = false; + GotBaseSymOff = 0x8000; + GotHeaderEntriesNum = 1; + GotPltHeaderEntriesNum = 2; + PltHeaderSize = 60; + NeedsThunks = true; + TcbSize = 8; + TlsTpOffset = 0x7000; + + TlsModuleIndexRel = R_PPC64_DTPMOD64; + TlsOffsetRel = R_PPC64_DTPREL64; + + TlsGotRel = R_PPC64_TPREL64; // We need 64K pages (at least under glibc/Linux, the loader won't // set different permissions on a finer granularity than that). @@ -80,6 +106,110 @@ PPC64::PPC64() { // And because the lowest non-zero 256M boundary is 0x10000000, PPC64 linkers // use 0x10000000 as the starting address. DefaultImageBase = 0x10000000; + + TrapInstr = + (Config->IsLE == sys::IsLittleEndianHost) ? 0x7fe00008 : 0x0800e07f; +} + +static uint32_t getEFlags(InputFile *File) { + if (Config->EKind == ELF64BEKind) + return cast<ObjFile<ELF64BE>>(File)->getObj().getHeader()->e_flags; + return cast<ObjFile<ELF64LE>>(File)->getObj().getHeader()->e_flags; +} + +// This file implements v2 ABI. This function makes sure that all +// object files have v2 or an unspecified version as an ABI version. +uint32_t PPC64::calcEFlags() const { + for (InputFile *F : ObjectFiles) { + uint32_t Flag = getEFlags(F); + if (Flag == 1) + error(toString(F) + ": ABI version 1 is not supported"); + else if (Flag > 2) + error(toString(F) + ": unrecognized e_flags: " + Twine(Flag)); + } + return 2; +} + +void PPC64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { + // Reference: 3.7.4.2 of the 64-bit ELF V2 abi supplement. + // The general dynamic code sequence for a global `x` will look like: + // Instruction Relocation Symbol + // addis r3, r2, x@got@tlsgd@ha R_PPC64_GOT_TLSGD16_HA x + // addi r3, r3, x@got@tlsgd@l R_PPC64_GOT_TLSGD16_LO x + // bl __tls_get_addr(x@tlsgd) R_PPC64_TLSGD x + // R_PPC64_REL24 __tls_get_addr + // nop None None + + // Relaxing to local exec entails converting: + // addis r3, r2, x@got@tlsgd@ha into nop + // addi r3, r3, x@got@tlsgd@l into addis r3, r13, x@tprel@ha + // bl __tls_get_addr(x@tlsgd) into nop + // nop into addi r3, r3, x@tprel@l + + uint32_t EndianOffset = Config->EKind == ELF64BEKind ? 2U : 0U; + + switch (Type) { + case R_PPC64_GOT_TLSGD16_HA: + write32(Loc - EndianOffset, 0x60000000); // nop + break; + case R_PPC64_GOT_TLSGD16_LO: + write32(Loc - EndianOffset, 0x3c6d0000); // addis r3, r13 + relocateOne(Loc, R_PPC64_TPREL16_HA, Val); + break; + case R_PPC64_TLSGD: + write32(Loc, 0x60000000); // nop + write32(Loc + 4, 0x38630000); // addi r3, r3 + relocateOne(Loc + 4 + EndianOffset, R_PPC64_TPREL16_LO, Val); + break; + default: + llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); + } +} + + +void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { + // Reference: 3.7.4.3 of the 64-bit ELF V2 abi supplement. + // The local dynamic code sequence for a global `x` will look like: + // Instruction Relocation Symbol + // addis r3, r2, x@got@tlsld@ha R_PPC64_GOT_TLSLD16_HA x + // addi r3, r3, x@got@tlsld@l R_PPC64_GOT_TLSLD16_LO x + // bl __tls_get_addr(x@tlsgd) R_PPC64_TLSLD x + // R_PPC64_REL24 __tls_get_addr + // nop None None + + // Relaxing to local exec entails converting: + // addis r3, r2, x@got@tlsld@ha into nop + // addi r3, r3, x@got@tlsld@l into addis r3, r13, 0 + // bl __tls_get_addr(x@tlsgd) into nop + // nop into addi r3, r3, 4096 + + uint32_t EndianOffset = Config->EKind == ELF64BEKind ? 2U : 0U; + switch (Type) { + case R_PPC64_GOT_TLSLD16_HA: + write32(Loc - EndianOffset, 0x60000000); // nop + break; + case R_PPC64_GOT_TLSLD16_LO: + write32(Loc - EndianOffset, 0x3c6d0000); // addis r3, r13, 0 + break; + case R_PPC64_TLSLD: + write32(Loc, 0x60000000); // nop + write32(Loc + 4, 0x38631000); // addi r3, r3, 4096 + break; + case R_PPC64_DTPREL16: + case R_PPC64_DTPREL16_HA: + case R_PPC64_DTPREL16_HI: + case R_PPC64_DTPREL16_DS: + case R_PPC64_DTPREL16_LO: + case R_PPC64_DTPREL16_LO_DS: + case R_PPC64_GOT_DTPREL16_HA: + case R_PPC64_GOT_DTPREL16_LO_DS: + case R_PPC64_GOT_DTPREL16_DS: + case R_PPC64_GOT_DTPREL16_HI: + relocateOne(Loc, Type, Val); + break; + default: + llvm_unreachable("unsupported relocation for TLS LD to LE relaxation"); + } } RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S, @@ -95,48 +225,162 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S, case R_PPC64_TOC: return R_PPC_TOC; case R_PPC64_REL24: - return R_PPC_PLT_OPD; + return R_PPC_CALL_PLT; + case R_PPC64_REL16_LO: + case R_PPC64_REL16_HA: + case R_PPC64_REL32: + case R_PPC64_REL64: + return R_PC; + case R_PPC64_GOT_TLSGD16: + case R_PPC64_GOT_TLSGD16_HA: + case R_PPC64_GOT_TLSGD16_HI: + case R_PPC64_GOT_TLSGD16_LO: + return R_TLSGD_GOT; + case R_PPC64_GOT_TLSLD16: + case R_PPC64_GOT_TLSLD16_HA: + case R_PPC64_GOT_TLSLD16_HI: + case R_PPC64_GOT_TLSLD16_LO: + return R_TLSLD_GOT; + case R_PPC64_GOT_TPREL16_HA: + case R_PPC64_GOT_TPREL16_LO_DS: + case R_PPC64_GOT_TPREL16_DS: + case R_PPC64_GOT_TPREL16_HI: + return R_GOT_OFF; + case R_PPC64_GOT_DTPREL16_HA: + case R_PPC64_GOT_DTPREL16_LO_DS: + case R_PPC64_GOT_DTPREL16_DS: + case R_PPC64_GOT_DTPREL16_HI: + return R_TLSLD_GOT_OFF; + case R_PPC64_TPREL16: + case R_PPC64_TPREL16_HA: + case R_PPC64_TPREL16_LO: + case R_PPC64_TPREL16_HI: + case R_PPC64_TPREL16_DS: + case R_PPC64_TPREL16_LO_DS: + case R_PPC64_TPREL16_HIGHER: + case R_PPC64_TPREL16_HIGHERA: + case R_PPC64_TPREL16_HIGHEST: + case R_PPC64_TPREL16_HIGHESTA: + return R_TLS; + case R_PPC64_DTPREL16: + case R_PPC64_DTPREL16_DS: + case R_PPC64_DTPREL16_HA: + case R_PPC64_DTPREL16_HI: + case R_PPC64_DTPREL16_HIGHER: + case R_PPC64_DTPREL16_HIGHERA: + case R_PPC64_DTPREL16_HIGHEST: + case R_PPC64_DTPREL16_HIGHESTA: + case R_PPC64_DTPREL16_LO: + case R_PPC64_DTPREL16_LO_DS: + case R_PPC64_DTPREL64: + return R_ABS; + case R_PPC64_TLSGD: + return R_TLSDESC_CALL; + case R_PPC64_TLSLD: + return R_TLSLD_HINT; + case R_PPC64_TLS: + return R_HINT; default: return R_ABS; } } +void PPC64::writeGotHeader(uint8_t *Buf) const { + write64(Buf, getPPC64TocBase()); +} + +void PPC64::writePltHeader(uint8_t *Buf) const { + // The generic resolver stub goes first. + write32(Buf + 0, 0x7c0802a6); // mflr r0 + write32(Buf + 4, 0x429f0005); // bcl 20,4*cr7+so,8 <_glink+0x8> + write32(Buf + 8, 0x7d6802a6); // mflr r11 + write32(Buf + 12, 0x7c0803a6); // mtlr r0 + write32(Buf + 16, 0x7d8b6050); // subf r12, r11, r12 + write32(Buf + 20, 0x380cffcc); // subi r0,r12,52 + write32(Buf + 24, 0x7800f082); // srdi r0,r0,62,2 + write32(Buf + 28, 0xe98b002c); // ld r12,44(r11) + write32(Buf + 32, 0x7d6c5a14); // add r11,r12,r11 + write32(Buf + 36, 0xe98b0000); // ld r12,0(r11) + write32(Buf + 40, 0xe96b0008); // ld r11,8(r11) + write32(Buf + 44, 0x7d8903a6); // mtctr r12 + write32(Buf + 48, 0x4e800420); // bctr + + // The 'bcl' instruction will set the link register to the address of the + // following instruction ('mflr r11'). Here we store the offset from that + // instruction to the first entry in the GotPlt section. + int64_t GotPltOffset = InX::GotPlt->getVA() - (InX::Plt->getVA() + 8); + write64(Buf + 52, GotPltOffset); +} + void PPC64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const { - uint64_t Off = GotPltEntryAddr - getPPC64TocBase(); - - // FIXME: What we should do, in theory, is get the offset of the function - // descriptor in the .opd section, and use that as the offset from %r2 (the - // TOC-base pointer). Instead, we have the GOT-entry offset, and that will - // be a pointer to the function descriptor in the .opd section. Using - // this scheme is simpler, but requires an extra indirection per PLT dispatch. - - write32be(Buf, 0xf8410028); // std %r2, 40(%r1) - write32be(Buf + 4, 0x3d620000 | applyPPCHa(Off)); // addis %r11, %r2, X@ha - write32be(Buf + 8, 0xe98b0000 | applyPPCLo(Off)); // ld %r12, X@l(%r11) - write32be(Buf + 12, 0xe96c0000); // ld %r11,0(%r12) - write32be(Buf + 16, 0x7d6903a6); // mtctr %r11 - write32be(Buf + 20, 0xe84c0008); // ld %r2,8(%r12) - write32be(Buf + 24, 0xe96c0010); // ld %r11,16(%r12) - write32be(Buf + 28, 0x4e800420); // bctr + int32_t Offset = PltHeaderSize + Index * PltEntrySize; + // bl __glink_PLTresolve + write32(Buf, 0x48000000 | ((-Offset) & 0x03FFFFFc)); } static std::pair<RelType, uint64_t> toAddr16Rel(RelType Type, uint64_t Val) { - uint64_t V = Val - PPC64TocOffset; + // Relocations relative to the toc-base need to be adjusted by the Toc offset. + uint64_t TocBiasedVal = Val - PPC64TocOffset; + // Relocations relative to dtv[dtpmod] need to be adjusted by the DTP offset. + uint64_t DTPBiasedVal = Val - DynamicThreadPointerOffset; + switch (Type) { + // TOC biased relocation. + case R_PPC64_GOT_TLSGD16: + case R_PPC64_GOT_TLSLD16: case R_PPC64_TOC16: - return {R_PPC64_ADDR16, V}; + return {R_PPC64_ADDR16, TocBiasedVal}; case R_PPC64_TOC16_DS: - return {R_PPC64_ADDR16_DS, V}; + case R_PPC64_GOT_TPREL16_DS: + case R_PPC64_GOT_DTPREL16_DS: + return {R_PPC64_ADDR16_DS, TocBiasedVal}; + case R_PPC64_GOT_TLSGD16_HA: + case R_PPC64_GOT_TLSLD16_HA: + case R_PPC64_GOT_TPREL16_HA: + case R_PPC64_GOT_DTPREL16_HA: case R_PPC64_TOC16_HA: - return {R_PPC64_ADDR16_HA, V}; + return {R_PPC64_ADDR16_HA, TocBiasedVal}; + case R_PPC64_GOT_TLSGD16_HI: + case R_PPC64_GOT_TLSLD16_HI: + case R_PPC64_GOT_TPREL16_HI: + case R_PPC64_GOT_DTPREL16_HI: case R_PPC64_TOC16_HI: - return {R_PPC64_ADDR16_HI, V}; + return {R_PPC64_ADDR16_HI, TocBiasedVal}; + case R_PPC64_GOT_TLSGD16_LO: + case R_PPC64_GOT_TLSLD16_LO: case R_PPC64_TOC16_LO: - return {R_PPC64_ADDR16_LO, V}; + return {R_PPC64_ADDR16_LO, TocBiasedVal}; case R_PPC64_TOC16_LO_DS: - return {R_PPC64_ADDR16_LO_DS, V}; + case R_PPC64_GOT_TPREL16_LO_DS: + case R_PPC64_GOT_DTPREL16_LO_DS: + return {R_PPC64_ADDR16_LO_DS, TocBiasedVal}; + + // Dynamic Thread pointer biased relocation types. + case R_PPC64_DTPREL16: + return {R_PPC64_ADDR16, DTPBiasedVal}; + case R_PPC64_DTPREL16_DS: + return {R_PPC64_ADDR16_DS, DTPBiasedVal}; + case R_PPC64_DTPREL16_HA: + return {R_PPC64_ADDR16_HA, DTPBiasedVal}; + case R_PPC64_DTPREL16_HI: + return {R_PPC64_ADDR16_HI, DTPBiasedVal}; + case R_PPC64_DTPREL16_HIGHER: + return {R_PPC64_ADDR16_HIGHER, DTPBiasedVal}; + case R_PPC64_DTPREL16_HIGHERA: + return {R_PPC64_ADDR16_HIGHERA, DTPBiasedVal}; + case R_PPC64_DTPREL16_HIGHEST: + return {R_PPC64_ADDR16_HIGHEST, DTPBiasedVal}; + case R_PPC64_DTPREL16_HIGHESTA: + return {R_PPC64_ADDR16_HIGHESTA, DTPBiasedVal}; + case R_PPC64_DTPREL16_LO: + return {R_PPC64_ADDR16_LO, DTPBiasedVal}; + case R_PPC64_DTPREL16_LO_DS: + return {R_PPC64_ADDR16_LO_DS, DTPBiasedVal}; + case R_PPC64_DTPREL64: + return {R_PPC64_ADDR64, DTPBiasedVal}; + default: return {Type, Val}; } @@ -149,68 +393,139 @@ void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { switch (Type) { case R_PPC64_ADDR14: { - checkAlignment<4>(Loc, Val, Type); + checkAlignment(Loc, Val, 4, Type); // Preserve the AA/LK bits in the branch instruction uint8_t AALK = Loc[3]; - write16be(Loc + 2, (AALK & 3) | (Val & 0xfffc)); + write16(Loc + 2, (AALK & 3) | (Val & 0xfffc)); break; } case R_PPC64_ADDR16: - checkInt<16>(Loc, Val, Type); - write16be(Loc, Val); + case R_PPC64_TPREL16: + checkInt(Loc, Val, 16, Type); + write16(Loc, Val); break; case R_PPC64_ADDR16_DS: - checkInt<16>(Loc, Val, Type); - write16be(Loc, (read16be(Loc) & 3) | (Val & ~3)); + case R_PPC64_TPREL16_DS: + checkInt(Loc, Val, 16, Type); + write16(Loc, (read16(Loc) & 3) | (Val & ~3)); break; case R_PPC64_ADDR16_HA: case R_PPC64_REL16_HA: - write16be(Loc, applyPPCHa(Val)); + case R_PPC64_TPREL16_HA: + write16(Loc, ha(Val)); break; case R_PPC64_ADDR16_HI: case R_PPC64_REL16_HI: - write16be(Loc, applyPPCHi(Val)); + case R_PPC64_TPREL16_HI: + write16(Loc, hi(Val)); break; case R_PPC64_ADDR16_HIGHER: - write16be(Loc, applyPPCHigher(Val)); + case R_PPC64_TPREL16_HIGHER: + write16(Loc, higher(Val)); break; case R_PPC64_ADDR16_HIGHERA: - write16be(Loc, applyPPCHighera(Val)); + case R_PPC64_TPREL16_HIGHERA: + write16(Loc, highera(Val)); break; case R_PPC64_ADDR16_HIGHEST: - write16be(Loc, applyPPCHighest(Val)); + case R_PPC64_TPREL16_HIGHEST: + write16(Loc, highest(Val)); break; case R_PPC64_ADDR16_HIGHESTA: - write16be(Loc, applyPPCHighesta(Val)); + case R_PPC64_TPREL16_HIGHESTA: + write16(Loc, highesta(Val)); break; case R_PPC64_ADDR16_LO: - write16be(Loc, applyPPCLo(Val)); + case R_PPC64_REL16_LO: + case R_PPC64_TPREL16_LO: + write16(Loc, lo(Val)); break; case R_PPC64_ADDR16_LO_DS: - case R_PPC64_REL16_LO: - write16be(Loc, (read16be(Loc) & 3) | (applyPPCLo(Val) & ~3)); + case R_PPC64_TPREL16_LO_DS: + write16(Loc, (read16(Loc) & 3) | (lo(Val) & ~3)); break; case R_PPC64_ADDR32: case R_PPC64_REL32: - checkInt<32>(Loc, Val, Type); - write32be(Loc, Val); + checkInt(Loc, Val, 32, Type); + write32(Loc, Val); break; case R_PPC64_ADDR64: case R_PPC64_REL64: case R_PPC64_TOC: - write64be(Loc, Val); + write64(Loc, Val); break; case R_PPC64_REL24: { uint32_t Mask = 0x03FFFFFC; - checkInt<24>(Loc, Val, Type); - write32be(Loc, (read32be(Loc) & ~Mask) | (Val & Mask)); + checkInt(Loc, Val, 24, Type); + write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask)); break; } + case R_PPC64_DTPREL64: + write64(Loc, Val - DynamicThreadPointerOffset); + break; default: error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); } } +bool PPC64::needsThunk(RelExpr Expr, RelType Type, const InputFile *File, + uint64_t BranchAddr, const Symbol &S) const { + // If a function is in the plt it needs to be called through + // a call stub. + return Type == R_PPC64_REL24 && S.isInPlt(); +} + +RelExpr PPC64::adjustRelaxExpr(RelType Type, const uint8_t *Data, + RelExpr Expr) const { + if (Expr == R_RELAX_TLS_GD_TO_IE) + return R_RELAX_TLS_GD_TO_IE_GOT_OFF; + if (Expr == R_RELAX_TLS_LD_TO_LE) + return R_RELAX_TLS_LD_TO_LE_ABS; + return Expr; +} + +// Reference: 3.7.4.1 of the 64-bit ELF V2 abi supplement. +// The general dynamic code sequence for a global `x` uses 4 instructions. +// Instruction Relocation Symbol +// addis r3, r2, x@got@tlsgd@ha R_PPC64_GOT_TLSGD16_HA x +// addi r3, r3, x@got@tlsgd@l R_PPC64_GOT_TLSGD16_LO x +// bl __tls_get_addr(x@tlsgd) R_PPC64_TLSGD x +// R_PPC64_REL24 __tls_get_addr +// nop None None +// +// Relaxing to initial-exec entails: +// 1) Convert the addis/addi pair that builds the address of the tls_index +// struct for 'x' to an addis/ld pair that loads an offset from a got-entry. +// 2) Convert the call to __tls_get_addr to a nop. +// 3) Convert the nop following the call to an add of the loaded offset to the +// thread pointer. +// Since the nop must directly follow the call, the R_PPC64_TLSGD relocation is +// used as the relaxation hint for both steps 2 and 3. +void PPC64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const { + switch (Type) { + case R_PPC64_GOT_TLSGD16_HA: + // This is relaxed from addis rT, r2, sym@got@tlsgd@ha to + // addis rT, r2, sym@got@tprel@ha. + relocateOne(Loc, R_PPC64_GOT_TPREL16_HA, Val); + return; + case R_PPC64_GOT_TLSGD16_LO: { + // Relax from addi r3, rA, sym@got@tlsgd@l to + // ld r3, sym@got@tprel@l(rA) + uint32_t EndianOffset = Config->EKind == ELF64BEKind ? 2U : 0U; + uint32_t InputRegister = (read32(Loc - EndianOffset) & (0x1f << 16)); + write32(Loc - EndianOffset, 0xE8600000 | InputRegister); + relocateOne(Loc, R_PPC64_GOT_TPREL16_LO_DS, Val); + return; + } + case R_PPC64_TLSGD: + write32(Loc, 0x60000000); // bl __tls_get_addr(sym@tlsgd) --> nop + write32(Loc + 4, 0x7c636A14); // nop --> add r3, r3, r13 + return; + default: + llvm_unreachable("unsupported relocation for TLS GD to IE relaxation"); + } +} + TargetInfo *elf::getPPC64TargetInfo() { static PPC64 Target; return &Target; diff --git a/contrib/llvm/tools/lld/ELF/Arch/SPARCV9.cpp b/contrib/llvm/tools/lld/ELF/Arch/SPARCV9.cpp index d9d6e1390407..36f5c836930e 100644 --- a/contrib/llvm/tools/lld/ELF/Arch/SPARCV9.cpp +++ b/contrib/llvm/tools/lld/ELF/Arch/SPARCV9.cpp @@ -77,23 +77,23 @@ void SPARCV9::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_SPARC_32: case R_SPARC_UA32: // V-word32 - checkUInt<32>(Loc, Val, Type); + checkUInt(Loc, Val, 32, Type); write32be(Loc, Val); break; case R_SPARC_DISP32: // V-disp32 - checkInt<32>(Loc, Val, Type); + checkInt(Loc, Val, 32, Type); write32be(Loc, Val); break; case R_SPARC_WDISP30: case R_SPARC_WPLT30: // V-disp30 - checkInt<32>(Loc, Val, Type); + checkInt(Loc, Val, 32, Type); write32be(Loc, (read32be(Loc) & ~0x3fffffff) | ((Val >> 2) & 0x3fffffff)); break; case R_SPARC_22: // V-imm22 - checkUInt<22>(Loc, Val, Type); + checkUInt(Loc, Val, 22, Type); write32be(Loc, (read32be(Loc) & ~0x003fffff) | (Val & 0x003fffff)); break; case R_SPARC_GOT22: @@ -103,7 +103,7 @@ void SPARCV9::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { break; case R_SPARC_WDISP19: // V-disp19 - checkInt<21>(Loc, Val, Type); + checkInt(Loc, Val, 21, Type); write32be(Loc, (read32be(Loc) & ~0x0007ffff) | ((Val >> 2) & 0x0007ffff)); break; case R_SPARC_GOT10: @@ -137,7 +137,7 @@ void SPARCV9::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, }; memcpy(Buf, PltData, sizeof(PltData)); - uint64_t Off = PltHeaderSize + Index * PltEntrySize; + uint64_t Off = getPltEntryOffset(Index); relocateOne(Buf, R_SPARC_22, Off); relocateOne(Buf + 4, R_SPARC_WDISP19, -(Off + 4 - PltEntrySize)); } diff --git a/contrib/llvm/tools/lld/ELF/Arch/X86.cpp b/contrib/llvm/tools/lld/ELF/Arch/X86.cpp index 94b46acdd896..0624fe78750c 100644 --- a/contrib/llvm/tools/lld/ELF/Arch/X86.cpp +++ b/contrib/llvm/tools/lld/ELF/Arch/X86.cpp @@ -46,7 +46,6 @@ public: } // namespace X86::X86() { - GotBaseSymOff = -1; CopyRel = R_386_COPY; GotRel = R_386_GLOB_DAT; PltRel = R_386_JUMP_SLOT; @@ -78,9 +77,9 @@ RelExpr X86::getRelExpr(RelType Type, const Symbol &S, case R_386_TLS_LDO_32: return R_ABS; case R_386_TLS_GD: - return R_TLSGD; + return R_TLSGD_GOT_FROM_END; case R_386_TLS_LDM: - return R_TLSLD; + return R_TLSLD_GOT_FROM_END; case R_386_PLT32: return R_PLT_PC; case R_386_PC8: @@ -228,7 +227,7 @@ void X86::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, } write32le(Buf + 7, RelOff); - write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16); + write32le(Buf + 12, -getPltEntryOffset(Index) - 16); } int64_t X86::getImplicitAddend(const uint8_t *Buf, RelType Type) const { @@ -260,15 +259,15 @@ void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { // R_386_{PC,}{8,16} are not part of the i386 psABI, but they are // being used for some 16-bit programs such as boot loaders, so // we want to support them. - checkUInt<8>(Loc, Val, Type); + checkIntUInt(Loc, Val, 8, Type); *Loc = Val; break; case R_386_PC8: - checkInt<8>(Loc, Val, Type); + checkInt(Loc, Val, 8, Type); *Loc = Val; break; case R_386_16: - checkUInt<16>(Loc, Val, Type); + checkIntUInt(Loc, Val, 16, Type); write16le(Loc, Val); break; case R_386_PC16: @@ -282,7 +281,7 @@ void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { // current location subtracted from it. // We just check that Val fits in 17 bits. This misses some cases, but // should have no false positives. - checkInt<17>(Loc, Val, Type); + checkInt(Loc, Val, 17, Type); write16le(Loc, Val); break; case R_386_32: @@ -305,7 +304,7 @@ void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_386_TLS_LE_32: case R_386_TLS_TPOFF: case R_386_TLS_TPOFF32: - checkInt<32>(Loc, Val, Type); + checkInt(Loc, Val, 32, Type); write32le(Loc, Val); break; default: @@ -448,6 +447,7 @@ void RetpolinePic::writePltHeader(uint8_t *Buf) const { 0x89, 0xc8, // 2b: mov %ecx, %eax 0x59, // 2d: pop %ecx 0xc3, // 2e: ret + 0xcc, // 2f: int3; padding }; memcpy(Buf, Insn, sizeof(Insn)); @@ -461,21 +461,23 @@ void RetpolinePic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const { const uint8_t Insn[] = { - 0x50, // pushl %eax - 0x8b, 0x83, 0, 0, 0, 0, // mov foo@GOT(%ebx), %eax - 0xe8, 0, 0, 0, 0, // call plt+0x20 - 0xe9, 0, 0, 0, 0, // jmp plt+0x12 - 0x68, 0, 0, 0, 0, // pushl $reloc_offset - 0xe9, 0, 0, 0, 0, // jmp plt+0 + 0x50, // pushl %eax + 0x8b, 0x83, 0, 0, 0, 0, // mov foo@GOT(%ebx), %eax + 0xe8, 0, 0, 0, 0, // call plt+0x20 + 0xe9, 0, 0, 0, 0, // jmp plt+0x12 + 0x68, 0, 0, 0, 0, // pushl $reloc_offset + 0xe9, 0, 0, 0, 0, // jmp plt+0 + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // int3; padding }; memcpy(Buf, Insn, sizeof(Insn)); uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize(); + unsigned Off = getPltEntryOffset(Index); write32le(Buf + 3, GotPltEntryAddr - Ebx); - write32le(Buf + 8, -Index * PltEntrySize - PltHeaderSize - 12 + 32); - write32le(Buf + 13, -Index * PltEntrySize - PltHeaderSize - 17 + 18); + write32le(Buf + 8, -Off - 12 + 32); + write32le(Buf + 13, -Off - 17 + 18); write32le(Buf + 18, RelOff); - write32le(Buf + 23, -Index * PltEntrySize - PltHeaderSize - 27); + write32le(Buf + 23, -Off - 27); } RetpolineNoPic::RetpolineNoPic() { @@ -488,7 +490,7 @@ void RetpolineNoPic::writeGotPlt(uint8_t *Buf, const Symbol &S) const { } void RetpolineNoPic::writePltHeader(uint8_t *Buf) const { - const uint8_t PltData[] = { + const uint8_t Insn[] = { 0xff, 0x35, 0, 0, 0, 0, // 0: pushl GOTPLT+4 0x50, // 6: pushl %eax 0xa1, 0, 0, 0, 0, // 7: mov GOTPLT+8, %eax @@ -504,8 +506,9 @@ void RetpolineNoPic::writePltHeader(uint8_t *Buf) const { 0x89, 0xc8, // 2b: mov %ecx, %eax 0x59, // 2d: pop %ecx 0xc3, // 2e: ret + 0xcc, // 2f: int3; padding }; - memcpy(Buf, PltData, sizeof(PltData)); + memcpy(Buf, Insn, sizeof(Insn)); uint32_t GotPlt = InX::GotPlt->getVA(); write32le(Buf + 2, GotPlt + 4); @@ -516,20 +519,23 @@ void RetpolineNoPic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const { const uint8_t Insn[] = { - 0x50, // 0: pushl %eax - 0xa1, 0, 0, 0, 0, // 1: mov foo_in_GOT, %eax - 0xe8, 0, 0, 0, 0, // 6: call plt+0x20 - 0xe9, 0, 0, 0, 0, // b: jmp plt+0x11 - 0x68, 0, 0, 0, 0, // 10: pushl $reloc_offset - 0xe9, 0, 0, 0, 0, // 15: jmp plt+0 + 0x50, // 0: pushl %eax + 0xa1, 0, 0, 0, 0, // 1: mov foo_in_GOT, %eax + 0xe8, 0, 0, 0, 0, // 6: call plt+0x20 + 0xe9, 0, 0, 0, 0, // b: jmp plt+0x11 + 0x68, 0, 0, 0, 0, // 10: pushl $reloc_offset + 0xe9, 0, 0, 0, 0, // 15: jmp plt+0 + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding + 0xcc, // 1f: int3; padding }; memcpy(Buf, Insn, sizeof(Insn)); + unsigned Off = getPltEntryOffset(Index); write32le(Buf + 2, GotPltEntryAddr); - write32le(Buf + 7, -Index * PltEntrySize - PltHeaderSize - 11 + 32); - write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16 + 17); + write32le(Buf + 7, -Off - 11 + 32); + write32le(Buf + 12, -Off - 16 + 17); write32le(Buf + 17, RelOff); - write32le(Buf + 22, -Index * PltEntrySize - PltHeaderSize - 26); + write32le(Buf + 22, -Off - 26); } TargetInfo *elf::getX86TargetInfo() { diff --git a/contrib/llvm/tools/lld/ELF/Arch/X86_64.cpp b/contrib/llvm/tools/lld/ELF/Arch/X86_64.cpp index 3db391e9f01c..d4bdb3730c58 100644 --- a/contrib/llvm/tools/lld/ELF/Arch/X86_64.cpp +++ b/contrib/llvm/tools/lld/ELF/Arch/X86_64.cpp @@ -28,7 +28,7 @@ public: X86_64(); RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const override; - bool isPicRel(RelType Type) const override; + RelType getDynRel(RelType Type) const override; void writeGotPltHeader(uint8_t *Buf) const override; void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; void writePltHeader(uint8_t *Buf) const override; @@ -43,6 +43,8 @@ public: void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; + bool adjustPrologueForCrossSplitStack(uint8_t *Loc, + uint8_t *End) const override; private: void relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op, @@ -51,7 +53,6 @@ private: } // namespace template <class ELFT> X86_64<ELFT>::X86_64() { - GotBaseSymOff = -1; CopyRel = R_X86_64_COPY; GotRel = R_X86_64_GLOB_DAT; PltRel = R_X86_64_JUMP_SLOT; @@ -106,6 +107,11 @@ RelExpr X86_64<ELFT>::getRelExpr(RelType Type, const Symbol &S, case R_X86_64_REX_GOTPCRELX: case R_X86_64_GOTTPOFF: return R_GOT_PC; + case R_X86_64_GOTOFF64: + return R_GOTREL_FROM_END; + case R_X86_64_GOTPC32: + case R_X86_64_GOTPC64: + return R_GOTONLY_PC_FROM_END; case R_X86_64_NONE: return R_NONE; default: @@ -124,7 +130,7 @@ template <class ELFT> void X86_64<ELFT>::writeGotPltHeader(uint8_t *Buf) const { template <class ELFT> void X86_64<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &S) const { // See comments in X86::writeGotPlt. - write32le(Buf, S.getPltVA() + 6); + write64le(Buf, S.getPltVA() + 6); } template <class ELFT> void X86_64<ELFT>::writePltHeader(uint8_t *Buf) const { @@ -153,12 +159,14 @@ void X86_64<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, write32le(Buf + 2, GotPltEntryAddr - PltEntryAddr - 6); write32le(Buf + 7, Index); - write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16); + write32le(Buf + 12, -getPltEntryOffset(Index) - 16); } -template <class ELFT> bool X86_64<ELFT>::isPicRel(RelType Type) const { - return Type != R_X86_64_PC32 && Type != R_X86_64_32 && - Type != R_X86_64_TPOFF32; +template <class ELFT> RelType X86_64<ELFT>::getDynRel(RelType Type) const { + if (Type == R_X86_64_64 || Type == R_X86_64_PC64 || Type == R_X86_64_SIZE32 || + Type == R_X86_64_SIZE64) + return Type; + return R_X86_64_NONE; } template <class ELFT> @@ -285,20 +293,21 @@ template <class ELFT> void X86_64<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { switch (Type) { case R_X86_64_8: - checkUInt<8>(Loc, Val, Type); + checkUInt(Loc, Val, 8, Type); *Loc = Val; break; case R_X86_64_16: - checkUInt<16>(Loc, Val, Type); + checkUInt(Loc, Val, 16, Type); write16le(Loc, Val); break; case R_X86_64_32: - checkUInt<32>(Loc, Val, Type); + checkUInt(Loc, Val, 32, Type); write32le(Loc, Val); break; case R_X86_64_32S: case R_X86_64_TPOFF32: case R_X86_64_GOT32: + case R_X86_64_GOTPC32: case R_X86_64_GOTPCREL: case R_X86_64_GOTPCRELX: case R_X86_64_REX_GOTPCRELX: @@ -309,7 +318,7 @@ void X86_64<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_X86_64_TLSLD: case R_X86_64_DTPOFF32: case R_X86_64_SIZE32: - checkInt<32>(Loc, Val, Type); + checkInt(Loc, Val, 32, Type); write32le(Loc, Val); break; case R_X86_64_64: @@ -318,6 +327,8 @@ void X86_64<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_X86_64_PC64: case R_X86_64_SIZE64: case R_X86_64_GOT64: + case R_X86_64_GOTOFF64: + case R_X86_64_GOTPC64: write64le(Loc, Val); break; default: @@ -460,6 +471,55 @@ void X86_64<ELFT>::relaxGot(uint8_t *Loc, uint64_t Val) const { write32le(Loc - 1, Val + 1); } +// This anonymous namespace works around a warning bug in +// old versions of gcc. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480 +namespace { + +// A split-stack prologue starts by checking the amount of stack remaining +// in one of two ways: +// A) Comparing of the stack pointer to a field in the tcb. +// B) Or a load of a stack pointer offset with an lea to r10 or r11. +template <> +bool X86_64<ELF64LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc, + uint8_t *End) const { + // Replace "cmp %fs:0x70,%rsp" and subsequent branch + // with "stc, nopl 0x0(%rax,%rax,1)" + if (Loc + 8 < End && memcmp(Loc, "\x64\x48\x3b\x24\x25", 4) == 0) { + memcpy(Loc, "\xf9\x0f\x1f\x84\x00\x00\x00\x00", 8); + return true; + } + + // Adjust "lea -0x200(%rsp),%r10" to lea "-0x4200(%rsp),%r10" + if (Loc + 7 < End && memcmp(Loc, "\x4c\x8d\x94\x24\x00\xfe\xff", 7) == 0) { + memcpy(Loc, "\x4c\x8d\x94\x24\x00\xbe\xff", 7); + return true; + } + + // Adjust "lea -0x200(%rsp),%r11" to lea "-0x4200(%rsp),%r11" + if (Loc + 7 < End && memcmp(Loc, "\x4c\x8d\x9c\x24\x00\xfe\xff", 7) == 0) { + memcpy(Loc, "\x4c\x8d\x9c\x24\x00\xbe\xff", 7); + return true; + } + return false; +} + +template <> +bool X86_64<ELF32LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc, + uint8_t *End) const { + llvm_unreachable("Target doesn't support split stacks."); +} + +} // namespace + +// These nonstandard PLT entries are to migtigate Spectre v2 security +// vulnerability. In order to mitigate Spectre v2, we want to avoid indirect +// branch instructions such as `jmp *GOTPLT(%rip)`. So, in the following PLT +// entries, we use a CALL followed by MOV and RET to do the same thing as an +// indirect jump. That instruction sequence is so-called "retpoline". +// +// We have two types of retpoline PLTs as a size optimization. If `-z now` +// is specified, all dynamic symbols are resolved at load-time. Thus, when +// that option is given, we can omit code for symbol lazy resolution. namespace { template <class ELFT> class Retpoline : public X86_64<ELFT> { public: @@ -487,7 +547,7 @@ template <class ELFT> Retpoline<ELFT>::Retpoline() { template <class ELFT> void Retpoline<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &S) const { - write32le(Buf, S.getPltVA() + 17); + write64le(Buf, S.getPltVA() + 17); } template <class ELFT> void Retpoline<ELFT>::writePltHeader(uint8_t *Buf) const { @@ -501,6 +561,8 @@ template <class ELFT> void Retpoline<ELFT>::writePltHeader(uint8_t *Buf) const { 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 19: int3; .align 16 0x4c, 0x89, 0x1c, 0x24, // 20: next: mov %r11, (%rsp) 0xc3, // 24: ret + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 25: int3; padding + 0xcc, 0xcc, 0xcc, 0xcc, // 2c: int3; padding }; memcpy(Buf, Insn, sizeof(Insn)); @@ -516,14 +578,15 @@ void Retpoline<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, unsigned RelOff) const { const uint8_t Insn[] = { 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 0: mov foo@GOTPLT(%rip), %r11 - 0xe8, 0, 0, 0, 0, // 7: callq plt+0x20 - 0xe9, 0, 0, 0, 0, // c: jmp plt+0x12 - 0x68, 0, 0, 0, 0, // 11: pushq <relocation index> - 0xe9, 0, 0, 0, 0, // 16: jmp plt+0 + 0xe8, 0, 0, 0, 0, // 7: callq plt+0x20 + 0xe9, 0, 0, 0, 0, // c: jmp plt+0x12 + 0x68, 0, 0, 0, 0, // 11: pushq <relocation index> + 0xe9, 0, 0, 0, 0, // 16: jmp plt+0 + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1b: int3; padding }; memcpy(Buf, Insn, sizeof(Insn)); - uint64_t Off = TargetInfo::PltHeaderSize + TargetInfo::PltEntrySize * Index; + uint64_t Off = TargetInfo::getPltEntryOffset(Index); write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7); write32le(Buf + 8, -Off - 12 + 32); @@ -547,6 +610,9 @@ void RetpolineZNow<ELFT>::writePltHeader(uint8_t *Buf) const { 0xcc, 0xcc, 0xcc, 0xcc, // c: int3; .align 16 0x4c, 0x89, 0x1c, 0x24, // 10: next: mov %r11, (%rsp) 0xc3, // 14: ret + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 15: int3; padding + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding + 0xcc, // 1f: int3; padding }; memcpy(Buf, Insn, sizeof(Insn)); } @@ -556,17 +622,17 @@ void RetpolineZNow<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const { const uint8_t Insn[] = { - 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // mov foo@GOTPLT(%rip), %r11 - 0xe9, 0, 0, 0, 0, // jmp plt+0 + 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // mov foo@GOTPLT(%rip), %r11 + 0xe9, 0, 0, 0, 0, // jmp plt+0 + 0xcc, 0xcc, 0xcc, 0xcc, // int3; padding }; memcpy(Buf, Insn, sizeof(Insn)); write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7); - write32le(Buf + 8, - -Index * TargetInfo::PltEntrySize - TargetInfo::PltHeaderSize - 12); + write32le(Buf + 8, -TargetInfo::getPltEntryOffset(Index) - 12); } -template <class ELFT> TargetInfo *getTargetInfo() { +template <class ELFT> static TargetInfo *getTargetInfo() { if (Config->ZRetpolineplt) { if (Config->ZNow) { static RetpolineZNow<ELFT> T; diff --git a/contrib/llvm/tools/lld/ELF/CMakeLists.txt b/contrib/llvm/tools/lld/ELF/CMakeLists.txt index 7ec837841315..fb2f53a72025 100644 --- a/contrib/llvm/tools/lld/ELF/CMakeLists.txt +++ b/contrib/llvm/tools/lld/ELF/CMakeLists.txt @@ -12,6 +12,7 @@ add_lld_library(lldELF Arch/AMDGPU.cpp Arch/ARM.cpp Arch/AVR.cpp + Arch/Hexagon.cpp Arch/Mips.cpp Arch/MipsArchTree.cpp Arch/PPC.cpp @@ -19,6 +20,7 @@ add_lld_library(lldELF Arch/SPARCV9.cpp Arch/X86.cpp Arch/X86_64.cpp + CallGraphSort.cpp Driver.cpp DriverUtils.cpp EhFrame.cpp @@ -35,7 +37,6 @@ add_lld_library(lldELF Relocations.cpp ScriptLexer.cpp ScriptParser.cpp - Strings.cpp SymbolTable.cpp Symbols.cpp SyntheticSections.cpp @@ -46,6 +47,7 @@ add_lld_library(lldELF LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} BinaryFormat + BitWriter Core DebugInfoDWARF LTO diff --git a/contrib/llvm/tools/lld/ELF/CallGraphSort.cpp b/contrib/llvm/tools/lld/ELF/CallGraphSort.cpp new file mode 100644 index 000000000000..33ac159a6e26 --- /dev/null +++ b/contrib/llvm/tools/lld/ELF/CallGraphSort.cpp @@ -0,0 +1,249 @@ +//===- CallGraphSort.cpp --------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// Implementation of Call-Chain Clustering from: Optimizing Function Placement +/// for Large-Scale Data-Center Applications +/// https://research.fb.com/wp-content/uploads/2017/01/cgo2017-hfsort-final1.pdf +/// +/// The goal of this algorithm is to improve runtime performance of the final +/// executable by arranging code sections such that page table and i-cache +/// misses are minimized. +/// +/// Definitions: +/// * Cluster +/// * An ordered list of input sections which are layed out as a unit. At the +/// beginning of the algorithm each input section has its own cluster and +/// the weight of the cluster is the sum of the weight of all incomming +/// edges. +/// * Call-Chain Clustering (C³) Heuristic +/// * Defines when and how clusters are combined. Pick the highest weighted +/// input section then add it to its most likely predecessor if it wouldn't +/// penalize it too much. +/// * Density +/// * The weight of the cluster divided by the size of the cluster. This is a +/// proxy for the ammount of execution time spent per byte of the cluster. +/// +/// It does so given a call graph profile by the following: +/// * Build a weighted call graph from the call graph profile +/// * Sort input sections by weight +/// * For each input section starting with the highest weight +/// * Find its most likely predecessor cluster +/// * Check if the combined cluster would be too large, or would have too low +/// a density. +/// * If not, then combine the clusters. +/// * Sort non-empty clusters by density +/// +//===----------------------------------------------------------------------===// + +#include "CallGraphSort.h" +#include "OutputSections.h" +#include "SymbolTable.h" +#include "Symbols.h" + +using namespace llvm; +using namespace lld; +using namespace lld::elf; + +namespace { +struct Edge { + int From; + uint64_t Weight; +}; + +struct Cluster { + Cluster(int Sec, size_t S) { + Sections.push_back(Sec); + Size = S; + } + + double getDensity() const { + if (Size == 0) + return 0; + return double(Weight) / double(Size); + } + + std::vector<int> Sections; + size_t Size = 0; + uint64_t Weight = 0; + uint64_t InitialWeight = 0; + std::vector<Edge> Preds; +}; + +class CallGraphSort { +public: + CallGraphSort(); + + DenseMap<const InputSectionBase *, int> run(); + +private: + std::vector<Cluster> Clusters; + std::vector<const InputSectionBase *> Sections; + + void groupClusters(); +}; + +// Maximum ammount the combined cluster density can be worse than the original +// cluster to consider merging. +constexpr int MAX_DENSITY_DEGRADATION = 8; + +// Maximum cluster size in bytes. +constexpr uint64_t MAX_CLUSTER_SIZE = 1024 * 1024; +} // end anonymous namespace + +// Take the edge list in Config->CallGraphProfile, resolve symbol names to +// Symbols, and generate a graph between InputSections with the provided +// weights. +CallGraphSort::CallGraphSort() { + llvm::MapVector<std::pair<const InputSectionBase *, const InputSectionBase *>, + uint64_t> &Profile = Config->CallGraphProfile; + DenseMap<const InputSectionBase *, int> SecToCluster; + + auto GetOrCreateNode = [&](const InputSectionBase *IS) -> int { + auto Res = SecToCluster.insert(std::make_pair(IS, Clusters.size())); + if (Res.second) { + Sections.push_back(IS); + Clusters.emplace_back(Clusters.size(), IS->getSize()); + } + return Res.first->second; + }; + + // Create the graph. + for (const auto &C : Profile) { + const auto *FromSB = cast<InputSectionBase>(C.first.first->Repl); + const auto *ToSB = cast<InputSectionBase>(C.first.second->Repl); + uint64_t Weight = C.second; + + // Ignore edges between input sections belonging to different output + // sections. This is done because otherwise we would end up with clusters + // containing input sections that can't actually be placed adjacently in the + // output. This messes with the cluster size and density calculations. We + // would also end up moving input sections in other output sections without + // moving them closer to what calls them. + if (FromSB->getOutputSection() != ToSB->getOutputSection()) + continue; + + int From = GetOrCreateNode(FromSB); + int To = GetOrCreateNode(ToSB); + + Clusters[To].Weight += Weight; + + if (From == To) + continue; + + // Add an edge + Clusters[To].Preds.push_back({From, Weight}); + } + for (Cluster &C : Clusters) + C.InitialWeight = C.Weight; +} + +// It's bad to merge clusters which would degrade the density too much. +static bool isNewDensityBad(Cluster &A, Cluster &B) { + double NewDensity = double(A.Weight + B.Weight) / double(A.Size + B.Size); + if (NewDensity < A.getDensity() / MAX_DENSITY_DEGRADATION) + return true; + return false; +} + +static void mergeClusters(Cluster &Into, Cluster &From) { + Into.Sections.insert(Into.Sections.end(), From.Sections.begin(), + From.Sections.end()); + Into.Size += From.Size; + Into.Weight += From.Weight; + From.Sections.clear(); + From.Size = 0; + From.Weight = 0; +} + +// Group InputSections into clusters using the Call-Chain Clustering heuristic +// then sort the clusters by density. +void CallGraphSort::groupClusters() { + std::vector<int> SortedSecs(Clusters.size()); + std::vector<Cluster *> SecToCluster(Clusters.size()); + + for (int SI = 0, SE = Clusters.size(); SI != SE; ++SI) { + SortedSecs[SI] = SI; + SecToCluster[SI] = &Clusters[SI]; + } + + std::stable_sort(SortedSecs.begin(), SortedSecs.end(), [&](int A, int B) { + return Clusters[B].getDensity() < Clusters[A].getDensity(); + }); + + for (int SI : SortedSecs) { + // Clusters[SI] is the same as SecToClusters[SI] here because it has not + // been merged into another cluster yet. + Cluster &C = Clusters[SI]; + + int BestPred = -1; + uint64_t BestWeight = 0; + + for (Edge &E : C.Preds) { + if (BestPred == -1 || E.Weight > BestWeight) { + BestPred = E.From; + BestWeight = E.Weight; + } + } + + // don't consider merging if the edge is unlikely. + if (BestWeight * 10 <= C.InitialWeight) + continue; + + Cluster *PredC = SecToCluster[BestPred]; + if (PredC == &C) + continue; + + if (C.Size + PredC->Size > MAX_CLUSTER_SIZE) + continue; + + if (isNewDensityBad(*PredC, C)) + continue; + + // NOTE: Consider using a disjoint-set to track section -> cluster mapping + // if this is ever slow. + for (int SI : C.Sections) + SecToCluster[SI] = PredC; + + mergeClusters(*PredC, C); + } + + // Remove empty or dead nodes. Invalidates all cluster indices. + llvm::erase_if(Clusters, [](const Cluster &C) { + return C.Size == 0 || C.Sections.empty(); + }); + + // Sort by density. + std::stable_sort(Clusters.begin(), Clusters.end(), + [](const Cluster &A, const Cluster &B) { + return A.getDensity() > B.getDensity(); + }); +} + +DenseMap<const InputSectionBase *, int> CallGraphSort::run() { + groupClusters(); + + // Generate order. + llvm::DenseMap<const InputSectionBase *, int> OrderMap; + ssize_t CurOrder = 1; + + for (const Cluster &C : Clusters) + for (int SecIndex : C.Sections) + OrderMap[Sections[SecIndex]] = CurOrder++; + + return OrderMap; +} + +// Sort sections by the profile data provided by -callgraph-profile-file +// +// This first builds a call graph based on the profile data then merges sections +// according to the C³ huristic. All clusters are then sorted by a density +// metric to further improve locality. +DenseMap<const InputSectionBase *, int> elf::computeCallGraphProfileOrder() { + return CallGraphSort().run(); +} diff --git a/contrib/llvm/tools/lld/ELF/CallGraphSort.h b/contrib/llvm/tools/lld/ELF/CallGraphSort.h new file mode 100644 index 000000000000..3f96dc88f435 --- /dev/null +++ b/contrib/llvm/tools/lld/ELF/CallGraphSort.h @@ -0,0 +1,23 @@ +//===- CallGraphSort.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_ELF_CALL_GRAPH_SORT_H +#define LLD_ELF_CALL_GRAPH_SORT_H + +#include "llvm/ADT/DenseMap.h" + +namespace lld { +namespace elf { +class InputSectionBase; + +llvm::DenseMap<const InputSectionBase *, int> computeCallGraphProfileOrder(); +} // namespace elf +} // namespace lld + +#endif diff --git a/contrib/llvm/tools/lld/ELF/Config.h b/contrib/llvm/tools/lld/ELF/Config.h index d705f11bf626..622324c13e2d 100644 --- a/contrib/llvm/tools/lld/ELF/Config.h +++ b/contrib/llvm/tools/lld/ELF/Config.h @@ -10,6 +10,7 @@ #ifndef LLD_ELF_CONFIG_H #define LLD_ELF_CONFIG_H +#include "lld/Common/ErrorHandler.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" @@ -17,13 +18,13 @@ #include "llvm/Support/CachePruning.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/Endian.h" - #include <vector> namespace lld { namespace elf { class InputFile; +class InputSectionBase; enum ELFKind { ELFNoneKind, @@ -39,6 +40,9 @@ enum class BuildIdKind { None, Fast, Md5, Sha1, Hexstring, Uuid }; // For --discard-{all,locals,none}. enum class DiscardPolicy { Default, All, Locals, None }; +// For --icf={none,safe,all}. +enum class ICFLevel { None, Safe, All }; + // For --strip-{all,debug}. enum class StripPolicy { None, All, Debug }; @@ -82,21 +86,27 @@ struct Configuration { llvm::StringMap<uint64_t> SectionStartMap; llvm::StringRef Chroot; llvm::StringRef DynamicLinker; + llvm::StringRef DwoDir; llvm::StringRef Entry; llvm::StringRef Emulation; llvm::StringRef Fini; llvm::StringRef Init; llvm::StringRef LTOAAPipeline; llvm::StringRef LTONewPmPasses; + llvm::StringRef LTOObjPath; + llvm::StringRef LTOSampleProfile; llvm::StringRef MapFile; llvm::StringRef OutputFile; llvm::StringRef OptRemarksFilename; + llvm::StringRef ProgName; llvm::StringRef SoName; llvm::StringRef Sysroot; llvm::StringRef ThinLTOCacheDir; + llvm::StringRef ThinLTOIndexOnlyArg; + std::pair<llvm::StringRef, llvm::StringRef> ThinLTOObjectSuffixReplace; + std::pair<llvm::StringRef, llvm::StringRef> ThinLTOPrefixReplace; std::string Rpath; std::vector<VersionDefinition> VersionDefinitions; - std::vector<llvm::StringRef> Argv; std::vector<llvm::StringRef> AuxiliaryList; std::vector<llvm::StringRef> FilterList; std::vector<llvm::StringRef> SearchPaths; @@ -106,34 +116,41 @@ struct Configuration { std::vector<SymbolVersion> VersionScriptGlobals; std::vector<SymbolVersion> VersionScriptLocals; std::vector<uint8_t> BuildIdVector; + llvm::MapVector<std::pair<const InputSectionBase *, const InputSectionBase *>, + uint64_t> + CallGraphProfile; bool AllowMultipleDefinition; - bool AndroidPackDynRelocs = false; + bool AndroidPackDynRelocs; bool ARMHasBlx = false; bool ARMHasMovtMovw = false; bool ARMJ1J2BranchEncoding = false; bool AsNeeded = false; bool Bsymbolic; bool BsymbolicFunctions; + bool CheckSections; bool CompressDebugSections; + bool Cref; bool DefineCommon; bool Demangle = true; bool DisableVerify; bool EhFrameHdr; bool EmitRelocs; bool EnableNewDtags; + bool ExecuteOnly; bool ExportDynamic; bool FixCortexA53Errata843419; bool GcSections; bool GdbIndex; bool GnuHash = false; + bool GnuUnique; bool HasDynamicList = false; bool HasDynSymTab; - bool ICF; - bool ICFData; + bool IgnoreDataAddressEquality; + bool IgnoreFunctionAddressEquality; + bool LTODebugPassManager; + bool LTONewPassManager; bool MergeArmExidx; bool MipsN32Abi = false; - bool NoGnuUnique; - bool NoUndefinedVersion; bool NoinhibitExec; bool Nostdlib; bool OFormatBinary; @@ -141,7 +158,9 @@ struct Configuration { bool OptRemarksWithHotness; bool Pie; bool PrintGcSections; + bool PrintIcfSections; bool Relocatable; + bool RelrPackDynRelocs; bool SaveTemps; bool SingleRoRx; bool Shared; @@ -149,13 +168,21 @@ struct Configuration { bool SysvHash = false; bool Target1Rel; bool Trace; - bool Verbose; + bool ThinLTOEmitImportsFiles; + bool ThinLTOIndexOnly; + bool UndefinedVersion; + bool UseAndroidRelrTags = false; + bool WarnBackrefs; bool WarnCommon; bool WarnMissingEntry; + bool WarnSymbolOrdering; + bool WriteAddends; bool ZCombreloc; + bool ZCopyreloc; bool ZExecstack; bool ZHazardplt; - bool ZNocopyreloc; + bool ZInitfirst; + bool ZKeepTextSectionPrefix; bool ZNodelete; bool ZNodlopen; bool ZNow; @@ -164,9 +191,9 @@ struct Configuration { bool ZRodynamic; bool ZText; bool ZRetpolineplt; - bool ExitEarly; bool ZWxneeded; DiscardPolicy Discard; + ICFLevel ICF; OrphanHandlingPolicy OrphanHandling; SortSectionPolicy SortSection; StripPolicy Strip; @@ -179,6 +206,7 @@ struct Configuration { uint16_t EMachine = llvm::ELF::EM_NONE; llvm::Optional<uint64_t> ImageBase; uint64_t MaxPageSize; + uint64_t MipsGotSize; uint64_t ZStackSize; unsigned LTOPartitions; unsigned LTOO; @@ -244,6 +272,12 @@ struct Configuration { // The only instance of Configuration struct. extern Configuration *Config; +static inline void errorOrWarn(const Twine &Msg) { + if (!Config->NoinhibitExec) + error(Msg); + else + warn(Msg); +} } // namespace elf } // namespace lld diff --git a/contrib/llvm/tools/lld/ELF/Driver.cpp b/contrib/llvm/tools/lld/ELF/Driver.cpp index 44cfa56c94ce..693dba64ab5a 100644 --- a/contrib/llvm/tools/lld/ELF/Driver.cpp +++ b/contrib/llvm/tools/lld/ELF/Driver.cpp @@ -30,9 +30,9 @@ #include "InputFiles.h" #include "InputSection.h" #include "LinkerScript.h" +#include "MarkLive.h" #include "OutputSections.h" #include "ScriptParser.h" -#include "Strings.h" #include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" @@ -42,12 +42,16 @@ #include "lld/Common/Driver.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" +#include "lld/Common/Strings.h" +#include "lld/Common/TargetOptionsCommandFlags.h" #include "lld/Common/Threads.h" #include "lld/Common/Version.h" +#include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compression.h" +#include "llvm/Support/LEB128.h" #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/TargetSelect.h" @@ -66,16 +70,18 @@ using namespace lld::elf; Configuration *elf::Config; LinkerDriver *elf::Driver; -static void setConfigs(); +static void setConfigs(opt::InputArgList &Args); bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly, raw_ostream &Error) { - errorHandler().LogName = Args[0]; + errorHandler().LogName = sys::path::filename(Args[0]); errorHandler().ErrorLimitExceededMsg = "too many errors emitted, stopping now (use " "-error-limit=0 to see all errors)"; errorHandler().ErrorOS = &Error; + errorHandler().ExitEarly = CanExitEarly; errorHandler().ColorDiagnostics = Error.has_colors(); + InputSections.clear(); OutputSections.clear(); Tar = nullptr; @@ -88,14 +94,14 @@ bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly, Driver = make<LinkerDriver>(); Script = make<LinkerScript>(); Symtab = make<SymbolTable>(); - Config->Argv = {Args.begin(), Args.end()}; + Config->ProgName = Args[0]; - Driver->main(Args, CanExitEarly); + Driver->main(Args); // Exit immediately if we don't need to return to the caller. // This saves time because the overhead of calling destructors // for all globally-allocated objects is not negligible. - if (Config->ExitEarly) + if (CanExitEarly) exitLld(errorCount() ? 1 : 0); freeArena(); @@ -113,7 +119,8 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) { std::pair<ELFKind, uint16_t> Ret = StringSwitch<std::pair<ELFKind, uint16_t>>(S) - .Cases("aarch64elf", "aarch64linux", {ELF64LEKind, EM_AARCH64}) + .Cases("aarch64elf", "aarch64linux", "aarch64_elf64_le_vec", + {ELF64LEKind, EM_AARCH64}) .Cases("armelf", "armelf_linux_eabi", {ELF32LEKind, EM_ARM}) .Case("elf32_x86_64", {ELF32LEKind, EM_X86_64}) .Cases("elf32btsmip", "elf32btsmipn32", {ELF32BEKind, EM_MIPS}) @@ -122,6 +129,7 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) { .Case("elf64btsmip", {ELF64BEKind, EM_MIPS}) .Case("elf64ltsmip", {ELF64LEKind, EM_MIPS}) .Case("elf64ppc", {ELF64BEKind, EM_PPC64}) + .Case("elf64lppc", {ELF64LEKind, EM_PPC64}) .Cases("elf_amd64", "elf_x86_64", {ELF64LEKind, EM_X86_64}) .Case("elf_i386", {ELF32LEKind, EM_386}) .Case("elf_iamcu", {ELF32LEKind, EM_IAMCU}) @@ -228,11 +236,15 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) { Files.push_back( createSharedFile(MBRef, WithLOption ? path::filename(Path) : Path)); return; - default: + case file_magic::bitcode: + case file_magic::elf_relocatable: if (InLib) Files.push_back(make<LazyObjFile>(MBRef, "", 0)); else Files.push_back(createObjectFile(MBRef)); + break; + default: + error(Path + ": unknown file type"); } } @@ -248,18 +260,11 @@ void LinkerDriver::addLibrary(StringRef Name) { // LTO calls LLVM functions to compile bitcode files to native code. // Technically this can be delayed until we read bitcode files, but // we don't bother to do lazily because the initialization is fast. -static void initLLVM(opt::InputArgList &Args) { +static void initLLVM() { InitializeAllTargets(); InitializeAllTargetMCs(); InitializeAllAsmPrinters(); InitializeAllAsmParsers(); - - // Parse and evaluate -mllvm options. - std::vector<const char *> V; - V.push_back("lld (LLVM option parsing)"); - for (auto *Arg : Args.filtered(OPT_mllvm)) - V.push_back(Arg->getValue()); - cl::ParseCommandLineOptions(V.size(), V.data()); } // Some command line options or some combinations of them are not allowed. @@ -290,11 +295,21 @@ static void checkOptions(opt::InputArgList &Args) { error("-r and -shared may not be used together"); if (Config->GcSections) error("-r and --gc-sections may not be used together"); - if (Config->ICF) + if (Config->GdbIndex) + error("-r and --gdb-index may not be used together"); + if (Config->ICF != ICFLevel::None) error("-r and --icf may not be used together"); if (Config->Pie) error("-r and -pie may not be used together"); } + + if (Config->ExecuteOnly) { + if (Config->EMachine != EM_AARCH64) + error("-execute-only is only supported on AArch64 targets"); + + if (Config->SingleRoRx && !Script->HasSectionsCommand) + error("-execute-only and -no-rosegment cannot be used together"); + } } static const char *getReproduceOption(opt::InputArgList &Args) { @@ -310,7 +325,37 @@ static bool hasZOption(opt::InputArgList &Args, StringRef Key) { return false; } -void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) { +static bool getZFlag(opt::InputArgList &Args, StringRef K1, StringRef K2, + bool Default) { + for (auto *Arg : Args.filtered_reverse(OPT_z)) { + if (K1 == Arg->getValue()) + return true; + if (K2 == Arg->getValue()) + return false; + } + return Default; +} + +static bool isKnown(StringRef S) { + return S == "combreloc" || S == "copyreloc" || S == "defs" || + S == "execstack" || S == "hazardplt" || S == "initfirst" || + S == "keep-text-section-prefix" || S == "lazy" || S == "muldefs" || + S == "nocombreloc" || S == "nocopyreloc" || S == "nodelete" || + S == "nodlopen" || S == "noexecstack" || + S == "nokeep-text-section-prefix" || S == "norelro" || S == "notext" || + S == "now" || S == "origin" || S == "relro" || S == "retpolineplt" || + S == "rodynamic" || S == "text" || S == "wxneeded" || + S.startswith("max-page-size=") || S.startswith("stack-size="); +} + +// Report an error for an unknown -z option. +static void checkZOptions(opt::InputArgList &Args) { + for (auto *Arg : Args.filtered(OPT_z)) + if (!isKnown(Arg->getValue())) + error("unknown -z value: " + StringRef(Arg->getValue())); +} + +void LinkerDriver::main(ArrayRef<const char *> ArgsArr) { ELFOptTable Parser; opt::InputArgList Args = Parser.parse(ArgsArr.slice(1)); @@ -319,7 +364,7 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) { // Handle -help if (Args.hasArg(OPT_help)) { - printHelp(ArgsArr[0]); + printHelp(); return; } @@ -348,9 +393,6 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) { if (Args.hasArg(OPT_version)) return; - Config->ExitEarly = CanExitEarly && !Args.hasArg(OPT_full_shutdown); - errorHandler().ExitEarly = Config->ExitEarly; - if (const char *Path = getReproduceOption(Args)) { // Note that --reproduce is a debug option so you can ignore it // if you are trying to understand the whole picture of the code. @@ -368,10 +410,14 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) { } readConfigs(Args); - initLLVM(Args); + checkZOptions(Args); + initLLVM(); createFiles(Args); + if (errorCount()) + return; + inferMachineType(); - setConfigs(); + setConfigs(Args); checkOptions(Args); if (errorCount()) return; @@ -455,6 +501,8 @@ static bool isOutputFormatBinary(opt::InputArgList &Args) { StringRef S = Arg->getValue(); if (S == "binary") return true; + if (S.startswith("elf")) + return false; error("unknown --oformat value: " + S); } return false; @@ -482,6 +530,15 @@ static StringRef getDynamicLinker(opt::InputArgList &Args) { return Arg->getValue(); } +static ICFLevel getICF(opt::InputArgList &Args) { + auto *Arg = Args.getLastArg(OPT_icf_none, OPT_icf_safe, OPT_icf_all); + if (!Arg || Arg->getOption().getID() == OPT_icf_none) + return ICFLevel::None; + if (Arg->getOption().getID() == OPT_icf_safe) + return ICFLevel::Safe; + return ICFLevel::All; +} + static StripPolicy getStrip(opt::InputArgList &Args) { if (Args.hasArg(OPT_relocatable)) return StripPolicy::None; @@ -556,6 +613,8 @@ getBuildId(opt::InputArgList &Args) { return {BuildIdKind::Fast, {}}; StringRef S = Arg->getValue(); + if (S == "fast") + return {BuildIdKind::Fast, {}}; if (S == "md5") return {BuildIdKind::Md5, {}}; if (S == "sha1" || S == "tree") @@ -570,6 +629,57 @@ getBuildId(opt::InputArgList &Args) { return {BuildIdKind::None, {}}; } +static std::pair<bool, bool> getPackDynRelocs(opt::InputArgList &Args) { + StringRef S = Args.getLastArgValue(OPT_pack_dyn_relocs, "none"); + if (S == "android") + return {true, false}; + if (S == "relr") + return {false, true}; + if (S == "android+relr") + return {true, true}; + + if (S != "none") + error("unknown -pack-dyn-relocs format: " + S); + return {false, false}; +} + +static void readCallGraph(MemoryBufferRef MB) { + // Build a map from symbol name to section + DenseMap<StringRef, const Symbol *> SymbolNameToSymbol; + for (InputFile *File : ObjectFiles) + for (Symbol *Sym : File->getSymbols()) + SymbolNameToSymbol[Sym->getName()] = Sym; + + for (StringRef L : args::getLines(MB)) { + SmallVector<StringRef, 3> Fields; + L.split(Fields, ' '); + uint64_t Count; + if (Fields.size() != 3 || !to_integer(Fields[2], Count)) + fatal(MB.getBufferIdentifier() + ": parse error"); + const Symbol *FromSym = SymbolNameToSymbol.lookup(Fields[0]); + const Symbol *ToSym = SymbolNameToSymbol.lookup(Fields[1]); + if (Config->WarnSymbolOrdering) { + if (!FromSym) + warn(MB.getBufferIdentifier() + ": no such symbol: " + Fields[0]); + if (!ToSym) + warn(MB.getBufferIdentifier() + ": no such symbol: " + Fields[1]); + } + if (!FromSym || !ToSym || Count == 0) + continue; + warnUnorderableSymbol(FromSym); + warnUnorderableSymbol(ToSym); + const Defined *FromSymD = dyn_cast<Defined>(FromSym); + const Defined *ToSymD = dyn_cast<Defined>(ToSym); + if (!FromSymD || !ToSymD) + continue; + const auto *FromSB = dyn_cast_or_null<InputSectionBase>(FromSymD->Section); + const auto *ToSB = dyn_cast_or_null<InputSectionBase>(ToSymD->Section); + if (!FromSB || !ToSB) + continue; + Config->CallGraphProfile[std::make_pair(FromSB, ToSB)] += Count; + } +} + static bool getCompressDebugSections(opt::InputArgList &Args) { StringRef S = Args.getLastArgValue(OPT_compress_debug_sections, "none"); if (S == "none") @@ -581,54 +691,100 @@ static bool getCompressDebugSections(opt::InputArgList &Args) { return true; } -static int parseInt(StringRef S, opt::Arg *Arg) { - int V = 0; - if (!to_integer(S, V, 10)) - error(Arg->getSpelling() + ": number expected, but got '" + S + "'"); - return V; +static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &Args, + unsigned Id) { + auto *Arg = Args.getLastArg(Id); + if (!Arg) + return {"", ""}; + + StringRef S = Arg->getValue(); + std::pair<StringRef, StringRef> Ret = S.split(';'); + if (Ret.second.empty()) + error(Arg->getSpelling() + " expects 'old;new' format, but got " + S); + return Ret; +} + +// Parse the symbol ordering file and warn for any duplicate entries. +static std::vector<StringRef> getSymbolOrderingFile(MemoryBufferRef MB) { + SetVector<StringRef> Names; + for (StringRef S : args::getLines(MB)) + if (!Names.insert(S) && Config->WarnSymbolOrdering) + warn(MB.getBufferIdentifier() + ": duplicate ordered symbol: " + S); + + return Names.takeVector(); +} + +static void parseClangOption(StringRef Opt, const Twine &Msg) { + std::string Err; + raw_string_ostream OS(Err); + + const char *Argv[] = {Config->ProgName.data(), Opt.data()}; + if (cl::ParseCommandLineOptions(2, Argv, "", &OS)) + return; + OS.flush(); + error(Msg + ": " + StringRef(Err).trim()); } // Initializes Config members by the command line options. void LinkerDriver::readConfigs(opt::InputArgList &Args) { + errorHandler().Verbose = Args.hasArg(OPT_verbose); + errorHandler().FatalWarnings = + Args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false); + ThreadsEnabled = Args.hasFlag(OPT_threads, OPT_no_threads, true); + Config->AllowMultipleDefinition = - Args.hasArg(OPT_allow_multiple_definition) || hasZOption(Args, "muldefs"); + Args.hasFlag(OPT_allow_multiple_definition, + OPT_no_allow_multiple_definition, false) || + hasZOption(Args, "muldefs"); Config->AuxiliaryList = args::getStrings(Args, OPT_auxiliary); Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic); Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions); + Config->CheckSections = + Args.hasFlag(OPT_check_sections, OPT_no_check_sections, true); Config->Chroot = Args.getLastArgValue(OPT_chroot); Config->CompressDebugSections = getCompressDebugSections(Args); + Config->Cref = Args.hasFlag(OPT_cref, OPT_no_cref, false); Config->DefineCommon = Args.hasFlag(OPT_define_common, OPT_no_define_common, !Args.hasArg(OPT_relocatable)); Config->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true); Config->DisableVerify = Args.hasArg(OPT_disable_verify); Config->Discard = getDiscard(Args); + Config->DwoDir = Args.getLastArgValue(OPT_plugin_opt_dwo_dir_eq); Config->DynamicLinker = getDynamicLinker(Args); Config->EhFrameHdr = Args.hasFlag(OPT_eh_frame_hdr, OPT_no_eh_frame_hdr, false); Config->EmitRelocs = Args.hasArg(OPT_emit_relocs); - Config->EnableNewDtags = !Args.hasArg(OPT_disable_new_dtags); + Config->EnableNewDtags = + Args.hasFlag(OPT_enable_new_dtags, OPT_disable_new_dtags, true); Config->Entry = Args.getLastArgValue(OPT_entry); + Config->ExecuteOnly = + Args.hasFlag(OPT_execute_only, OPT_no_execute_only, false); Config->ExportDynamic = Args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false); - errorHandler().FatalWarnings = - Args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false); Config->FilterList = args::getStrings(Args, OPT_filter); Config->Fini = Args.getLastArgValue(OPT_fini, "_fini"); Config->FixCortexA53Errata843419 = Args.hasArg(OPT_fix_cortex_a53_843419); Config->GcSections = Args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false); + Config->GnuUnique = Args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true); Config->GdbIndex = Args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false); - Config->ICF = Args.hasFlag(OPT_icf_all, OPT_icf_none, false); - Config->ICFData = Args.hasArg(OPT_icf_data); + Config->ICF = getICF(Args); + Config->IgnoreDataAddressEquality = + Args.hasArg(OPT_ignore_data_address_equality); + Config->IgnoreFunctionAddressEquality = + Args.hasArg(OPT_ignore_function_address_equality); Config->Init = Args.getLastArgValue(OPT_init, "_init"); Config->LTOAAPipeline = Args.getLastArgValue(OPT_lto_aa_pipeline); + Config->LTODebugPassManager = Args.hasArg(OPT_lto_debug_pass_manager); + Config->LTONewPassManager = Args.hasArg(OPT_lto_new_pass_manager); Config->LTONewPmPasses = Args.getLastArgValue(OPT_lto_newpm_passes); Config->LTOO = args::getInteger(Args, OPT_lto_O, 2); + Config->LTOObjPath = Args.getLastArgValue(OPT_plugin_opt_obj_path_eq); Config->LTOPartitions = args::getInteger(Args, OPT_lto_partitions, 1); + Config->LTOSampleProfile = Args.getLastArgValue(OPT_lto_sample_profile); Config->MapFile = Args.getLastArgValue(OPT_Map); - Config->NoGnuUnique = Args.hasArg(OPT_no_gnu_unique); + Config->MipsGotSize = args::getInteger(Args, OPT_mips_got_size, 0xfff0); Config->MergeArmExidx = Args.hasFlag(OPT_merge_exidx_entries, OPT_no_merge_exidx_entries, true); - Config->NoUndefinedVersion = Args.hasArg(OPT_no_undefined_version); Config->NoinhibitExec = Args.hasArg(OPT_noinhibit_exec); Config->Nostdlib = Args.hasArg(OPT_nostdlib); Config->OFormatBinary = isOutputFormatBinary(Args); @@ -638,7 +794,9 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { Config->Optimize = args::getInteger(Args, OPT_O, 1); Config->OrphanHandling = getOrphanHandling(Args); Config->OutputFile = Args.getLastArgValue(OPT_o); - Config->Pie = Args.hasFlag(OPT_pie, OPT_nopie, false); + Config->Pie = Args.hasFlag(OPT_pie, OPT_no_pie, false); + Config->PrintIcfSections = + Args.hasFlag(OPT_print_icf_sections, OPT_no_print_icf_sections, false); Config->PrintGcSections = Args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false); Config->Rpath = getRpath(Args); @@ -658,48 +816,58 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { Config->ThinLTOCachePolicy = CHECK( parseCachePruningPolicy(Args.getLastArgValue(OPT_thinlto_cache_policy)), "--thinlto-cache-policy: invalid cache policy"); + Config->ThinLTOEmitImportsFiles = + Args.hasArg(OPT_plugin_opt_thinlto_emit_imports_files); + Config->ThinLTOIndexOnly = Args.hasArg(OPT_plugin_opt_thinlto_index_only) || + Args.hasArg(OPT_plugin_opt_thinlto_index_only_eq); + Config->ThinLTOIndexOnlyArg = + Args.getLastArgValue(OPT_plugin_opt_thinlto_index_only_eq); Config->ThinLTOJobs = args::getInteger(Args, OPT_thinlto_jobs, -1u); - ThreadsEnabled = Args.hasFlag(OPT_threads, OPT_no_threads, true); + Config->ThinLTOObjectSuffixReplace = + getOldNewOptions(Args, OPT_plugin_opt_thinlto_object_suffix_replace_eq); + Config->ThinLTOPrefixReplace = + getOldNewOptions(Args, OPT_plugin_opt_thinlto_prefix_replace_eq); Config->Trace = Args.hasArg(OPT_trace); Config->Undefined = args::getStrings(Args, OPT_undefined); + Config->UndefinedVersion = + Args.hasFlag(OPT_undefined_version, OPT_no_undefined_version, true); + Config->UseAndroidRelrTags = Args.hasFlag( + OPT_use_android_relr_tags, OPT_no_use_android_relr_tags, false); Config->UnresolvedSymbols = getUnresolvedSymbolPolicy(Args); - Config->Verbose = Args.hasArg(OPT_verbose); - errorHandler().Verbose = Config->Verbose; - Config->WarnCommon = Args.hasArg(OPT_warn_common); - Config->ZCombreloc = !hasZOption(Args, "nocombreloc"); - Config->ZExecstack = hasZOption(Args, "execstack"); + Config->WarnBackrefs = + Args.hasFlag(OPT_warn_backrefs, OPT_no_warn_backrefs, false); + Config->WarnCommon = Args.hasFlag(OPT_warn_common, OPT_no_warn_common, false); + Config->WarnSymbolOrdering = + Args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true); + Config->ZCombreloc = getZFlag(Args, "combreloc", "nocombreloc", true); + Config->ZCopyreloc = getZFlag(Args, "copyreloc", "nocopyreloc", true); + Config->ZExecstack = getZFlag(Args, "execstack", "noexecstack", false); Config->ZHazardplt = hasZOption(Args, "hazardplt"); - Config->ZNocopyreloc = hasZOption(Args, "nocopyreloc"); + Config->ZInitfirst = hasZOption(Args, "initfirst"); + Config->ZKeepTextSectionPrefix = getZFlag( + Args, "keep-text-section-prefix", "nokeep-text-section-prefix", false); Config->ZNodelete = hasZOption(Args, "nodelete"); Config->ZNodlopen = hasZOption(Args, "nodlopen"); - Config->ZNow = hasZOption(Args, "now"); + Config->ZNow = getZFlag(Args, "now", "lazy", false); Config->ZOrigin = hasZOption(Args, "origin"); - Config->ZRelro = !hasZOption(Args, "norelro"); + Config->ZRelro = getZFlag(Args, "relro", "norelro", true); Config->ZRetpolineplt = hasZOption(Args, "retpolineplt"); Config->ZRodynamic = hasZOption(Args, "rodynamic"); Config->ZStackSize = args::getZOptionValue(Args, OPT_z, "stack-size", 0); - Config->ZText = !hasZOption(Args, "notext"); + Config->ZText = getZFlag(Args, "text", "notext", true); Config->ZWxneeded = hasZOption(Args, "wxneeded"); - // Parse LTO plugin-related options for compatibility with gold. - for (auto *Arg : Args.filtered(OPT_plugin_opt, OPT_plugin_opt_eq)) { - StringRef S = Arg->getValue(); - if (S == "disable-verify") - Config->DisableVerify = true; - else if (S == "save-temps") - Config->SaveTemps = true; - else if (S.startswith("O")) - Config->LTOO = parseInt(S.substr(1), Arg); - else if (S.startswith("lto-partitions=")) - Config->LTOPartitions = parseInt(S.substr(15), Arg); - else if (S.startswith("jobs=")) - Config->ThinLTOJobs = parseInt(S.substr(5), Arg); - else if (!S.startswith("/") && !S.startswith("-fresolution=") && - !S.startswith("-pass-through=") && !S.startswith("mcpu=") && - !S.startswith("thinlto") && S != "-function-sections" && - S != "-data-sections") - error(Arg->getSpelling() + ": unknown option: " + S); - } + // Parse LTO options. + if (auto *Arg = Args.getLastArg(OPT_plugin_opt_mcpu_eq)) + parseClangOption(Saver.save("-mcpu=" + StringRef(Arg->getValue())), + Arg->getSpelling()); + + for (auto *Arg : Args.filtered(OPT_plugin_opt)) + parseClangOption(Arg->getValue(), Arg->getSpelling()); + + // Parse -mllvm options. + for (auto *Arg : Args.filtered(OPT_mllvm)) + parseClangOption(Arg->getValue(), Arg->getSpelling()); if (Config->LTOO > 3) error("invalid optimization level for LTO: " + Twine(Config->LTOO)); @@ -742,17 +910,12 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { std::tie(Config->BuildId, Config->BuildIdVector) = getBuildId(Args); - if (auto *Arg = Args.getLastArg(OPT_pack_dyn_relocs_eq)) { - StringRef S = Arg->getValue(); - if (S == "android") - Config->AndroidPackDynRelocs = true; - else if (S != "none") - error("unknown -pack-dyn-relocs format: " + S); - } + std::tie(Config->AndroidPackDynRelocs, Config->RelrPackDynRelocs) = + getPackDynRelocs(Args); if (auto *Arg = Args.getLastArg(OPT_symbol_ordering_file)) if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue())) - Config->SymbolOrderingFile = args::getLines(*Buffer); + Config->SymbolOrderingFile = getSymbolOrderingFile(*Buffer); // If --retain-symbol-file is used, we'll keep only the symbols listed in // the file and discard all others. @@ -780,32 +943,67 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { {Arg->getValue(), /*IsExternCpp*/ false, /*HasWildcard*/ false}); } + // If --export-dynamic-symbol=foo is given and symbol foo is defined in + // an object file in an archive file, that object file should be pulled + // out and linked. (It doesn't have to behave like that from technical + // point of view, but this is needed for compatibility with GNU.) + for (auto *Arg : Args.filtered(OPT_export_dynamic_symbol)) + Config->Undefined.push_back(Arg->getValue()); + for (auto *Arg : Args.filtered(OPT_version_script)) - if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue())) - readVersionScript(*Buffer); + if (Optional<std::string> Path = searchScript(Arg->getValue())) { + if (Optional<MemoryBufferRef> Buffer = readFile(*Path)) + readVersionScript(*Buffer); + } else { + error(Twine("cannot find version script ") + Arg->getValue()); + } } // Some Config members do not directly correspond to any particular // command line options, but computed based on other Config values. // This function initialize such members. See Config.h for the details // of these values. -static void setConfigs() { +static void setConfigs(opt::InputArgList &Args) { ELFKind Kind = Config->EKind; uint16_t Machine = Config->EMachine; - // There is an ILP32 ABI for x86-64, although it's not very popular. - // It is called the x32 ABI. - bool IsX32 = (Kind == ELF32LEKind && Machine == EM_X86_64); - Config->CopyRelocs = (Config->Relocatable || Config->EmitRelocs); Config->Is64 = (Kind == ELF64LEKind || Kind == ELF64BEKind); Config->IsLE = (Kind == ELF32LEKind || Kind == ELF64LEKind); Config->Endianness = Config->IsLE ? support::endianness::little : support::endianness::big; Config->IsMips64EL = (Kind == ELF64LEKind && Machine == EM_MIPS); - Config->IsRela = Config->Is64 || IsX32 || Config->MipsN32Abi; Config->Pic = Config->Pie || Config->Shared; Config->Wordsize = Config->Is64 ? 8 : 4; + + // There is an ILP32 ABI for x86-64, although it's not very popular. + // It is called the x32 ABI. + bool IsX32 = (Kind == ELF32LEKind && Machine == EM_X86_64); + + // ELF defines two different ways to store relocation addends as shown below: + // + // Rel: Addends are stored to the location where relocations are applied. + // Rela: Addends are stored as part of relocation entry. + // + // In other words, Rela makes it easy to read addends at the price of extra + // 4 or 8 byte for each relocation entry. We don't know why ELF defined two + // different mechanisms in the first place, but this is how the spec is + // defined. + // + // You cannot choose which one, Rel or Rela, you want to use. Instead each + // ABI defines which one you need to use. The following expression expresses + // that. + Config->IsRela = + (Config->Is64 || IsX32 || Machine == EM_PPC) && Machine != EM_MIPS; + + // If the output uses REL relocations we must store the dynamic relocation + // addends to the output sections. We also store addends for RELA relocations + // if --apply-dynamic-relocs is used. + // We default to not writing the addends when using RELA relocations since + // any standard conforming tool can find it in r_addend. + Config->WriteAddends = Args.hasFlag(OPT_apply_dynamic_relocs, + OPT_no_apply_dynamic_relocs, false) || + !Config->IsRela; } // Returns a value of "-format" option. @@ -820,6 +1018,10 @@ static bool getBinaryOption(StringRef S) { } void LinkerDriver::createFiles(opt::InputArgList &Args) { + // For --{push,pop}-state. + std::vector<std::tuple<bool, bool, bool>> Stack; + + // Iterate over argv to process input files and positional arguments. for (auto *Arg : Args) { switch (Arg->getOption().getUnaliasedOption().getID()) { case OPT_library: @@ -828,8 +1030,15 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) { case OPT_INPUT: addFile(Arg->getValue(), /*WithLOption=*/false); break; + case OPT_defsym: { + StringRef From; + StringRef To; + std::tie(From, To) = StringRef(Arg->getValue()).split('='); + readDefsym(From, MemoryBufferRef(To, "-defsym")); + break; + } case OPT_script: - if (Optional<std::string> Path = searchLinkerScript(Arg->getValue())) { + if (Optional<std::string> Path = searchScript(Arg->getValue())) { if (Optional<MemoryBufferRef> MB = readFile(*Path)) readLinkerScript(*MB); break; @@ -857,11 +1066,48 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) { case OPT_no_whole_archive: InWholeArchive = false; break; + case OPT_just_symbols: + if (Optional<MemoryBufferRef> MB = readFile(Arg->getValue())) { + Files.push_back(createObjectFile(*MB)); + Files.back()->JustSymbols = true; + } + break; + case OPT_start_group: + if (InputFile::IsInGroup) + error("nested --start-group"); + InputFile::IsInGroup = true; + break; + case OPT_end_group: + if (!InputFile::IsInGroup) + error("stray --end-group"); + InputFile::IsInGroup = false; + ++InputFile::NextGroupId; + break; case OPT_start_lib: + if (InLib) + error("nested --start-lib"); + if (InputFile::IsInGroup) + error("may not nest --start-lib in --start-group"); InLib = true; + InputFile::IsInGroup = true; break; case OPT_end_lib: + if (!InLib) + error("stray --end-lib"); InLib = false; + InputFile::IsInGroup = false; + ++InputFile::NextGroupId; + break; + case OPT_push_state: + Stack.emplace_back(Config->AsNeeded, Config->Static, InWholeArchive); + break; + case OPT_pop_state: + if (Stack.empty()) { + error("unbalanced --push-state/--pop-state"); + break; + } + std::tie(Config->AsNeeded, Config->Static, InWholeArchive) = Stack.back(); + Stack.pop_back(); break; } } @@ -934,14 +1180,6 @@ static DenseSet<StringRef> getExcludeLibs(opt::InputArgList &Args) { return Ret; } -static Optional<StringRef> getArchiveName(InputFile *File) { - if (isa<ArchiveFile>(File)) - return File->getName(); - if (!File->ArchiveName.empty()) - return File->ArchiveName; - return None; -} - // Handles the -exclude-libs option. If a static library file is specified // by the -exclude-libs option, all public symbols from the archive become // private unless otherwise specified by version scripts or something. @@ -949,18 +1187,140 @@ static Optional<StringRef> getArchiveName(InputFile *File) { // // This is not a popular option, but some programs such as bionic libc use it. template <class ELFT> -static void excludeLibs(opt::InputArgList &Args, ArrayRef<InputFile *> Files) { +static void excludeLibs(opt::InputArgList &Args) { DenseSet<StringRef> Libs = getExcludeLibs(Args); bool All = Libs.count("ALL"); - for (InputFile *File : Files) - if (Optional<StringRef> Archive = getArchiveName(File)) - if (All || Libs.count(path::filename(*Archive))) + auto Visit = [&](InputFile *File) { + if (!File->ArchiveName.empty()) + if (All || Libs.count(path::filename(File->ArchiveName))) for (Symbol *Sym : File->getSymbols()) - if (!Sym->isLocal()) + if (!Sym->isLocal() && Sym->File == File) Sym->VersionId = VER_NDX_LOCAL; + }; + + for (InputFile *File : ObjectFiles) + Visit(File); + + for (BitcodeFile *File : BitcodeFiles) + Visit(File); +} + +// Force Sym to be entered in the output. Used for -u or equivalent. +template <class ELFT> static void handleUndefined(StringRef Name) { + Symbol *Sym = Symtab->find(Name); + if (!Sym) + return; + + // Since symbol S may not be used inside the program, LTO may + // eliminate it. Mark the symbol as "used" to prevent it. + Sym->IsUsedInRegularObj = true; + + if (Sym->isLazy()) + Symtab->fetchLazy<ELFT>(Sym); +} + +template <class ELFT> static bool shouldDemote(Symbol &Sym) { + // If all references to a DSO happen to be weak, the DSO is not added to + // DT_NEEDED. If that happens, we need to eliminate shared symbols created + // from the DSO. Otherwise, they become dangling references that point to a + // non-existent DSO. + if (auto *S = dyn_cast<SharedSymbol>(&Sym)) + return !S->getFile<ELFT>().IsNeeded; + + // We are done processing archives, so lazy symbols that were used but not + // found can be converted to undefined. We could also just delete the other + // lazy symbols, but that seems to be more work than it is worth. + return Sym.isLazy() && Sym.IsUsedInRegularObj; } +// Some files, such as .so or files between -{start,end}-lib may be removed +// after their symbols are added to the symbol table. If that happens, we +// need to remove symbols that refer files that no longer exist, so that +// they won't appear in the symbol table of the output file. +// +// We remove symbols by demoting them to undefined symbol. +template <class ELFT> static void demoteSymbols() { + for (Symbol *Sym : Symtab->getSymbols()) { + if (shouldDemote<ELFT>(*Sym)) { + bool Used = Sym->Used; + replaceSymbol<Undefined>(Sym, nullptr, Sym->getName(), Sym->Binding, + Sym->StOther, Sym->Type); + Sym->Used = Used; + } + } +} + +// The section referred to by S is considered address-significant. Set the +// KeepUnique flag on the section if appropriate. +static void markAddrsig(Symbol *S) { + if (auto *D = dyn_cast_or_null<Defined>(S)) + if (D->Section) + // We don't need to keep text sections unique under --icf=all even if they + // are address-significant. + if (Config->ICF == ICFLevel::Safe || !(D->Section->Flags & SHF_EXECINSTR)) + D->Section->KeepUnique = true; +} + +// Record sections that define symbols mentioned in --keep-unique <symbol> +// and symbols referred to by address-significance tables. These sections are +// ineligible for ICF. +template <class ELFT> +static void findKeepUniqueSections(opt::InputArgList &Args) { + for (auto *Arg : Args.filtered(OPT_keep_unique)) { + StringRef Name = Arg->getValue(); + auto *D = dyn_cast_or_null<Defined>(Symtab->find(Name)); + if (!D || !D->Section) { + warn("could not find symbol " + Name + " to keep unique"); + continue; + } + D->Section->KeepUnique = true; + } + + // --icf=all --ignore-data-address-equality means that we can ignore + // the dynsym and address-significance tables entirely. + if (Config->ICF == ICFLevel::All && Config->IgnoreDataAddressEquality) + return; + + // Symbols in the dynsym could be address-significant in other executables + // or DSOs, so we conservatively mark them as address-significant. + for (Symbol *S : Symtab->getSymbols()) + if (S->includeInDynsym()) + markAddrsig(S); + + // Visit the address-significance table in each object file and mark each + // referenced symbol as address-significant. + for (InputFile *F : ObjectFiles) { + auto *Obj = cast<ObjFile<ELFT>>(F); + ArrayRef<Symbol *> Syms = Obj->getSymbols(); + if (Obj->AddrsigSec) { + ArrayRef<uint8_t> Contents = + check(Obj->getObj().getSectionContents(Obj->AddrsigSec)); + const uint8_t *Cur = Contents.begin(); + while (Cur != Contents.end()) { + unsigned Size; + const char *Err; + uint64_t SymIndex = decodeULEB128(Cur, &Size, Contents.end(), &Err); + if (Err) + fatal(toString(F) + ": could not decode addrsig section: " + Err); + markAddrsig(Syms[SymIndex]); + Cur += Size; + } + } else { + // If an object file does not have an address-significance table, + // conservatively mark all of its symbols as address-significant. + for (Symbol *S : Syms) + markAddrsig(S); + } + } +} + +static const char *LibcallRoutineNames[] = { +#define HANDLE_LIBCALL(code, name) name, +#include "llvm/IR/RuntimeLibcalls.def" +#undef HANDLE_LIBCALL +}; + // Do actual linking. Note that when this function is called, // all linker scripts have already been parsed. template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) { @@ -1009,14 +1369,6 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) { for (InputFile *F : Files) Symtab->addFile<ELFT>(F); - // Process -defsym option. - for (auto *Arg : Args.filtered(OPT_defsym)) { - StringRef From; - StringRef To; - std::tie(From, To) = StringRef(Arg->getValue()).split('='); - readDefsym(From, MemoryBufferRef(To, "-defsym")); - } - // Now that we have every file, we can decide if we will need a // dynamic symbol table. // We need one if we were asked to export dynamic symbols or if we are @@ -1033,24 +1385,39 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) { // Handle the `--undefined <sym>` options. for (StringRef S : Config->Undefined) - Symtab->fetchIfLazy<ELFT>(S); + handleUndefined<ELFT>(S); - // If an entry symbol is in a static archive, pull out that file now - // to complete the symbol table. After this, no new names except a - // few linker-synthesized ones will be added to the symbol table. - Symtab->fetchIfLazy<ELFT>(Config->Entry); + // If an entry symbol is in a static archive, pull out that file now. + handleUndefined<ELFT>(Config->Entry); + + // If any of our inputs are bitcode files, the LTO code generator may create + // references to certain library functions that might not be explicit in the + // bitcode file's symbol table. If any of those library functions are defined + // in a bitcode file in an archive member, we need to arrange to use LTO to + // compile those archive members by adding them to the link beforehand. + // + // With this the symbol table should be complete. After this, no new names + // except a few linker-synthesized ones will be added to the symbol table. + if (!BitcodeFiles.empty()) + for (const char *S : LibcallRoutineNames) + handleUndefined<ELFT>(S); // Return if there were name resolution errors. if (errorCount()) return; - // Handle undefined symbols in DSOs. - if (!Config->Shared) - Symtab->scanShlibUndefined<ELFT>(); + // Now when we read all script files, we want to finalize order of linker + // script commands, which can be not yet final because of INSERT commands. + Script->processInsertCommands(); + + // We want to declare linker script's symbols early, + // so that we can version them. + // They also might be exported if referenced by DSOs. + Script->declareSymbols(); // Handle the -exclude-libs option. if (Args.hasArg(OPT_exclude_libs)) - excludeLibs<ELFT>(Args, Files); + excludeLibs<ELFT>(Args); // Create ElfHeader early. We need a dummy section in // addReservedSymbols to mark the created symbols as not absolute. @@ -1073,10 +1440,18 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) { for (auto *Arg : Args.filtered(OPT_wrap)) Symtab->addSymbolWrap<ELFT>(Arg->getValue()); + // Do link-time optimization if given files are LLVM bitcode files. + // This compiles bitcode files into real object files. Symtab->addCombinedLTOObject<ELFT>(); if (errorCount()) return; + // If -thinlto-index-only is given, we should create only "index + // files" and not object files. Index file creation is already done + // in addCombinedLTOObject, so we are done if that's the case. + if (Config->ThinLTOIndexOnly) + return; + // Apply symbol renames for -wrap. Symtab->applySymbolWrap(); @@ -1123,11 +1498,20 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) { // Do size optimizations: garbage collection, merging of SHF_MERGE sections // and identical code folding. - markLive<ELFT>(); decompressSections(); + splitSections<ELFT>(); + markLive<ELFT>(); + demoteSymbols<ELFT>(); mergeSections(); - if (Config->ICF) + if (Config->ICF != ICFLevel::None) { + findKeepUniqueSections<ELFT>(Args); doIcf<ELFT>(); + } + + // Read the callgraph now that we know what was gced or icfed + if (auto *Arg = Args.getLastArg(OPT_call_graph_ordering_file)) + if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue())) + readCallGraph(*Buffer); // Write the result to the file. writeResult<ELFT>(); diff --git a/contrib/llvm/tools/lld/ELF/Driver.h b/contrib/llvm/tools/lld/ELF/Driver.h index 7d2ad7b9cfde..3804fa20f2d3 100644 --- a/contrib/llvm/tools/lld/ELF/Driver.h +++ b/contrib/llvm/tools/lld/ELF/Driver.h @@ -26,7 +26,7 @@ extern class LinkerDriver *Driver; class LinkerDriver { public: - void main(ArrayRef<const char *> Args, bool CanExitEarly); + void main(ArrayRef<const char *> Args); void addFile(StringRef Path, bool WithLOption); void addLibrary(StringRef Name); @@ -63,11 +63,11 @@ enum { #undef OPTION }; -void printHelp(const char *Argv0); +void printHelp(); std::string createResponseFile(const llvm::opt::InputArgList &Args); llvm::Optional<std::string> findFromSearchPaths(StringRef Path); -llvm::Optional<std::string> searchLinkerScript(StringRef Path); +llvm::Optional<std::string> searchScript(StringRef Path); llvm::Optional<std::string> searchLibrary(StringRef Path); } // namespace elf diff --git a/contrib/llvm/tools/lld/ELF/DriverUtils.cpp b/contrib/llvm/tools/lld/ELF/DriverUtils.cpp index 774de9f1d03c..c8ca622b0628 100644 --- a/contrib/llvm/tools/lld/ELF/DriverUtils.cpp +++ b/contrib/llvm/tools/lld/ELF/DriverUtils.cpp @@ -29,6 +29,7 @@ using namespace llvm; using namespace llvm::sys; +using namespace llvm::opt; using namespace lld; using namespace lld::elf; @@ -58,18 +59,18 @@ static void handleColorDiagnostics(opt::InputArgList &Args) { OPT_no_color_diagnostics); if (!Arg) return; - else if (Arg->getOption().getID() == OPT_color_diagnostics) + if (Arg->getOption().getID() == OPT_color_diagnostics) { errorHandler().ColorDiagnostics = true; - else if (Arg->getOption().getID() == OPT_no_color_diagnostics) + } else if (Arg->getOption().getID() == OPT_no_color_diagnostics) { errorHandler().ColorDiagnostics = false; - else { + } else { StringRef S = Arg->getValue(); if (S == "always") errorHandler().ColorDiagnostics = true; else if (S == "never") errorHandler().ColorDiagnostics = false; else if (S != "auto") - error("unknown option: -color-diagnostics=" + S); + error("unknown option: --color-diagnostics=" + S); } } @@ -87,6 +88,29 @@ static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) { return cl::TokenizeGNUCommandLine; } +// Gold LTO plugin takes a `--plugin-opt foo=bar` option as an alias for +// `--plugin-opt=foo=bar`. We want to handle `--plugin-opt=foo=` as an +// option name and `bar` as a value. Unfortunately, OptParser cannot +// handle an option with a space in it. +// +// In this function, we concatenate command line arguments so that +// `--plugin-opt <foo>` is converted to `--plugin-opt=<foo>`. This is a +// bit hacky, but looks like it is still better than handling --plugin-opt +// options by hand. +static void concatLTOPluginOptions(SmallVectorImpl<const char *> &Args) { + SmallVector<const char *, 256> V; + for (size_t I = 0, E = Args.size(); I != E; ++I) { + StringRef S = Args[I]; + if ((S == "-plugin-opt" || S == "--plugin-opt") && I + 1 != E) { + V.push_back(Saver.save(S + "=" + Args[I + 1]).data()); + ++I; + } else { + V.push_back(Args[I]); + } + } + Args = std::move(V); +} + // Parses a given list of options. opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) { // Make InputArgList from string vectors. @@ -102,6 +126,7 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) { // Expand response files (arguments in the form of @<filename>) // and then parse the argument again. cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), Vec); + concatLTOPluginOptions(Vec); Args = this->ParseArgs(Vec, MissingIndex, MissingCount); handleColorDiagnostics(Args); @@ -113,9 +138,9 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) { return Args; } -void elf::printHelp(const char *Argv0) { - ELFOptTable().PrintHelp(outs(), Argv0, "lld", false /*ShowHidden*/, - true /*ShowAllAliases*/); +void elf::printHelp() { + ELFOptTable().PrintHelp(outs(), Config->ProgName.data(), "lld", + false /*ShowHidden*/, true /*ShowAllAliases*/); outs() << "\n"; // Scripts generated by Libtool versions up to at least 2.4.6 (the most @@ -123,13 +148,7 @@ void elf::printHelp(const char *Argv0) { // in a message for the -help option. If it doesn't match, the scripts // assume that the linker doesn't support very basic features such as // shared libraries. Therefore, we need to print out at least "elf". - // Here, we print out all the targets that we support. - outs() << Argv0 << ": supported targets: " - << "elf32-i386 elf32-iamcu elf32-littlearm elf32-ntradbigmips " - << "elf32-ntradlittlemips elf32-powerpc elf32-tradbigmips " - << "elf32-tradlittlemips elf32-x86-64 " - << "elf64-amdgpu elf64-littleaarch64 elf64-powerpc elf64-tradbigmips " - << "elf64-tradlittlemips elf64-x86-64\n"; + outs() << Config->ProgName << ": supported targets: elf\n"; } // Reconstructs command line arguments so that so that you can re-run @@ -208,10 +227,10 @@ Optional<std::string> elf::searchLibrary(StringRef Name) { return None; } -// If a linker script doesn't exist in the current directory, we also look for -// the script in the '-L' search paths. This matches the behaviour of both '-T' -// and linker script INPUT() directives in ld.bfd. -Optional<std::string> elf::searchLinkerScript(StringRef Name) { +// If a linker/version script doesn't exist in the current directory, we also +// look for the script in the '-L' search paths. This matches the behaviour of +// '-T', --version-script=, and linker script INPUT() command in ld.bfd. +Optional<std::string> elf::searchScript(StringRef Name) { if (fs::exists(Name)) return Name.str(); return findFromSearchPaths(Name); diff --git a/contrib/llvm/tools/lld/ELF/EhFrame.cpp b/contrib/llvm/tools/lld/ELF/EhFrame.cpp index 62abc3973e7e..20b32c0a96e6 100644 --- a/contrib/llvm/tools/lld/ELF/EhFrame.cpp +++ b/contrib/llvm/tools/lld/ELF/EhFrame.cpp @@ -20,18 +20,16 @@ #include "Config.h" #include "InputSection.h" #include "Relocations.h" -#include "Strings.h" - +#include "Target.h" #include "lld/Common/ErrorHandler.h" +#include "lld/Common/Strings.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Object/ELF.h" -#include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::ELF; using namespace llvm::dwarf; using namespace llvm::object; -using namespace llvm::support::endian; using namespace lld; using namespace lld::elf; @@ -73,7 +71,7 @@ size_t EhReader::readEhRecordSize() { // First 4 bytes of CIE/FDE is the size of the record. // If it is 0xFFFFFFFF, the next 8 bytes contain the size instead, // but we do not support that format yet. - uint64_t V = read32(D.data(), Config->Endianness); + uint64_t V = read32(D.data()); if (V == UINT32_MAX) failOn(D.data(), "CIE/FDE too large"); uint64_t Size = V + 4; diff --git a/contrib/llvm/tools/lld/ELF/Filesystem.cpp b/contrib/llvm/tools/lld/ELF/Filesystem.cpp index 8d0b5d8a2f1c..5cf240eeca56 100644 --- a/contrib/llvm/tools/lld/ELF/Filesystem.cpp +++ b/contrib/llvm/tools/lld/ELF/Filesystem.cpp @@ -44,7 +44,7 @@ using namespace lld::elf; // The calling thread returns almost immediately. void elf::unlinkAsync(StringRef Path) { // Removing a file is async on windows. -#if defined(LLVM_ON_WIN32) +#if defined(_WIN32) sys::fs::remove(Path); #else if (!ThreadsEnabled || !sys::fs::exists(Path) || diff --git a/contrib/llvm/tools/lld/ELF/GdbIndex.cpp b/contrib/llvm/tools/lld/ELF/GdbIndex.cpp index d27b57f95938..85449a200647 100644 --- a/contrib/llvm/tools/lld/ELF/GdbIndex.cpp +++ b/contrib/llvm/tools/lld/ELF/GdbIndex.cpp @@ -34,7 +34,7 @@ template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *Obj) { .Case(".debug_ranges", &RangeSection) .Case(".debug_line", &LineSection) .Default(nullptr)) { - Sec->maybeUncompress(); + Sec->maybeDecompress(); M->Data = toStringRef(Sec->Data); M->Sec = Sec; continue; diff --git a/contrib/llvm/tools/lld/ELF/GdbIndex.h b/contrib/llvm/tools/lld/ELF/GdbIndex.h index 41ae9d793c11..eba1ba22f879 100644 --- a/contrib/llvm/tools/lld/ELF/GdbIndex.h +++ b/contrib/llvm/tools/lld/ELF/GdbIndex.h @@ -49,7 +49,6 @@ public: return LineSection; } StringRef getFileName() const override { return ""; } - StringRef getCUIndexSection() const override { return ""; } StringRef getAbbrevSection() const override { return AbbrevSection; } StringRef getStringSection() const override { return StrSection; } StringRef getGnuPubNamesSection() const override { diff --git a/contrib/llvm/tools/lld/ELF/ICF.cpp b/contrib/llvm/tools/lld/ELF/ICF.cpp index b1e12e0590d5..075938bd16b9 100644 --- a/contrib/llvm/tools/lld/ELF/ICF.cpp +++ b/contrib/llvm/tools/lld/ELF/ICF.cpp @@ -77,10 +77,13 @@ #include "Config.h" #include "SymbolTable.h" #include "Symbols.h" +#include "SyntheticSections.h" +#include "Writer.h" #include "lld/Common/Threads.h" -#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Object/ELF.h" +#include "llvm/Support/xxhash.h" #include <algorithm> #include <atomic> @@ -112,9 +115,9 @@ private: size_t findBoundary(size_t Begin, size_t End); void forEachClassRange(size_t Begin, size_t End, - std::function<void(size_t, size_t)> Fn); + llvm::function_ref<void(size_t, size_t)> Fn); - void forEachClass(std::function<void(size_t, size_t)> Fn); + void forEachClass(llvm::function_ref<void(size_t, size_t)> Fn); std::vector<InputSection *> Sections; @@ -153,23 +156,40 @@ private: }; } -// Returns a hash value for S. Note that the information about -// relocation targets is not included in the hash value. -template <class ELFT> static uint32_t getHash(InputSection *S) { - return hash_combine(S->Flags, S->getSize(), S->NumRelocations, S->Data); -} - // Returns true if section S is subject of ICF. static bool isEligible(InputSection *S) { - // Don't merge read only data sections unless --icf-data was passed. - if (!(S->Flags & SHF_EXECINSTR) && !Config->ICFData) + if (!S->Live || S->KeepUnique || !(S->Flags & SHF_ALLOC)) + return false; + + // Don't merge writable sections. .data.rel.ro sections are marked as writable + // but are semantically read-only. + if ((S->Flags & SHF_WRITE) && S->Name != ".data.rel.ro" && + !S->Name.startswith(".data.rel.ro.")) return false; - // .init and .fini contains instructions that must be executed to - // initialize and finalize the process. They cannot and should not - // be merged. - return S->Live && (S->Flags & SHF_ALLOC) && !(S->Flags & SHF_WRITE) && - S->Name != ".init" && S->Name != ".fini"; + // SHF_LINK_ORDER sections are ICF'd as a unit with their dependent sections, + // so we don't consider them for ICF individually. + if (S->Flags & SHF_LINK_ORDER) + return false; + + // Don't merge synthetic sections as their Data member is not valid and empty. + // The Data member needs to be valid for ICF as it is used by ICF to determine + // the equality of section contents. + if (isa<SyntheticSection>(S)) + return false; + + // .init and .fini contains instructions that must be executed to initialize + // and finalize the process. They cannot and should not be merged. + if (S->Name == ".init" || S->Name == ".fini") + return false; + + // A user program may enumerate sections named with a C identifier using + // __start_* and __stop_* symbols. We cannot ICF any such sections because + // that could change program semantics. + if (isValidCIdentifier(S->Name)) + return false; + + return true; } // Split an equivalence class into smaller classes. @@ -214,9 +234,6 @@ template <class ELFT> template <class RelTy> bool ICF<ELFT>::constantEq(const InputSection *SecA, ArrayRef<RelTy> RA, const InputSection *SecB, ArrayRef<RelTy> RB) { - if (RA.size() != RB.size()) - return false; - for (size_t I = 0; I < RA.size(); ++I) { if (RA[I].r_offset != RB[I].r_offset || RA[I].getType(Config->IsMips64EL) != RB[I].getType(Config->IsMips64EL)) @@ -284,6 +301,13 @@ bool ICF<ELFT>::equalsConstant(const InputSection *A, const InputSection *B) { A->getSize() != B->getSize() || A->Data != B->Data) return false; + // If two sections have different output sections, we cannot merge them. + // FIXME: This doesn't do the right thing in the case where there is a linker + // script. We probably need to move output section assignment before ICF to + // get the correct behaviour here. + if (getOutputSectionName(A) != getOutputSectionName(B)) + return false; + if (A->AreRelocsRela) return constantEq(A, A->template relas<ELFT>(), B, B->template relas<ELFT>()); @@ -350,17 +374,12 @@ template <class ELFT> size_t ICF<ELFT>::findBoundary(size_t Begin, size_t End) { // vector. Therefore, Sections vector can be considered as contiguous // groups of sections, grouped by the class. // -// This function calls Fn on every group that starts within [Begin, End). -// Note that a group must start in that range but doesn't necessarily -// have to end before End. +// This function calls Fn on every group within [Begin, End). template <class ELFT> void ICF<ELFT>::forEachClassRange(size_t Begin, size_t End, - std::function<void(size_t, size_t)> Fn) { - if (Begin > 0) - Begin = findBoundary(Begin - 1, End); - + llvm::function_ref<void(size_t, size_t)> Fn) { while (Begin < End) { - size_t Mid = findBoundary(Begin, Sections.size()); + size_t Mid = findBoundary(Begin, End); Fn(Begin, Mid); Begin = Mid; } @@ -368,7 +387,7 @@ void ICF<ELFT>::forEachClassRange(size_t Begin, size_t End, // Call Fn on each equivalence class. template <class ELFT> -void ICF<ELFT>::forEachClass(std::function<void(size_t, size_t)> Fn) { +void ICF<ELFT>::forEachClass(llvm::function_ref<void(size_t, size_t)> Fn) { // If threading is disabled or the number of sections are // too small to use threading, call Fn sequentially. if (!ThreadsEnabled || Sections.size() < 1024) { @@ -380,16 +399,32 @@ void ICF<ELFT>::forEachClass(std::function<void(size_t, size_t)> Fn) { Current = Cnt % 2; Next = (Cnt + 1) % 2; - // Split sections into 256 shards and call Fn in parallel. - size_t NumShards = 256; + // Shard into non-overlapping intervals, and call Fn in parallel. + // The sharding must be completed before any calls to Fn are made + // so that Fn can modify the Chunks in its shard without causing data + // races. + const size_t NumShards = 256; size_t Step = Sections.size() / NumShards; - parallelForEachN(0, NumShards, [&](size_t I) { - size_t End = (I == NumShards - 1) ? Sections.size() : (I + 1) * Step; - forEachClassRange(I * Step, End, Fn); + size_t Boundaries[NumShards + 1]; + Boundaries[0] = 0; + Boundaries[NumShards] = Sections.size(); + + parallelForEachN(1, NumShards, [&](size_t I) { + Boundaries[I] = findBoundary((I - 1) * Step, Sections.size()); + }); + + parallelForEachN(1, NumShards + 1, [&](size_t I) { + if (Boundaries[I - 1] < Boundaries[I]) + forEachClassRange(Boundaries[I - 1], Boundaries[I], Fn); }); ++Cnt; } +static void print(const Twine &S) { + if (Config->PrintIcfSections) + message(S); +} + // The main function of ICF. template <class ELFT> void ICF<ELFT>::run() { // Collect sections to merge. @@ -401,7 +436,7 @@ template <class ELFT> void ICF<ELFT>::run() { // Initially, we use hash values to partition sections. parallelForEach(Sections, [&](InputSection *S) { // Set MSB to 1 to avoid collisions with non-hash IDs. - S->Class[0] = getHash<ELFT>(S) | (1 << 31); + S->Class[0] = xxHash64(S->Data) | (1U << 31); }); // From now on, sections in Sections vector are ordered so that sections @@ -424,25 +459,21 @@ template <class ELFT> void ICF<ELFT>::run() { log("ICF needed " + Twine(Cnt) + " iterations"); // Merge sections by the equivalence class. - forEachClass([&](size_t Begin, size_t End) { + forEachClassRange(0, Sections.size(), [&](size_t Begin, size_t End) { if (End - Begin == 1) return; - - log("selected " + Sections[Begin]->Name); + print("selected section " + toString(Sections[Begin])); for (size_t I = Begin + 1; I < End; ++I) { - log(" removed " + Sections[I]->Name); + print(" removing identical section " + toString(Sections[I])); Sections[Begin]->replace(Sections[I]); + + // At this point we know sections merged are fully identical and hence + // we want to remove duplicate implicit dependencies such as link order + // and relocation sections. + for (InputSection *IS : Sections[I]->DependentSections) + IS->Live = false; } }); - - // Mark ARM Exception Index table sections that refer to folded code - // sections as not live. These sections have an implict dependency - // via the link order dependency. - if (Config->EMachine == EM_ARM) - for (InputSectionBase *Sec : InputSections) - if (auto *S = dyn_cast<InputSection>(Sec)) - if (S->Flags & SHF_LINK_ORDER) - S->Live = S->getLinkOrderDep()->Live; } // ICF entry point function. diff --git a/contrib/llvm/tools/lld/ELF/ICF.h b/contrib/llvm/tools/lld/ELF/ICF.h index 24219855fc17..a6c8636ead6d 100644 --- a/contrib/llvm/tools/lld/ELF/ICF.h +++ b/contrib/llvm/tools/lld/ELF/ICF.h @@ -12,8 +12,10 @@ namespace lld { namespace elf { + template <class ELFT> void doIcf(); -} + +} // namespace elf } // namespace lld #endif diff --git a/contrib/llvm/tools/lld/ELF/InputFiles.cpp b/contrib/llvm/tools/lld/ELF/InputFiles.cpp index dfd5bbf1e0fc..0eb605a556ae 100644 --- a/contrib/llvm/tools/lld/ELF/InputFiles.cpp +++ b/contrib/llvm/tools/lld/ELF/InputFiles.cpp @@ -38,14 +38,23 @@ using namespace llvm::sys::fs; using namespace lld; using namespace lld::elf; +bool InputFile::IsInGroup; +uint32_t InputFile::NextGroupId; std::vector<BinaryFile *> elf::BinaryFiles; std::vector<BitcodeFile *> elf::BitcodeFiles; +std::vector<LazyObjFile *> elf::LazyObjFiles; std::vector<InputFile *> elf::ObjectFiles; std::vector<InputFile *> elf::SharedFiles; TarWriter *elf::Tar; -InputFile::InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {} +InputFile::InputFile(Kind K, MemoryBufferRef M) + : MB(M), GroupId(NextGroupId), FileKind(K) { + // All files within the same --{start,end}-group get the same group ID. + // Otherwise, a new file will get a new group ID. + if (!IsInGroup) + ++NextGroupId; +} Optional<MemoryBufferRef> elf::readFile(StringRef Path) { // The --chroot option changes our virtual root directory. @@ -55,7 +64,7 @@ Optional<MemoryBufferRef> elf::readFile(StringRef Path) { log(Path); - auto MBOrErr = MemoryBuffer::getFile(Path); + auto MBOrErr = MemoryBuffer::getFile(Path, -1, false); if (auto EC = MBOrErr.getError()) { error("cannot open " + Path + ": " + EC.message()); return None; @@ -115,51 +124,60 @@ std::string InputFile::getSrcMsg(const Symbol &Sym, InputSectionBase &Sec, } template <class ELFT> void ObjFile<ELFT>::initializeDwarf() { - DWARFContext Dwarf(make_unique<LLDDwarfObj<ELFT>>(this)); - const DWARFObject &Obj = Dwarf.getDWARFObj(); - DwarfLine.reset(new DWARFDebugLine); + Dwarf = llvm::make_unique<DWARFContext>(make_unique<LLDDwarfObj<ELFT>>(this)); + const DWARFObject &Obj = Dwarf->getDWARFObj(); DWARFDataExtractor LineData(Obj, Obj.getLineSection(), Config->IsLE, Config->Wordsize); - // The second parameter is offset in .debug_line section - // for compilation unit (CU) of interest. We have only one - // CU (object file), so offset is always 0. - // FIXME: Provide the associated DWARFUnit if there is one. DWARF v5 - // needs it in order to find indirect strings. - const DWARFDebugLine::LineTable *LT = - DwarfLine->getOrParseLineTable(LineData, 0, nullptr); - - // Return if there is no debug information about CU available. - if (!Dwarf.getNumCompileUnits()) - return; - - // Loop over variable records and insert them to VariableLoc. - DWARFCompileUnit *CU = Dwarf.getCompileUnitAtIndex(0); - for (const auto &Entry : CU->dies()) { - DWARFDie Die(CU, &Entry); - // Skip all tags that are not variables. - if (Die.getTag() != dwarf::DW_TAG_variable) + for (std::unique_ptr<DWARFCompileUnit> &CU : Dwarf->compile_units()) { + auto Report = [](Error Err) { + handleAllErrors(std::move(Err), + [](ErrorInfoBase &Info) { warn(Info.message()); }); + }; + Expected<const DWARFDebugLine::LineTable *> ExpectedLT = + Dwarf->getLineTableForUnit(CU.get(), Report); + const DWARFDebugLine::LineTable *LT = nullptr; + if (ExpectedLT) + LT = *ExpectedLT; + else + Report(ExpectedLT.takeError()); + if (!LT) continue; + LineTables.push_back(LT); - // Skip if a local variable because we don't need them for generating error - // messages. In general, only non-local symbols can fail to be linked. - if (!dwarf::toUnsigned(Die.find(dwarf::DW_AT_external), 0)) - continue; + // Loop over variable records and insert them to VariableLoc. + for (const auto &Entry : CU->dies()) { + DWARFDie Die(CU.get(), &Entry); + // Skip all tags that are not variables. + if (Die.getTag() != dwarf::DW_TAG_variable) + continue; - // Get the source filename index for the variable. - unsigned File = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_file), 0); - if (!LT->hasFileAtIndex(File)) - continue; + // Skip if a local variable because we don't need them for generating + // error messages. In general, only non-local symbols can fail to be + // linked. + if (!dwarf::toUnsigned(Die.find(dwarf::DW_AT_external), 0)) + continue; - // Get the line number on which the variable is declared. - unsigned Line = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_line), 0); + // Get the source filename index for the variable. + unsigned File = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_file), 0); + if (!LT->hasFileAtIndex(File)) + continue; - // Get the name of the variable and add the collected information to - // VariableLoc. Usually Name is non-empty, but it can be empty if the input - // object file lacks some debug info. - StringRef Name = dwarf::toString(Die.find(dwarf::DW_AT_name), ""); - if (!Name.empty()) - VariableLoc.insert({Name, {File, Line}}); + // Get the line number on which the variable is declared. + unsigned Line = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_line), 0); + + // Here we want to take the variable name to add it into VariableLoc. + // Variable can have regular and linkage name associated. At first, we try + // to get linkage name as it can be different, for example when we have + // two variables in different namespaces of the same object. Use common + // name otherwise, but handle the case when it also absent in case if the + // input object file lacks some debug info. + StringRef Name = + dwarf::toString(Die.find(dwarf::DW_AT_linkage_name), + dwarf::toString(Die.find(dwarf::DW_AT_name), "")); + if (!Name.empty()) + VariableLoc.insert({Name, {LT, File, Line}}); + } } } @@ -170,11 +188,6 @@ Optional<std::pair<std::string, unsigned>> ObjFile<ELFT>::getVariableLoc(StringRef Name) { llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); }); - // There is always only one CU so it's offset is 0. - const DWARFDebugLine::LineTable *LT = DwarfLine->getLineTable(0); - if (!LT) - return None; - // Return if we have no debug information about data object. auto It = VariableLoc.find(Name); if (It == VariableLoc.end()) @@ -182,12 +195,12 @@ ObjFile<ELFT>::getVariableLoc(StringRef Name) { // Take file name string from line table. std::string FileName; - if (!LT->getFileNameByIndex( - It->second.first /* File */, nullptr, + if (!It->second.LT->getFileNameByIndex( + It->second.File, nullptr, DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FileName)) return None; - return std::make_pair(FileName, It->second.second /*Line*/); + return std::make_pair(FileName, It->second.Line); } // Returns source line information for a given offset @@ -197,29 +210,15 @@ Optional<DILineInfo> ObjFile<ELFT>::getDILineInfo(InputSectionBase *S, uint64_t Offset) { llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); }); - // The offset to CU is 0. - const DWARFDebugLine::LineTable *Tbl = DwarfLine->getLineTable(0); - if (!Tbl) - return None; - // Use fake address calcuated by adding section file offset and offset in // section. See comments for ObjectInfo class. DILineInfo Info; - Tbl->getFileLineInfoForAddress( - S->getOffsetInFile() + Offset, nullptr, - DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Info); - if (Info.Line == 0) - return None; - return Info; -} - -// Returns source line information for a given offset -// using DWARF debug info. -template <class ELFT> -std::string ObjFile<ELFT>::getLineInfo(InputSectionBase *S, uint64_t Offset) { - if (Optional<DILineInfo> Info = getDILineInfo(S, Offset)) - return Info->FileName + ":" + std::to_string(Info->Line); - return ""; + for (const llvm::DWARFDebugLine::LineTable *LT : LineTables) + if (LT->getFileLineInfoForAddress( + S->getOffsetInFile() + Offset, nullptr, + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Info)) + return Info; + return None; } // Returns "<internal>", "foo.a(bar.o)" or "baz.o". @@ -249,7 +248,7 @@ ELFFileBase<ELFT>::ELFFileBase(Kind K, MemoryBufferRef MB) : InputFile(K, MB) { template <class ELFT> typename ELFT::SymRange ELFFileBase<ELFT>::getGlobalELFSyms() { - return makeArrayRef(ELFSyms.begin() + FirstNonLocal, ELFSyms.end()); + return makeArrayRef(ELFSyms.begin() + FirstGlobal, ELFSyms.end()); } template <class ELFT> @@ -260,9 +259,9 @@ uint32_t ELFFileBase<ELFT>::getSectionIndex(const Elf_Sym &Sym) const { template <class ELFT> void ELFFileBase<ELFT>::initSymtab(ArrayRef<Elf_Shdr> Sections, const Elf_Shdr *Symtab) { - FirstNonLocal = Symtab->sh_info; + FirstGlobal = Symtab->sh_info; ELFSyms = CHECK(getObj().symbols(Symtab), this); - if (FirstNonLocal == 0 || FirstNonLocal > ELFSyms.size()) + if (FirstGlobal == 0 || FirstGlobal > ELFSyms.size()) fatal(toString(this) + ": invalid sh_info in symbol table"); StringTable = @@ -278,17 +277,22 @@ ObjFile<ELFT>::ObjFile(MemoryBufferRef M, StringRef ArchiveName) template <class ELFT> ArrayRef<Symbol *> ObjFile<ELFT>::getLocalSymbols() { if (this->Symbols.empty()) return {}; - return makeArrayRef(this->Symbols).slice(1, this->FirstNonLocal - 1); + return makeArrayRef(this->Symbols).slice(1, this->FirstGlobal - 1); } template <class ELFT> ArrayRef<Symbol *> ObjFile<ELFT>::getGlobalSymbols() { - return makeArrayRef(this->Symbols).slice(this->FirstNonLocal); + return makeArrayRef(this->Symbols).slice(this->FirstGlobal); } template <class ELFT> void ObjFile<ELFT>::parse(DenseSet<CachedHashStringRef> &ComdatGroups) { - // Read section and symbol tables. - initializeSections(ComdatGroups); + // Read a section table. JustSymbols is usually false. + if (this->JustSymbols) + initializeJustSymbols(); + else + initializeSections(ComdatGroups); + + // Read a symbol table. initializeSymbols(); } @@ -312,7 +316,7 @@ StringRef ObjFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> Sections, // we use a section name as a signature. // // Such SHT_GROUP sections are invalid from the perspective of the ELF - // standard, but GNU gold 1.14 (the neweset version as of July 2017) or + // standard, but GNU gold 1.14 (the newest version as of July 2017) or // older produce such sections as outputs for the -r option, so we need // a bug-compatibility. if (Signature.empty() && Sym->getType() == STT_SECTION) @@ -332,9 +336,19 @@ ObjFile<ELFT>::getShtGroupEntries(const Elf_Shdr &Sec) { } template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) { - // We don't merge sections if -O0 (default is -O1). This makes sometimes - // the linker significantly faster, although the output will be bigger. - if (Config->Optimize == 0) + // On a regular link we don't merge sections if -O0 (default is -O1). This + // sometimes makes the linker significantly faster, although the output will + // be bigger. + // + // Doing the same for -r would create a problem as it would combine sections + // with different sh_entsize. One option would be to just copy every SHF_MERGE + // section as is to the output. While this would produce a valid ELF file with + // usable SHF_MERGE sections, tools like (llvm-)?dwarfdump get confused when + // they see two .debug_str. We could have separate logic for combining + // SHF_MERGE sections based both on their name and sh_entsize, but that seems + // to be more trouble than it is worth. Instead, we just use the regular (-O1) + // logic for -r. + if (Config->Optimize == 0 && !Config->Relocatable) return false; // A mergeable section with size 0 is useless because they don't have @@ -365,12 +379,33 @@ template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) { return true; } +// This is for --just-symbols. +// +// --just-symbols is a very minor feature that allows you to link your +// output against other existing program, so that if you load both your +// program and the other program into memory, your output can refer the +// other program's symbols. +// +// When the option is given, we link "just symbols". The section table is +// initialized with null pointers. +template <class ELFT> void ObjFile<ELFT>::initializeJustSymbols() { + ArrayRef<Elf_Shdr> ObjSections = CHECK(this->getObj().sections(), this); + this->Sections.resize(ObjSections.size()); + + for (const Elf_Shdr &Sec : ObjSections) { + if (Sec.sh_type != SHT_SYMTAB) + continue; + this->initSymtab(ObjSections, &Sec); + return; + } +} + template <class ELFT> void ObjFile<ELFT>::initializeSections( DenseSet<CachedHashStringRef> &ComdatGroups) { const ELFFile<ELFT> &Obj = this->getObj(); - ArrayRef<Elf_Shdr> ObjSections = CHECK(this->getObj().sections(), this); + ArrayRef<Elf_Shdr> ObjSections = CHECK(Obj.sections(), this); uint64_t Size = ObjSections.size(); this->Sections.resize(Size); this->SectionStringTable = @@ -385,6 +420,17 @@ void ObjFile<ELFT>::initializeSections( // if -r is given, we'll let the final link discard such sections. // This is compatible with GNU. if ((Sec.sh_flags & SHF_EXCLUDE) && !Config->Relocatable) { + if (Sec.sh_type == SHT_LLVM_ADDRSIG) { + // We ignore the address-significance table if we know that the object + // file was created by objcopy or ld -r. This is because these tools + // will reorder the symbols in the symbol table, invalidating the data + // in the address-significance table, which refers to symbols by index. + if (Sec.sh_link != 0) + this->AddrsigSec = &Sec; + else if (Config->ICF == ICFLevel::Safe) + warn(toString(this) + ": --icf=safe is incompatible with object " + "files created using objcopy or ld -r"); + } this->Sections[I] = &InputSection::Discarded; continue; } @@ -435,8 +481,15 @@ void ObjFile<ELFT>::initializeSections( if (Sec.sh_link >= this->Sections.size()) fatal(toString(this) + ": invalid sh_link index: " + Twine(Sec.sh_link)); - this->Sections[Sec.sh_link]->DependentSections.push_back( - cast<InputSection>(this->Sections[I])); + + InputSectionBase *LinkSec = this->Sections[Sec.sh_link]; + InputSection *IS = cast<InputSection>(this->Sections[I]); + LinkSec->DependentSections.push_back(IS); + if (!isa<InputSection>(LinkSec)) + error("a section " + IS->Name + + " with SHF_LINK_ORDER should not refer a non-regular " + "section: " + + toString(LinkSec)); } } } @@ -573,10 +626,11 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) { } case SHT_RELA: case SHT_REL: { - // Find the relocation target section and associate this - // section with it. Target can be discarded, for example - // if it is a duplicated member of SHT_GROUP section, we - // do not create or proccess relocatable sections then. + // Find a relocation target section and associate this section with that. + // Target may have been discarded if it is in a different section group + // and the group is discarded, even though it's a violation of the + // spec. We handle that situation gracefully by discarding dangling + // relocation sections. InputSectionBase *Target = getRelocTarget(Sec); if (!Target) return nullptr; @@ -591,32 +645,28 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) { fatal(toString(this) + ": multiple relocation sections to one section are not supported"); - // Mergeable sections with relocations are tricky because relocations - // need to be taken into account when comparing section contents for - // merging. It's not worth supporting such mergeable sections because - // they are rare and it'd complicates the internal design (we usually - // have to determine if two sections are mergeable early in the link - // process much before applying relocations). We simply handle mergeable - // sections with relocations as non-mergeable. + // ELF spec allows mergeable sections with relocations, but they are + // rare, and it is in practice hard to merge such sections by contents, + // because applying relocations at end of linking changes section + // contents. So, we simply handle such sections as non-mergeable ones. + // Degrading like this is acceptable because section merging is optional. if (auto *MS = dyn_cast<MergeInputSection>(Target)) { Target = toRegularSection(MS); this->Sections[Sec.sh_info] = Target; } - size_t NumRelocations; if (Sec.sh_type == SHT_RELA) { ArrayRef<Elf_Rela> Rels = CHECK(this->getObj().relas(&Sec), this); Target->FirstRelocation = Rels.begin(); - NumRelocations = Rels.size(); + Target->NumRelocations = Rels.size(); Target->AreRelocsRela = true; } else { ArrayRef<Elf_Rel> Rels = CHECK(this->getObj().rels(&Sec), this); Target->FirstRelocation = Rels.begin(); - NumRelocations = Rels.size(); + Target->NumRelocations = Rels.size(); Target->AreRelocsRela = false; } - assert(isUInt<31>(NumRelocations)); - Target->NumRelocations = NumRelocations; + assert(isUInt<31>(Target->NumRelocations)); // Relocation sections processed by the linker are usually removed // from the output, so returning `nullptr` for the normal case. @@ -648,13 +698,24 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) { if (Name == ".note.GNU-stack") return &InputSection::Discarded; - // Split stacks is a feature to support a discontiguous stack. At least - // as of 2017, it seems that the feature is not being used widely. - // Only GNU gold supports that. We don't. For the details about that, - // see https://gcc.gnu.org/wiki/SplitStacks + // Split stacks is a feature to support a discontiguous stack, + // commonly used in the programming language Go. For the details, + // see https://gcc.gnu.org/wiki/SplitStacks. An object file compiled + // for split stack will include a .note.GNU-split-stack section. if (Name == ".note.GNU-split-stack") { - error(toString(this) + - ": object file compiled with -fsplit-stack is not supported"); + if (Config->Relocatable) { + error("Cannot mix split-stack and non-split-stack in a relocatable link"); + return &InputSection::Discarded; + } + this->SplitStack = true; + return &InputSection::Discarded; + } + + // An object file cmpiled for split stack, but where some of the + // functions were compiled with the no_split_stack_attribute will + // include a .note.GNU-no-split-stack section. + if (Name == ".note.GNU-no-split-stack") { + this->SomeNoSplitStack = true; return &InputSection::Discarded; } @@ -666,6 +727,14 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) { if (Name.startswith(".gnu.linkonce.")) return &InputSection::Discarded; + // If we are creating a new .build-id section, strip existing .build-id + // sections so that the output won't have more than one .build-id. + // This is not usually a problem because input object files normally don't + // have .build-id sections, but you can create such files by + // "ld.{bfd,gold,lld} -r --build-id", and we want to guard against it. + if (Name == ".note.gnu.build-id" && Config->BuildId != BuildIdKind::None) + return &InputSection::Discarded; + // The linker merges EH (exception handling) frames and creates a // .eh_frame_hdr section for runtime. So we handle them with a special // class. For relocatable outputs, they are just passed through. @@ -747,33 +816,33 @@ ArchiveFile::ArchiveFile(std::unique_ptr<Archive> &&File) File(std::move(File)) {} template <class ELFT> void ArchiveFile::parse() { - Symbols.reserve(File->getNumberOfSymbols()); for (const Archive::Symbol &Sym : File->symbols()) - Symbols.push_back(Symtab->addLazyArchive<ELFT>(Sym.getName(), *this, Sym)); + Symtab->addLazyArchive<ELFT>(Sym.getName(), *this, Sym); } // Returns a buffer pointing to a member file containing a given symbol. -std::pair<MemoryBufferRef, uint64_t> -ArchiveFile::getMember(const Archive::Symbol *Sym) { +InputFile *ArchiveFile::fetch(const Archive::Symbol &Sym) { Archive::Child C = - CHECK(Sym->getMember(), toString(this) + - ": could not get the member for symbol " + - Sym->getName()); + CHECK(Sym.getMember(), toString(this) + + ": could not get the member for symbol " + + Sym.getName()); if (!Seen.insert(C.getChildOffset()).second) - return {MemoryBufferRef(), 0}; + return nullptr; - MemoryBufferRef Ret = + MemoryBufferRef MB = CHECK(C.getMemoryBufferRef(), toString(this) + ": could not get the buffer for the member defining symbol " + - Sym->getName()); + Sym.getName()); + + if (Tar && C.getParent()->isThin()) + Tar->append(relativeToRoot(CHECK(C.getFullName(), this)), MB.getBuffer()); - if (C.getParent()->isThin() && Tar) - Tar->append(relativeToRoot(CHECK(C.getFullName(), this)), Ret.getBuffer()); - if (C.getParent()->isThin()) - return {Ret, 0}; - return {Ret, C.getChildOffset()}; + InputFile *File = createObjectFile( + MB, getName(), C.getParent()->isThin() ? 0 : C.getChildOffset()); + File->GroupId = GroupId; + return File; } template <class ELFT> @@ -830,34 +899,42 @@ template <class ELFT> void SharedFile<ELFT>::parseSoName() { } } +// Parses ".gnu.version" section which is a parallel array for the symbol table. +// If a given file doesn't have ".gnu.version" section, returns VER_NDX_GLOBAL. +template <class ELFT> std::vector<uint32_t> SharedFile<ELFT>::parseVersyms() { + size_t Size = this->ELFSyms.size() - this->FirstGlobal; + if (!VersymSec) + return std::vector<uint32_t>(Size, VER_NDX_GLOBAL); + + const char *Base = this->MB.getBuffer().data(); + const Elf_Versym *Versym = + reinterpret_cast<const Elf_Versym *>(Base + VersymSec->sh_offset) + + this->FirstGlobal; + + std::vector<uint32_t> Ret(Size); + for (size_t I = 0; I < Size; ++I) + Ret[I] = Versym[I].vs_index; + return Ret; +} + // Parse the version definitions in the object file if present. Returns a vector // whose nth element contains a pointer to the Elf_Verdef for version identifier -// n. Version identifiers that are not definitions map to nullptr. The array -// always has at least length 1. +// n. Version identifiers that are not definitions map to nullptr. template <class ELFT> -std::vector<const typename ELFT::Verdef *> -SharedFile<ELFT>::parseVerdefs(const Elf_Versym *&Versym) { - std::vector<const Elf_Verdef *> Verdefs(1); - // We only need to process symbol versions for this DSO if it has both a - // versym and a verdef section, which indicates that the DSO contains symbol - // version definitions. - if (!VersymSec || !VerdefSec) - return Verdefs; - - // The location of the first global versym entry. - const char *Base = this->MB.getBuffer().data(); - Versym = reinterpret_cast<const Elf_Versym *>(Base + VersymSec->sh_offset) + - this->FirstNonLocal; +std::vector<const typename ELFT::Verdef *> SharedFile<ELFT>::parseVerdefs() { + if (!VerdefSec) + return {}; // We cannot determine the largest verdef identifier without inspecting // every Elf_Verdef, but both bfd and gold assign verdef identifiers // sequentially starting from 1, so we predict that the largest identifier // will be VerdefCount. unsigned VerdefCount = VerdefSec->sh_info; - Verdefs.resize(VerdefCount + 1); + std::vector<const Elf_Verdef *> Verdefs(VerdefCount + 1); // Build the Verdefs array by following the chain of Elf_Verdef objects // from the start of the .gnu.version_d section. + const char *Base = this->MB.getBuffer().data(); const char *Verdef = Base + VerdefSec->sh_offset; for (unsigned I = 0; I != VerdefCount; ++I) { auto *CurVerdef = reinterpret_cast<const Elf_Verdef *>(Verdef); @@ -871,82 +948,99 @@ SharedFile<ELFT>::parseVerdefs(const Elf_Versym *&Versym) { return Verdefs; } +// We do not usually care about alignments of data in shared object +// files because the loader takes care of it. However, if we promote a +// DSO symbol to point to .bss due to copy relocation, we need to keep +// the original alignment requirements. We infer it in this function. +template <class ELFT> +uint32_t SharedFile<ELFT>::getAlignment(ArrayRef<Elf_Shdr> Sections, + const Elf_Sym &Sym) { + uint64_t Ret = UINT64_MAX; + if (Sym.st_value) + Ret = 1ULL << countTrailingZeros((uint64_t)Sym.st_value); + if (0 < Sym.st_shndx && Sym.st_shndx < Sections.size()) + Ret = std::min<uint64_t>(Ret, Sections[Sym.st_shndx].sh_addralign); + return (Ret > UINT32_MAX) ? 0 : Ret; +} + // Fully parse the shared object file. This must be called after parseSoName(). +// +// This function parses symbol versions. If a DSO has version information, +// the file has a ".gnu.version_d" section which contains symbol version +// definitions. Each symbol is associated to one version through a table in +// ".gnu.version" section. That table is a parallel array for the symbol +// table, and each table entry contains an index in ".gnu.version_d". +// +// The special index 0 is reserved for VERF_NDX_LOCAL and 1 is for +// VER_NDX_GLOBAL. There's no table entry for these special versions in +// ".gnu.version_d". +// +// The file format for symbol versioning is perhaps a bit more complicated +// than necessary, but you can easily understand the code if you wrap your +// head around the data structure described above. template <class ELFT> void SharedFile<ELFT>::parseRest() { - // Create mapping from version identifiers to Elf_Verdef entries. - const Elf_Versym *Versym = nullptr; - Verdefs = parseVerdefs(Versym); - + Verdefs = parseVerdefs(); // parse .gnu.version_d + std::vector<uint32_t> Versyms = parseVersyms(); // parse .gnu.version ArrayRef<Elf_Shdr> Sections = CHECK(this->getObj().sections(), this); + // System libraries can have a lot of symbols with versions. Using a + // fixed buffer for computing the versions name (foo@ver) can save a + // lot of allocations. + SmallString<0> VersionedNameBuffer; + // Add symbols to the symbol table. - Elf_Sym_Range Syms = this->getGlobalELFSyms(); - for (const Elf_Sym &Sym : Syms) { - unsigned VersymIndex = VER_NDX_GLOBAL; - if (Versym) { - VersymIndex = Versym->vs_index; - ++Versym; - } - bool Hidden = VersymIndex & VERSYM_HIDDEN; - VersymIndex = VersymIndex & ~VERSYM_HIDDEN; + ArrayRef<Elf_Sym> Syms = this->getGlobalELFSyms(); + for (size_t I = 0; I < Syms.size(); ++I) { + const Elf_Sym &Sym = Syms[I]; StringRef Name = CHECK(Sym.getName(this->StringTable), this); if (Sym.isUndefined()) { - Undefs.push_back(Name); + Symbol *S = Symtab->addUndefined<ELFT>(Name, Sym.getBinding(), + Sym.st_other, Sym.getType(), + /*CanOmitFromDynSym=*/false, this); + S->ExportDynamic = true; continue; } + // ELF spec requires that all local symbols precede weak or global + // symbols in each symbol table, and the index of first non-local symbol + // is stored to sh_info. If a local symbol appears after some non-local + // symbol, that's a violation of the spec. if (Sym.getBinding() == STB_LOCAL) { warn("found local symbol '" + Name + "' in global part of symbol table in file " + toString(this)); continue; } - if (Config->EMachine == EM_MIPS) { - // FIXME: MIPS BFD linker puts _gp_disp symbol into DSO files - // and incorrectly assigns VER_NDX_LOCAL to this section global - // symbol. Here is a workaround for this bug. - if (Versym && VersymIndex == VER_NDX_LOCAL && Name == "_gp_disp") - continue; - } - - const Elf_Verdef *Ver = nullptr; - if (VersymIndex != VER_NDX_GLOBAL) { - if (VersymIndex >= Verdefs.size() || VersymIndex == VER_NDX_LOCAL) { - error("corrupt input file: version definition index " + - Twine(VersymIndex) + " for symbol " + Name + - " is out of bounds\n>>> defined in " + toString(this)); - continue; - } - Ver = Verdefs[VersymIndex]; - } else { - VersymIndex = 0; - } - - // We do not usually care about alignments of data in shared object - // files because the loader takes care of it. However, if we promote a - // DSO symbol to point to .bss due to copy relocation, we need to keep - // the original alignment requirements. We infer it here. - uint64_t Alignment = 1; - if (Sym.st_value) - Alignment = 1ULL << countTrailingZeros((uint64_t)Sym.st_value); - if (0 < Sym.st_shndx && Sym.st_shndx < Sections.size()) { - uint64_t SecAlign = Sections[Sym.st_shndx].sh_addralign; - Alignment = std::min(Alignment, SecAlign); - } - if (Alignment > UINT32_MAX) - error(toString(this) + ": alignment too large: " + Name); + // MIPS BFD linker puts _gp_disp symbol into DSO files and incorrectly + // assigns VER_NDX_LOCAL to this section global symbol. Here is a + // workaround for this bug. + uint32_t Idx = Versyms[I] & ~VERSYM_HIDDEN; + if (Config->EMachine == EM_MIPS && Idx == VER_NDX_LOCAL && + Name == "_gp_disp") + continue; - if (!Hidden) - Symtab->addShared(Name, *this, Sym, Alignment, VersymIndex); + uint64_t Alignment = getAlignment(Sections, Sym); + if (!(Versyms[I] & VERSYM_HIDDEN)) + Symtab->addShared(Name, *this, Sym, Alignment, Idx); // Also add the symbol with the versioned name to handle undefined symbols // with explicit versions. - if (Ver) { - StringRef VerName = this->StringTable.data() + Ver->getAux()->vda_name; - Name = Saver.save(Name + "@" + VerName); - Symtab->addShared(Name, *this, Sym, Alignment, VersymIndex); + if (Idx == VER_NDX_GLOBAL) + continue; + + if (Idx >= Verdefs.size() || Idx == VER_NDX_LOCAL) { + error("corrupt input file: version definition index " + Twine(Idx) + + " for symbol " + Name + " is out of bounds\n>>> defined in " + + toString(this)); + continue; } + + StringRef VerName = + this->StringTable.data() + Verdefs[Idx]->getAux()->vda_name; + VersionedNameBuffer.clear(); + Name = (Name + "@" + VerName).toStringRef(VersionedNameBuffer); + Symtab->addShared(Saver.save(Name), *this, Sym, Alignment, Idx); } } @@ -979,8 +1073,9 @@ static uint8_t getBitcodeMachineKind(StringRef Path, const Triple &T) { case Triple::x86_64: return EM_X86_64; default: - fatal(Path + ": could not infer e_machine from bitcode target triple " + + error(Path + ": could not infer e_machine from bitcode target triple " + T.str()); + return EM_NONE; } } @@ -989,17 +1084,21 @@ BitcodeFile::BitcodeFile(MemoryBufferRef MB, StringRef ArchiveName, : InputFile(BitcodeKind, MB) { this->ArchiveName = ArchiveName; - // Here we pass a new MemoryBufferRef which is identified by ArchiveName - // (the fully resolved path of the archive) + member name + offset of the - // member in the archive. - // ThinLTO uses the MemoryBufferRef identifier to access its internal - // data structures and if two archives define two members with the same name, - // this causes a collision which result in only one of the objects being - // taken into consideration at LTO time (which very likely causes undefined - // symbols later in the link stage). - MemoryBufferRef MBRef(MB.getBuffer(), - Saver.save(ArchiveName + MB.getBufferIdentifier() + - utostr(OffsetInArchive))); + std::string Path = MB.getBufferIdentifier().str(); + if (Config->ThinLTOIndexOnly) + Path = replaceThinLTOSuffix(MB.getBufferIdentifier()); + + // ThinLTO assumes that all MemoryBufferRefs given to it have a unique + // name. If two archives define two members with the same name, this + // causes a collision which result in only one of the objects being taken + // into consideration at LTO time (which very likely causes undefined + // symbols later in the link stage). So we append file offset to make + // filename unique. + MemoryBufferRef MBRef( + MB.getBuffer(), + Saver.save(ArchiveName + Path + + (ArchiveName.empty() ? "" : utostr(OffsetInArchive)))); + Obj = CHECK(lto::InputFile::create(MBRef), this); Triple T(Obj->getTargetTriple()); @@ -1023,7 +1122,7 @@ template <class ELFT> static Symbol *createBitcodeSymbol(const std::vector<bool> &KeptComdats, const lto::InputFile::Symbol &ObjSym, BitcodeFile &F) { - StringRef NameRef = Saver.save(ObjSym.getName()); + StringRef Name = Saver.save(ObjSym.getName()); uint32_t Binding = ObjSym.isWeak() ? STB_WEAK : STB_GLOBAL; uint8_t Type = ObjSym.isTLS() ? STT_TLS : STT_NOTYPE; @@ -1032,20 +1131,20 @@ static Symbol *createBitcodeSymbol(const std::vector<bool> &KeptComdats, int C = ObjSym.getComdatIndex(); if (C != -1 && !KeptComdats[C]) - return Symtab->addUndefined<ELFT>(NameRef, Binding, Visibility, Type, + return Symtab->addUndefined<ELFT>(Name, Binding, Visibility, Type, CanOmitFromDynSym, &F); if (ObjSym.isUndefined()) - return Symtab->addUndefined<ELFT>(NameRef, Binding, Visibility, Type, + return Symtab->addUndefined<ELFT>(Name, Binding, Visibility, Type, CanOmitFromDynSym, &F); if (ObjSym.isCommon()) - return Symtab->addCommon(NameRef, ObjSym.getCommonSize(), + return Symtab->addCommon(Name, ObjSym.getCommonSize(), ObjSym.getCommonAlignment(), Binding, Visibility, STT_OBJECT, F); - return Symtab->addBitcode(NameRef, Binding, Visibility, Type, - CanOmitFromDynSym, F); + return Symtab->addBitcode(Name, Binding, Visibility, Type, CanOmitFromDynSym, + F); } template <class ELFT> @@ -1080,8 +1179,8 @@ static ELFKind getELFKind(MemoryBufferRef MB) { void BinaryFile::parse() { ArrayRef<uint8_t> Data = toArrayRef(MB.getBuffer()); - auto *Section = make<InputSection>(nullptr, SHF_ALLOC | SHF_WRITE, - SHT_PROGBITS, 8, Data, ".data"); + auto *Section = make<InputSection>(this, SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, + 8, Data, ".data"); Sections.push_back(Section); // For each input file foo that is embedded to a result as a binary @@ -1101,11 +1200,6 @@ void BinaryFile::parse() { Data.size(), 0, STB_GLOBAL, nullptr, nullptr); } -static bool isBitcode(MemoryBufferRef MB) { - using namespace sys::fs; - return identify_magic(MB.getBuffer()) == file_magic::bitcode; -} - InputFile *elf::createObjectFile(MemoryBufferRef MB, StringRef ArchiveName, uint64_t OffsetInArchive) { if (isBitcode(MB)) @@ -1141,9 +1235,9 @@ InputFile *elf::createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName) { } MemoryBufferRef LazyObjFile::getBuffer() { - if (Seen) + if (AddedToLink) return MemoryBufferRef(); - Seen = true; + AddedToLink = true; return MB; } @@ -1151,66 +1245,72 @@ InputFile *LazyObjFile::fetch() { MemoryBufferRef MBRef = getBuffer(); if (MBRef.getBuffer().empty()) return nullptr; - return createObjectFile(MBRef, ArchiveName, OffsetInArchive); + + InputFile *File = createObjectFile(MBRef, ArchiveName, OffsetInArchive); + File->GroupId = GroupId; + return File; } template <class ELFT> void LazyObjFile::parse() { - for (StringRef Sym : getSymbolNames()) - Symtab->addLazyObject<ELFT>(Sym, *this); + // A lazy object file wraps either a bitcode file or an ELF file. + if (isBitcode(this->MB)) { + std::unique_ptr<lto::InputFile> Obj = + CHECK(lto::InputFile::create(this->MB), this); + for (const lto::InputFile::Symbol &Sym : Obj->symbols()) + if (!Sym.isUndefined()) + Symtab->addLazyObject<ELFT>(Saver.save(Sym.getName()), *this); + return; + } + + switch (getELFKind(this->MB)) { + case ELF32LEKind: + addElfSymbols<ELF32LE>(); + return; + case ELF32BEKind: + addElfSymbols<ELF32BE>(); + return; + case ELF64LEKind: + addElfSymbols<ELF64LE>(); + return; + case ELF64BEKind: + addElfSymbols<ELF64BE>(); + return; + default: + llvm_unreachable("getELFKind"); + } } -template <class ELFT> std::vector<StringRef> LazyObjFile::getElfSymbols() { - typedef typename ELFT::Shdr Elf_Shdr; - typedef typename ELFT::Sym Elf_Sym; - typedef typename ELFT::SymRange Elf_Sym_Range; +template <class ELFT> void LazyObjFile::addElfSymbols() { + ELFFile<ELFT> Obj = check(ELFFile<ELFT>::create(MB.getBuffer())); + ArrayRef<typename ELFT::Shdr> Sections = CHECK(Obj.sections(), this); - ELFFile<ELFT> Obj = check(ELFFile<ELFT>::create(this->MB.getBuffer())); - ArrayRef<Elf_Shdr> Sections = CHECK(Obj.sections(), this); - for (const Elf_Shdr &Sec : Sections) { + for (const typename ELFT::Shdr &Sec : Sections) { if (Sec.sh_type != SHT_SYMTAB) continue; - Elf_Sym_Range Syms = CHECK(Obj.symbols(&Sec), this); - uint32_t FirstNonLocal = Sec.sh_info; + typename ELFT::SymRange Syms = CHECK(Obj.symbols(&Sec), this); + uint32_t FirstGlobal = Sec.sh_info; StringRef StringTable = CHECK(Obj.getStringTableForSymtab(Sec, Sections), this); - std::vector<StringRef> V; - for (const Elf_Sym &Sym : Syms.slice(FirstNonLocal)) + for (const typename ELFT::Sym &Sym : Syms.slice(FirstGlobal)) if (Sym.st_shndx != SHN_UNDEF) - V.push_back(CHECK(Sym.getName(StringTable), this)); - return V; + Symtab->addLazyObject<ELFT>(CHECK(Sym.getName(StringTable), this), + *this); + return; } - return {}; } -std::vector<StringRef> LazyObjFile::getBitcodeSymbols() { - std::unique_ptr<lto::InputFile> Obj = - CHECK(lto::InputFile::create(this->MB), this); - std::vector<StringRef> V; - for (const lto::InputFile::Symbol &Sym : Obj->symbols()) - if (!Sym.isUndefined()) - V.push_back(Saver.save(Sym.getName())); - return V; -} +std::string elf::replaceThinLTOSuffix(StringRef Path) { + StringRef Suffix = Config->ThinLTOObjectSuffixReplace.first; + StringRef Repl = Config->ThinLTOObjectSuffixReplace.second; -// Returns a vector of globally-visible defined symbol names. -std::vector<StringRef> LazyObjFile::getSymbolNames() { - if (isBitcode(this->MB)) - return getBitcodeSymbols(); - - switch (getELFKind(this->MB)) { - case ELF32LEKind: - return getElfSymbols<ELF32LE>(); - case ELF32BEKind: - return getElfSymbols<ELF32BE>(); - case ELF64LEKind: - return getElfSymbols<ELF64LE>(); - case ELF64BEKind: - return getElfSymbols<ELF64BE>(); - default: - llvm_unreachable("getELFKind"); + if (!Path.endswith(Suffix)) { + error("-thinlto-object-suffix-replace=" + Suffix + ";" + Repl + + " was given, but " + Path + " does not end with the suffix"); + return ""; } + return (Path.drop_back(Suffix.size()) + Repl).str(); } template void ArchiveFile::parse<ELF32LE>(); diff --git a/contrib/llvm/tools/lld/ELF/InputFiles.h b/contrib/llvm/tools/lld/ELF/InputFiles.h index 70109f002e98..0db3203b0ba2 100644 --- a/contrib/llvm/tools/lld/ELF/InputFiles.h +++ b/contrib/llvm/tools/lld/ELF/InputFiles.h @@ -12,22 +12,20 @@ #include "Config.h" #include "lld/Common/ErrorHandler.h" - #include "lld/Common/LLVM.h" #include "lld/Common/Reproduce.h" #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" #include "llvm/IR/Comdat.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ELF.h" #include "llvm/Object/IRObjectFile.h" #include "llvm/Support/Threading.h" - #include <map> namespace llvm { -class DWARFDebugLine; class TarWriter; struct DILineInfo; namespace lto { @@ -48,7 +46,6 @@ namespace elf { using llvm::object::Archive; -class Lazy; class Symbol; // If -reproduce option is given, all input files are written @@ -90,15 +87,15 @@ public: // Returns object file symbols. It is a runtime error to call this // function on files of other types. ArrayRef<Symbol *> getSymbols() { - assert(FileKind == ObjKind || FileKind == BitcodeKind || - FileKind == ArchiveKind); + assert(FileKind == BinaryKind || FileKind == ObjKind || + FileKind == BitcodeKind); return Symbols; } // Filename of .a which contained this file. If this file was // not in an archive file, it is the empty string. We use this // string for creating error messages. - StringRef ArchiveName; + std::string ArchiveName; // If this is an architecture-specific file, the following members // have ELF type (i.e. ELF{32,64}{LE,BE}) and target machine type. @@ -112,6 +109,20 @@ public: std::string getSrcMsg(const Symbol &Sym, InputSectionBase &Sec, uint64_t Offset); + // True if this is an argument for --just-symbols. Usually false. + bool JustSymbols = false; + + // GroupId is used for --warn-backrefs which is an optional error + // checking feature. All files within the same --{start,end}-group or + // --{start,end}-lib get the same group ID. Otherwise, each file gets a new + // group ID. For more info, see checkDependency() in SymbolTable.cpp. + uint32_t GroupId; + static bool IsInGroup; + static uint32_t NextGroupId; + + // Index of MIPS GOT built for this file. + llvm::Optional<size_t> MipsGotIndex; + protected: InputFile(Kind K, MemoryBufferRef M); std::vector<InputSectionBase *> Sections; @@ -144,7 +155,7 @@ public: protected: ArrayRef<Elf_Sym> ELFSyms; - uint32_t FirstNonLocal = 0; + uint32_t FirstGlobal = 0; ArrayRef<Elf_Word> SymtabSHNDX; StringRef StringTable; void initSymtab(ArrayRef<Elf_Shdr> Sections, const Elf_Shdr *Symtab); @@ -183,9 +194,6 @@ public: return getSymbol(SymIndex); } - // Returns source line information for a given offset. - // If no information is available, returns "". - std::string getLineInfo(InputSectionBase *S, uint64_t Offset); llvm::Optional<llvm::DILineInfo> getDILineInfo(InputSectionBase *, uint64_t); llvm::Optional<std::pair<std::string, unsigned>> getVariableLoc(StringRef Name); @@ -199,10 +207,22 @@ public: // symbol table. StringRef SourceFile; + // True if the file defines functions compiled with + // -fsplit-stack. Usually false. + bool SplitStack = false; + + // True if the file defines functions compiled with -fsplit-stack, + // but had one or more functions with the no_split_stack attribute. + bool SomeNoSplitStack = false; + + // Pointer to this input file's .llvm_addrsig section, if it has one. + const Elf_Shdr *AddrsigSec = nullptr; + private: void initializeSections(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups); void initializeSymbols(); + void initializeJustSymbols(); void initializeDwarf(); InputSectionBase *getRelocTarget(const Elf_Shdr &Sec); InputSectionBase *createInputSection(const Elf_Shdr &Sec); @@ -218,8 +238,14 @@ private: // reporting. Linker may find reasonable number of errors in a // single object file, so we cache debugging information in order to // parse it only once for each object file we link. - std::unique_ptr<llvm::DWARFDebugLine> DwarfLine; - llvm::DenseMap<StringRef, std::pair<unsigned, unsigned>> VariableLoc; + std::unique_ptr<llvm::DWARFContext> Dwarf; + std::vector<const llvm::DWARFDebugLine::LineTable *> LineTables; + struct VarLoc { + const llvm::DWARFDebugLine::LineTable *LT; + unsigned File; + unsigned Line; + }; + llvm::DenseMap<StringRef, VarLoc> VariableLoc; llvm::once_flag InitDwarfLine; }; @@ -243,13 +269,11 @@ public: template <class ELFT> void parse(); MemoryBufferRef getBuffer(); InputFile *fetch(); + bool AddedToLink = false; private: - std::vector<StringRef> getSymbolNames(); - template <class ELFT> std::vector<StringRef> getElfSymbols(); - std::vector<StringRef> getBitcodeSymbols(); + template <class ELFT> void addElfSymbols(); - bool Seen = false; uint64_t OffsetInArchive; }; @@ -260,11 +284,11 @@ public: static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; } template <class ELFT> void parse(); - // Returns a memory buffer for a given symbol and the offset in the archive - // for the member. An empty memory buffer and an offset of zero - // is returned if we have already returned the same memory buffer. - // (So that we don't instantiate same members more than once.) - std::pair<MemoryBufferRef, uint64_t> getMember(const Archive::Symbol *Sym); + // Pulls out an object file that contains a definition for Sym and + // returns it. If the same file was instantiated before, this + // function returns a nullptr (so we don't instantiate the same file + // more than once.) + InputFile *fetch(const Archive::Symbol &Sym); private: std::unique_ptr<Archive> File; @@ -291,7 +315,6 @@ template <class ELFT> class SharedFile : public ELFFileBase<ELFT> { typedef typename ELFT::Verdef Elf_Verdef; typedef typename ELFT::Versym Elf_Versym; - std::vector<StringRef> Undefs; const Elf_Shdr *VersymSec = nullptr; const Elf_Shdr *VerdefSec = nullptr; @@ -299,8 +322,6 @@ public: std::vector<const Elf_Verdef *> Verdefs; std::string SoName; - llvm::ArrayRef<StringRef> getUndefinedSymbols() { return Undefs; } - static bool classof(const InputFile *F) { return F->kind() == Base::SharedKind; } @@ -309,7 +330,9 @@ public: void parseSoName(); void parseRest(); - std::vector<const Elf_Verdef *> parseVerdefs(const Elf_Versym *&Versym); + uint32_t getAlignment(ArrayRef<Elf_Shdr> Sections, const Elf_Sym &Sym); + std::vector<const Elf_Verdef *> parseVerdefs(); + std::vector<uint32_t> parseVersyms(); struct NeededVer { // The string table offset of the version name in the output file. @@ -338,8 +361,15 @@ InputFile *createObjectFile(MemoryBufferRef MB, StringRef ArchiveName = "", uint64_t OffsetInArchive = 0); InputFile *createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName); +inline bool isBitcode(MemoryBufferRef MB) { + return identify_magic(MB.getBuffer()) == llvm::file_magic::bitcode; +} + +std::string replaceThinLTOSuffix(StringRef Path); + extern std::vector<BinaryFile *> BinaryFiles; extern std::vector<BitcodeFile *> BitcodeFiles; +extern std::vector<LazyObjFile *> LazyObjFiles; extern std::vector<InputFile *> ObjectFiles; extern std::vector<InputFile *> SharedFiles; diff --git a/contrib/llvm/tools/lld/ELF/InputSection.cpp b/contrib/llvm/tools/lld/ELF/InputSection.cpp index 0b8fe7ba6b59..54fb57cf9888 100644 --- a/contrib/llvm/tools/lld/ELF/InputSection.cpp +++ b/contrib/llvm/tools/lld/ELF/InputSection.cpp @@ -14,6 +14,7 @@ #include "LinkerScript.h" #include "OutputSections.h" #include "Relocations.h" +#include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" @@ -26,7 +27,10 @@ #include "llvm/Support/Endian.h" #include "llvm/Support/Threading.h" #include "llvm/Support/xxhash.h" +#include <algorithm> #include <mutex> +#include <set> +#include <vector> using namespace llvm; using namespace llvm::ELF; @@ -45,32 +49,6 @@ std::string lld::toString(const InputSectionBase *Sec) { return (toString(Sec->File) + ":(" + Sec->Name + ")").str(); } -DenseMap<SectionBase *, int> elf::buildSectionOrder() { - DenseMap<SectionBase *, int> SectionOrder; - if (Config->SymbolOrderingFile.empty()) - return SectionOrder; - - // Build a map from symbols to their priorities. Symbols that didn't - // appear in the symbol ordering file have the lowest priority 0. - // All explicitly mentioned symbols have negative (higher) priorities. - DenseMap<StringRef, int> SymbolOrder; - int Priority = -Config->SymbolOrderingFile.size(); - for (StringRef S : Config->SymbolOrderingFile) - SymbolOrder.insert({S, Priority++}); - - // Build a map from sections to their priorities. - for (InputFile *File : ObjectFiles) { - for (Symbol *Sym : File->getSymbols()) { - auto *D = dyn_cast<Defined>(Sym); - if (!D || !D->Section) - continue; - int &Priority = SectionOrder[D->Section]; - Priority = std::min(Priority, SymbolOrder.lookup(D->getName())); - } - } - return SectionOrder; -} - template <class ELFT> static ArrayRef<uint8_t> getSectionContents(ObjFile<ELFT> &File, const typename ELFT::Shdr &Hdr) { @@ -168,12 +146,8 @@ uint64_t SectionBase::getOffset(uint64_t Offset) const { return Offset == uint64_t(-1) ? OS->Size : Offset; } case Regular: - return cast<InputSection>(this)->OutSecOff + Offset; - case Synthetic: { - auto *IS = cast<InputSection>(this); - // For synthetic sections we treat offset -1 as the end of the section. - return IS->OutSecOff + (Offset == uint64_t(-1) ? IS->getSize() : Offset); - } + case Synthetic: + return cast<InputSection>(this)->getOffset(Offset); case EHFrame: // The file crtbeginT.o has relocations pointing to the start of an empty // .eh_frame that is known to be the first in the link. It does that to @@ -182,16 +156,21 @@ uint64_t SectionBase::getOffset(uint64_t Offset) const { case Merge: const MergeInputSection *MS = cast<MergeInputSection>(this); if (InputSection *IS = MS->getParent()) - return IS->OutSecOff + MS->getOffset(Offset); - return MS->getOffset(Offset); + return IS->getOffset(MS->getParentOffset(Offset)); + return MS->getParentOffset(Offset); } llvm_unreachable("invalid section kind"); } +uint64_t SectionBase::getVA(uint64_t Offset) const { + const OutputSection *Out = getOutputSection(); + return (Out ? Out->Addr : 0) + getOffset(Offset); +} + OutputSection *SectionBase::getOutputSection() { InputSection *Sec; if (auto *IS = dyn_cast<InputSection>(this)) - return IS->getParent(); + Sec = IS; else if (auto *MS = dyn_cast<MergeInputSection>(this)) Sec = MS->getParent(); else if (auto *EH = dyn_cast<EhInputSection>(this)) @@ -201,34 +180,50 @@ OutputSection *SectionBase::getOutputSection() { return Sec ? Sec->getParent() : nullptr; } -// Uncompress section contents if required. Note that this function +// Decompress section contents if required. Note that this function // is called from parallelForEach, so it must be thread-safe. -void InputSectionBase::maybeUncompress() { - if (UncompressBuf || !Decompressor::isCompressedELFSection(Flags, Name)) +void InputSectionBase::maybeDecompress() { + if (DecompressBuf) + return; + if (!(Flags & SHF_COMPRESSED) && !Name.startswith(".zdebug")) return; + // Decompress a section. Decompressor Dec = check(Decompressor::create(Name, toStringRef(Data), Config->IsLE, Config->Is64)); size_t Size = Dec.getDecompressedSize(); - UncompressBuf.reset(new char[Size]()); - if (Error E = Dec.decompress({UncompressBuf.get(), Size})) + DecompressBuf.reset(new char[Size + Name.size()]()); + if (Error E = Dec.decompress({DecompressBuf.get(), Size})) fatal(toString(this) + ": decompress failed: " + llvm::toString(std::move(E))); - Data = makeArrayRef((uint8_t *)UncompressBuf.get(), Size); + Data = makeArrayRef((uint8_t *)DecompressBuf.get(), Size); Flags &= ~(uint64_t)SHF_COMPRESSED; + + // A section name may have been altered if compressed. If that's + // the case, restore the original name. (i.e. ".zdebug_" -> ".debug_") + if (Name.startswith(".zdebug")) { + DecompressBuf[Size] = '.'; + memcpy(&DecompressBuf[Size + 1], Name.data() + 2, Name.size() - 2); + Name = StringRef(&DecompressBuf[Size], Name.size() - 1); + } } InputSection *InputSectionBase::getLinkOrderDep() const { - if ((Flags & SHF_LINK_ORDER) && Link != 0) { - InputSectionBase *L = File->getSections()[Link]; - if (auto *IS = dyn_cast<InputSection>(L)) - return IS; - error("a section with SHF_LINK_ORDER should not refer a non-regular " - "section: " + - toString(L)); - } + assert(Link); + assert(Flags & SHF_LINK_ORDER); + return cast<InputSection>(File->getSections()[Link]); +} + +// Find a function symbol that encloses a given location. +template <class ELFT> +Defined *InputSectionBase::getEnclosingFunction(uint64_t Offset) { + for (Symbol *B : File->getSymbols()) + if (Defined *D = dyn_cast<Defined>(B)) + if (D->Section == this && D->Type == STT_FUNC && D->Value <= Offset && + Offset < D->Value + D->Size) + return D; return nullptr; } @@ -241,9 +236,8 @@ std::string InputSectionBase::getLocation(uint64_t Offset) { .str(); // First check if we can get desired values from debugging information. - std::string LineInfo = getFile<ELFT>()->getLineInfo(this, Offset); - if (!LineInfo.empty()) - return LineInfo; + if (Optional<DILineInfo> Info = getFile<ELFT>()->getDILineInfo(this, Offset)) + return Info->FileName + ":" + std::to_string(Info->Line); // File->SourceFile contains STT_FILE symbol that contains a // source file name. If it's missing, we use an object file name. @@ -251,12 +245,8 @@ std::string InputSectionBase::getLocation(uint64_t Offset) { if (SrcFile.empty()) SrcFile = toString(File); - // Find a function symbol that encloses a given location. - for (Symbol *B : File->getSymbols()) - if (auto *D = dyn_cast<Defined>(B)) - if (D->Section == this && D->Type == STT_FUNC) - if (D->Value <= Offset && Offset < D->Value + D->Size) - return SrcFile + ":(function " + toString(*D) + ")"; + if (Defined *D = getEnclosingFunction<ELFT>(Offset)) + return SrcFile + ":(function " + toString(*D) + ")"; // If there's no symbol, print out the offset in the section. return (SrcFile + ":(" + Name + "+0x" + utohexstr(Offset) + ")").str(); @@ -292,7 +282,7 @@ std::string InputSectionBase::getObjMsg(uint64_t Off) { std::string Archive; if (!File->ArchiveName.empty()) - Archive = (" in archive " + File->ArchiveName).str(); + Archive = " in archive " + File->ArchiveName; // Find a symbol that encloses a given location. for (Symbol *B : File->getSymbols()) @@ -345,8 +335,9 @@ template <class ELFT> void InputSection::copyShtGroup(uint8_t *Buf) { *To++ = Sections[Idx]->getOutputSection()->SectionIndex; } -InputSectionBase *InputSection::getRelocatedSection() { - assert(Type == SHT_RELA || Type == SHT_REL); +InputSectionBase *InputSection::getRelocatedSection() const { + if (!File || (Type != SHT_RELA && Type != SHT_REL)) + return nullptr; ArrayRef<InputSectionBase *> Sections = File->getSections(); return Sections[Info]; } @@ -365,12 +356,12 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) { auto *P = reinterpret_cast<typename ELFT::Rela *>(Buf); Buf += sizeof(RelTy); - if (Config->IsRela) + if (RelTy::IsRela) P->r_addend = getAddend<ELFT>(Rel); // Output section VA is zero for -r, so r_offset is an offset within the // section, but for --emit-relocs it is an virtual address. - P->r_offset = Sec->getOutputSection()->Addr + Sec->getOffset(Rel.r_offset); + P->r_offset = Sec->getVA(Rel.r_offset); P->setSymbolAndType(InX::SymTab->getSymbolIndex(&Sym), Type, Config->IsMips64EL); @@ -395,17 +386,32 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) { continue; } - if (Config->IsRela) { - P->r_addend = - Sym.getVA(getAddend<ELFT>(Rel)) - Section->getOutputSection()->Addr; - } else if (Config->Relocatable) { - const uint8_t *BufLoc = Sec->Data.begin() + Rel.r_offset; - Sec->Relocations.push_back({R_ABS, Type, Rel.r_offset, - Target->getImplicitAddend(BufLoc, Type), - &Sym}); + int64_t Addend = getAddend<ELFT>(Rel); + const uint8_t *BufLoc = Sec->Data.begin() + Rel.r_offset; + if (!RelTy::IsRela) + Addend = Target->getImplicitAddend(BufLoc, Type); + + if (Config->EMachine == EM_MIPS && Config->Relocatable && + Target->getRelExpr(Type, Sym, BufLoc) == R_MIPS_GOTREL) { + // Some MIPS relocations depend on "gp" value. By default, + // this value has 0x7ff0 offset from a .got section. But + // relocatable files produced by a complier or a linker + // might redefine this default value and we must use it + // for a calculation of the relocation result. When we + // generate EXE or DSO it's trivial. Generating a relocatable + // output is more difficult case because the linker does + // not calculate relocations in this mode and loses + // individual "gp" values used by each input object file. + // As a workaround we add the "gp" value to the relocation + // addend and save it back to the file. + Addend += Sec->getFile<ELFT>()->MipsGp0; } - } + if (RelTy::IsRela) + P->r_addend = Sym.getVA(Addend) - Section->getOutputSection()->Addr; + else if (Config->Relocatable) + Sec->Relocations.push_back({R_ABS, Type, Rel.r_offset, Addend, &Sym}); + } } } @@ -481,14 +487,17 @@ static uint64_t getARMStaticBase(const Symbol &Sym) { return OS->PtLoad->FirstSec->Addr; } -static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P, - const Symbol &Sym, RelExpr Expr) { +static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A, + uint64_t P, const Symbol &Sym, RelExpr Expr) { switch (Expr) { case R_INVALID: return 0; case R_ABS: + case R_RELAX_TLS_LD_TO_LE_ABS: case R_RELAX_GOT_PC_NOPIC: return Sym.getVA(A); + case R_ADDEND: + return A; case R_ARM_SBREL: return Sym.getVA(A) - getARMStaticBase(Sym); case R_GOT: @@ -505,7 +514,9 @@ static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P, case R_GOT_FROM_END: case R_RELAX_TLS_GD_TO_IE_END: return Sym.getGotOffset() + A - InX::Got->getSize(); + case R_TLSLD_GOT_OFF: case R_GOT_OFF: + case R_RELAX_TLS_GD_TO_IE_GOT_OFF: return Sym.getGotOffset() + A; case R_GOT_PAGE_PC: case R_RELAX_TLS_GD_TO_IE_PAGE_PC: @@ -516,11 +527,12 @@ static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P, case R_HINT: case R_NONE: case R_TLSDESC_CALL: + case R_TLSLD_HINT: llvm_unreachable("cannot relocate hint relocs"); case R_MIPS_GOTREL: - return Sym.getVA(A) - InX::MipsGot->getGp(); + return Sym.getVA(A) - InX::MipsGot->getGp(File); case R_MIPS_GOT_GP: - return InX::MipsGot->getGp() + A; + return InX::MipsGot->getGp(File) + A; case R_MIPS_GOT_GP_PC: { // R_MIPS_LO16 expression has R_MIPS_GOT_GP_PC type iif the target // is _gp_disp symbol. In that case we should use the following @@ -529,7 +541,7 @@ static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P, // microMIPS variants of these relocations use slightly different // expressions: AHL + GP - P + 3 for %lo() and AHL + GP - P - 1 for %hi() // to correctly handle less-sugnificant bit of the microMIPS symbol. - uint64_t V = InX::MipsGot->getGp() + A - P; + uint64_t V = InX::MipsGot->getGp(File) + A - P; if (Type == R_MIPS_LO16 || Type == R_MICROMIPS_LO16) V += 4; if (Type == R_MICROMIPS_LO16 || Type == R_MICROMIPS_HI16) @@ -540,21 +552,23 @@ static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P, // If relocation against MIPS local symbol requires GOT entry, this entry // should be initialized by 'page address'. This address is high 16-bits // of sum the symbol's value and the addend. - return InX::MipsGot->getVA() + InX::MipsGot->getPageEntryOffset(Sym, A) - - InX::MipsGot->getGp(); + return InX::MipsGot->getVA() + + InX::MipsGot->getPageEntryOffset(File, Sym, A) - + InX::MipsGot->getGp(File); case R_MIPS_GOT_OFF: case R_MIPS_GOT_OFF32: // In case of MIPS if a GOT relocation has non-zero addend this addend // should be applied to the GOT entry content not to the GOT entry offset. // That is why we use separate expression type. - return InX::MipsGot->getVA() + InX::MipsGot->getSymEntryOffset(Sym, A) - - InX::MipsGot->getGp(); + return InX::MipsGot->getVA() + + InX::MipsGot->getSymEntryOffset(File, Sym, A) - + InX::MipsGot->getGp(File); case R_MIPS_TLSGD: - return InX::MipsGot->getVA() + InX::MipsGot->getTlsOffset() + - InX::MipsGot->getGlobalDynOffset(Sym) - InX::MipsGot->getGp(); + return InX::MipsGot->getVA() + InX::MipsGot->getGlobalDynOffset(File, Sym) - + InX::MipsGot->getGp(File); case R_MIPS_TLSLD: - return InX::MipsGot->getVA() + InX::MipsGot->getTlsOffset() + - InX::MipsGot->getTlsIndexOff() - InX::MipsGot->getGp(); + return InX::MipsGot->getVA() + InX::MipsGot->getTlsIndexOffset(File) - + InX::MipsGot->getGp(File); case R_PAGE_PC: case R_PLT_PAGE_PC: { uint64_t Dest; @@ -583,25 +597,27 @@ static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P, case R_PLT: return Sym.getPltVA() + A; case R_PLT_PC: - case R_PPC_PLT_OPD: + case R_PPC_CALL_PLT: return Sym.getPltVA() + A - P; - case R_PPC_OPD: { + case R_PPC_CALL: { uint64_t SymVA = Sym.getVA(A); // If we have an undefined weak symbol, we might get here with a symbol // address of zero. That could overflow, but the code must be unreachable, // so don't bother doing anything at all. if (!SymVA) return 0; - if (Out::Opd) { - // If this is a local call, and we currently have the address of a - // function-descriptor, get the underlying code address instead. - uint64_t OpdStart = Out::Opd->Addr; - uint64_t OpdEnd = OpdStart + Out::Opd->Size; - bool InOpd = OpdStart <= SymVA && SymVA < OpdEnd; - if (InOpd) - SymVA = read64be(&Out::OpdBuf[SymVA - OpdStart]); - } - return SymVA - P; + + // PPC64 V2 ABI describes two entry points to a function. The global entry + // point sets up the TOC base pointer. When calling a local function, the + // call should branch to the local entry point rather than the global entry + // point. Section 3.4.1 describes using the 3 most significant bits of the + // st_other field to find out how many instructions there are between the + // local and global entry point. + uint8_t StOther = (Sym.StOther >> 5) & 7; + if (StOther == 0 || StOther == 1) + return SymVA - P; + + return SymVA - P + (1LL << StOther); } case R_PPC_TOC: return getPPC64TocBase() + A; @@ -618,25 +634,44 @@ static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P, // statically to zero. if (Sym.isTls() && Sym.isUndefWeak()) return 0; - if (Target->TcbSize) + + // For TLS variant 1 the TCB is a fixed size, whereas for TLS variant 2 the + // TCB is on unspecified size and content. Targets that implement variant 1 + // should set TcbSize. + if (Target->TcbSize) { + // PPC64 V2 ABI has the thread pointer offset into the middle of the TLS + // storage area by TlsTpOffset for efficient addressing TCB and up to + // 4KB – 8 B of other thread library information (placed before the TCB). + // Subtracting this offset will get the address of the first TLS block. + if (Target->TlsTpOffset) + return Sym.getVA(A) - Target->TlsTpOffset; + + // If thread pointer is not offset into the middle, the first thing in the + // TLS storage area is the TCB. Add the TcbSize to get the address of the + // first TLS block. return Sym.getVA(A) + alignTo(Target->TcbSize, Out::TlsPhdr->p_align); + } return Sym.getVA(A) - Out::TlsPhdr->p_memsz; case R_RELAX_TLS_GD_TO_LE_NEG: case R_NEG_TLS: return Out::TlsPhdr->p_memsz - Sym.getVA(A); case R_SIZE: - return A; // Sym.getSize was already folded into the addend. + return Sym.getSize() + A; case R_TLSDESC: return InX::Got->getGlobalDynAddr(Sym) + A; case R_TLSDESC_PAGE: return getAArch64Page(InX::Got->getGlobalDynAddr(Sym) + A) - getAArch64Page(P); - case R_TLSGD: + case R_TLSGD_GOT: + return InX::Got->getGlobalDynOffset(Sym) + A; + case R_TLSGD_GOT_FROM_END: return InX::Got->getGlobalDynOffset(Sym) + A - InX::Got->getSize(); case R_TLSGD_PC: return InX::Got->getGlobalDynAddr(Sym) + A - P; - case R_TLSLD: + case R_TLSLD_GOT_FROM_END: return InX::Got->getTlsIndexOff() + A - InX::Got->getSize(); + case R_TLSLD_GOT: + return InX::Got->getTlsIndexOff() + A; case R_TLSLD_PC: return InX::Got->getTlsIndexVA() + A - P; } @@ -656,6 +691,14 @@ void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef<RelTy> Rels) { for (const RelTy &Rel : Rels) { RelType Type = Rel.getType(Config->IsMips64EL); + + // GCC 8.0 or earlier have a bug that they emit R_386_GOTPC relocations + // against _GLOBAL_OFFSET_TABLE_ for .debug_info. The bug has been fixed + // in 2017 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82630), but we + // need to keep this bug-compatible code for a while. + if (Config->EMachine == EM_386 && Type == R_386_GOTPC) + continue; + uint64_t Offset = getOffset(Rel.r_offset); uint8_t *BufLoc = Buf + Offset; int64_t Addend = getAddend<ELFT>(Rel); @@ -666,17 +709,27 @@ void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef<RelTy> Rels) { RelExpr Expr = Target->getRelExpr(Type, Sym, BufLoc); if (Expr == R_NONE) continue; + if (Expr != R_ABS) { - // GCC 8.0 or earlier have a bug that it emits R_386_GOTPC relocations - // against _GLOBAL_OFFSET_TABLE for .debug_info. The bug seems to have - // been fixed in 2017: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82630, - // but we need to keep this bug-compatible code for a while. - if (Config->EMachine == EM_386 && Type == R_386_GOTPC) - continue; + std::string Msg = getLocation<ELFT>(Offset) + + ": has non-ABS relocation " + toString(Type) + + " against symbol '" + toString(Sym) + "'"; + if (Expr != R_PC) { + error(Msg); + return; + } - error(getLocation<ELFT>(Offset) + ": has non-ABS relocation " + - toString(Type) + " against symbol '" + toString(Sym) + "'"); - return; + // If the control reaches here, we found a PC-relative relocation in a + // non-ALLOC section. Since non-ALLOC section is not loaded into memory + // at runtime, the notion of PC-relative doesn't make sense here. So, + // this is a usage error. However, GNU linkers historically accept such + // relocations without any errors and relocate them as if they were at + // address 0. For bug-compatibilty, we accept them with warnings. We + // know Steel Bank Common Lisp as of 2018 have this bug. + warn(Msg); + Target->relocateOne(BufLoc, Type, + SignExtend64<Bits>(Sym.getVA(Addend - Offset))); + continue; } if (Sym.isTls() && !Out::TlsPhdr) @@ -705,6 +758,9 @@ static void relocateNonAllocForRelocatable(InputSection *Sec, uint8_t *Buf) { template <class ELFT> void InputSectionBase::relocate(uint8_t *Buf, uint8_t *BufEnd) { + if (Flags & SHF_EXECINSTR) + adjustSplitStackFunctionPrologues<ELFT>(Buf, BufEnd); + if (Flags & SHF_ALLOC) { relocateAlloc(Buf, BufEnd); return; @@ -724,14 +780,17 @@ void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) { const unsigned Bits = Config->Wordsize * 8; for (const Relocation &Rel : Relocations) { - uint64_t Offset = getOffset(Rel.Offset); + uint64_t Offset = Rel.Offset; + if (auto *Sec = dyn_cast<InputSection>(this)) + Offset += Sec->OutSecOff; uint8_t *BufLoc = Buf + Offset; RelType Type = Rel.Type; uint64_t AddrLoc = getOutputSection()->Addr + Offset; RelExpr Expr = Rel.Expr; uint64_t TargetVA = SignExtend64( - getRelocTargetVA(Type, Rel.Addend, AddrLoc, *Rel.Sym, Expr), Bits); + getRelocTargetVA(File, Type, Rel.Addend, AddrLoc, *Rel.Sym, Expr), + Bits); switch (Expr) { case R_RELAX_GOT_PC: @@ -742,6 +801,7 @@ void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) { Target->relaxTlsIeToLe(BufLoc, Type, TargetVA); break; case R_RELAX_TLS_LD_TO_LE: + case R_RELAX_TLS_LD_TO_LE_ABS: Target->relaxTlsLdToLe(BufLoc, Type, TargetVA); break; case R_RELAX_TLS_GD_TO_LE: @@ -750,15 +810,28 @@ void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) { break; case R_RELAX_TLS_GD_TO_IE: case R_RELAX_TLS_GD_TO_IE_ABS: + case R_RELAX_TLS_GD_TO_IE_GOT_OFF: case R_RELAX_TLS_GD_TO_IE_PAGE_PC: case R_RELAX_TLS_GD_TO_IE_END: Target->relaxTlsGdToIe(BufLoc, Type, TargetVA); break; - case R_PPC_PLT_OPD: + case R_PPC_CALL: + // If this is a call to __tls_get_addr, it may be part of a TLS + // sequence that has been relaxed and turned into a nop. In this + // case, we don't want to handle it as a call. + if (read32(BufLoc) == 0x60000000) // nop + break; + // Patch a nop (0x60000000) to a ld. - if (BufLoc + 8 <= BufEnd && read32be(BufLoc + 4) == 0x60000000) - write32be(BufLoc + 4, 0xe8410028); // ld %r2, 40(%r1) - LLVM_FALLTHROUGH; + if (Rel.Sym->NeedsTocRestore) { + if (BufLoc + 8 > BufEnd || read32(BufLoc + 4) != 0x60000000) { + error(getErrorLocation(BufLoc) + "call lacks nop, can't restore toc"); + break; + } + write32(BufLoc + 4, 0xe8410018); // ld %r2, 24(%r1) + } + Target->relocateOne(BufLoc, Type, TargetVA); + break; default: Target->relocateOne(BufLoc, Type, TargetVA); break; @@ -766,6 +839,101 @@ void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) { } } +// For each function-defining prologue, find any calls to __morestack, +// and replace them with calls to __morestack_non_split. +static void switchMorestackCallsToMorestackNonSplit( + DenseSet<Defined *> &Prologues, std::vector<Relocation *> &MorestackCalls) { + + // If the target adjusted a function's prologue, all calls to + // __morestack inside that function should be switched to + // __morestack_non_split. + Symbol *MoreStackNonSplit = Symtab->find("__morestack_non_split"); + + // Sort both collections to compare addresses efficiently. + llvm::sort(MorestackCalls.begin(), MorestackCalls.end(), + [](const Relocation *L, const Relocation *R) { + return L->Offset < R->Offset; + }); + std::vector<Defined *> Functions(Prologues.begin(), Prologues.end()); + llvm::sort( + Functions.begin(), Functions.end(), + [](const Defined *L, const Defined *R) { return L->Value < R->Value; }); + + auto It = MorestackCalls.begin(); + for (Defined *F : Functions) { + // Find the first call to __morestack within the function. + while (It != MorestackCalls.end() && (*It)->Offset < F->Value) + ++It; + // Adjust all calls inside the function. + while (It != MorestackCalls.end() && (*It)->Offset < F->Value + F->Size) { + (*It)->Sym = MoreStackNonSplit; + ++It; + } + } +} + +static bool enclosingPrologueAdjusted(uint64_t Offset, + const DenseSet<Defined *> &Prologues) { + for (Defined *F : Prologues) + if (F->Value <= Offset && Offset < F->Value + F->Size) + return true; + return false; +} + +// If a function compiled for split stack calls a function not +// compiled for split stack, then the caller needs its prologue +// adjusted to ensure that the called function will have enough stack +// available. Find those functions, and adjust their prologues. +template <class ELFT> +void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *Buf, + uint8_t *End) { + if (!getFile<ELFT>()->SplitStack) + return; + DenseSet<Defined *> AdjustedPrologues; + std::vector<Relocation *> MorestackCalls; + + for (Relocation &Rel : Relocations) { + // Local symbols can't possibly be cross-calls, and should have been + // resolved long before this line. + if (Rel.Sym->isLocal()) + continue; + + Defined *D = dyn_cast<Defined>(Rel.Sym); + // A reference to an undefined symbol was an error, and should not + // have gotten to this point. + if (!D) + continue; + + // Ignore calls into the split-stack api. + if (D->getName().startswith("__morestack")) { + if (D->getName().equals("__morestack")) + MorestackCalls.push_back(&Rel); + continue; + } + + // A relocation to non-function isn't relevant. Sometimes + // __morestack is not marked as a function, so this check comes + // after the name check. + if (D->Type != STT_FUNC) + continue; + + if (enclosingPrologueAdjusted(Rel.Offset, AdjustedPrologues)) + continue; + + if (Defined *F = getEnclosingFunction<ELFT>(Rel.Offset)) { + if (Target->adjustPrologueForCrossSplitStack(Buf + F->Value, End)) { + AdjustedPrologues.insert(F); + continue; + } + } + if (!getFile<ELFT>()->SomeNoSplitStack) + error("function call at " + getErrorLocation(Buf + Rel.Offset) + + "crosses a split-stack boundary, but unable " + + "to adjust the enclosing function's prologue"); + } + switchMorestackCallsToMorestackNonSplit(AdjustedPrologues, MorestackCalls); +} + template <class ELFT> void InputSection::writeTo(uint8_t *Buf) { if (Type == SHT_NOBITS) return; @@ -837,10 +1005,6 @@ static unsigned getReloc(IntTy Begin, IntTy Size, const ArrayRef<RelTy> &Rels, // .eh_frame is a sequence of CIE or FDE records. // This function splits an input section into records and returns them. template <class ELFT> void EhInputSection::split() { - // Early exit if already split. - if (!Pieces.empty()) - return; - if (AreRelocsRela) split<ELFT>(relas<ELFT>()); else @@ -905,8 +1069,7 @@ void MergeInputSection::splitNonStrings(ArrayRef<uint8_t> Data, bool IsAlloc = Flags & SHF_ALLOC; for (size_t I = 0; I != Size; I += EntSize) - Pieces.emplace_back(I, xxHash64(toStringRef(Data.slice(I, EntSize))), - !IsAlloc); + Pieces.emplace_back(I, xxHash64(Data.slice(I, EntSize)), !IsAlloc); } template <class ELFT> @@ -935,15 +1098,9 @@ void MergeInputSection::splitIntoPieces() { else splitNonStrings(Data, Entsize); - if (Config->GcSections && (Flags & SHF_ALLOC)) - for (uint64_t Off : LiveOffsets) - getSectionPiece(Off)->Live = true; -} - -// Do binary search to get a section piece at a given input offset. -SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) { - auto *This = static_cast<const MergeInputSection *>(this); - return const_cast<SectionPiece *>(This->getSectionPiece(Offset)); + OffsetMap.reserve(Pieces.size()); + for (size_t I = 0, E = Pieces.size(); I != E; ++I) + OffsetMap[Pieces[I].InputOff] = I; } template <class It, class T, class Compare> @@ -959,32 +1116,34 @@ static It fastUpperBound(It First, It Last, const T &Value, Compare Comp) { return Comp(Value, *First) ? First : First + 1; } -const SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) const { - if (Data.size() <= Offset) - fatal(toString(this) + ": entry is past the end of the section"); +// Do binary search to get a section piece at a given input offset. +static SectionPiece *findSectionPiece(MergeInputSection *Sec, uint64_t Offset) { + if (Sec->Data.size() <= Offset) + fatal(toString(Sec) + ": entry is past the end of the section"); // Find the element this offset points to. auto I = fastUpperBound( - Pieces.begin(), Pieces.end(), Offset, + Sec->Pieces.begin(), Sec->Pieces.end(), Offset, [](const uint64_t &A, const SectionPiece &B) { return A < B.InputOff; }); --I; return &*I; } +SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) { + // Find a piece starting at a given offset. + auto It = OffsetMap.find(Offset); + if (It != OffsetMap.end()) + return &Pieces[It->second]; + + // If Offset is not at beginning of a section piece, it is not in the map. + // In that case we need to search from the original section piece vector. + return findSectionPiece(this, Offset); +} + // Returns the offset in an output section for a given input offset. // Because contents of a mergeable section is not contiguous in output, // it is not just an addition to a base output offset. -uint64_t MergeInputSection::getOffset(uint64_t Offset) const { - if (!Live) - return 0; - - // Initialize OffsetMap lazily. - llvm::call_once(InitOffsetMap, [&] { - OffsetMap.reserve(Pieces.size()); - for (size_t I = 0; I < Pieces.size(); ++I) - OffsetMap[Pieces[I].InputOff] = I; - }); - +uint64_t MergeInputSection::getParentOffset(uint64_t Offset) const { // Find a string starting at a given offset. auto It = OffsetMap.find(Offset); if (It != OffsetMap.end()) @@ -992,10 +1151,8 @@ uint64_t MergeInputSection::getOffset(uint64_t Offset) const { // If Offset is not at beginning of a section piece, it is not in the map. // In that case we need to search from the original section piece vector. - const SectionPiece &Piece = *getSectionPiece(Offset); - if (!Piece.Live) - return 0; - + const SectionPiece &Piece = + *findSectionPiece(const_cast<MergeInputSection *>(this), Offset); uint64_t Addend = Offset - Piece.InputOff; return Piece.OutputOff + Addend; } diff --git a/contrib/llvm/tools/lld/ELF/InputSection.h b/contrib/llvm/tools/lld/ELF/InputSection.h index 8c114ae71948..4db01e035e32 100644 --- a/contrib/llvm/tools/lld/ELF/InputSection.h +++ b/contrib/llvm/tools/lld/ELF/InputSection.h @@ -18,8 +18,6 @@ #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Object/ELF.h" -#include "llvm/Support/Threading.h" -#include <mutex> namespace lld { namespace elf { @@ -63,6 +61,9 @@ public: unsigned Bss : 1; + // Set for sections that should not be folded by ICF. + unsigned KeepUnique : 1; + // These corresponds to the fields in Elf_Shdr. uint32_t Alignment; uint64_t Flags; @@ -80,13 +81,15 @@ public: // section. uint64_t getOffset(uint64_t Offset) const; + uint64_t getVA(uint64_t Offset = 0) const; + protected: SectionBase(Kind SectionKind, StringRef Name, uint64_t Flags, uint64_t Entsize, uint64_t Alignment, uint32_t Type, uint32_t Info, uint32_t Link) : Name(Name), Repl(this), SectionKind(SectionKind), Live(false), - Bss(false), Alignment(Alignment), Flags(Flags), Entsize(Entsize), - Type(Type), Link(Link), Info(Info) {} + Bss(false), KeepUnique(false), Alignment(Alignment), Flags(Flags), + Entsize(Entsize), Type(Type), Link(Link), Info(Info) {} }; // This corresponds to a section of an input file. @@ -103,7 +106,7 @@ public: static bool classof(const SectionBase *S) { return S->kind() != Output; } - // The file which contains this section. It's dynamic type is always + // The file which contains this section. Its dynamic type is always // ObjFile<ELFT>, but in order to avoid ELFT, we use InputFile as // its static type. InputFile *File; @@ -161,10 +164,15 @@ public: InputSection *getLinkOrderDep() const; + // Get the function symbol that encloses this offset from within the + // section. + template <class ELFT> + Defined *getEnclosingFunction(uint64_t Offset); + // Compilers emit zlib-compressed debug sections if the -gz option // is given. This function checks if this section is compressed, and // if so, decompress in memory. - void maybeUncompress(); + void maybeDecompress(); // Returns a source location string. Used to construct an error message. template <class ELFT> std::string getLocation(uint64_t Offset); @@ -182,6 +190,15 @@ public: // This vector contains such "cooked" relocations. std::vector<Relocation> Relocations; + // A function compiled with -fsplit-stack calling a function + // compiled without -fsplit-stack needs its prologue adjusted. Find + // such functions and adjust their prologues. This is very similar + // to relocation. See https://gcc.gnu.org/wiki/SplitStacks for more + // information. + template <typename ELFT> + void adjustSplitStackFunctionPrologues(uint8_t *Buf, uint8_t *End); + + template <typename T> llvm::ArrayRef<T> getDataAs() const { size_t S = Data.size(); assert(S % sizeof(T) == 0); @@ -189,9 +206,9 @@ public: } private: - // A pointer that owns uncompressed data if a section is compressed by zlib. + // A pointer that owns decompressed data if a section is compressed by zlib. // Since the feature is not used often, this is usually a nullptr. - std::unique_ptr<char[]> UncompressBuf; + std::unique_ptr<char[]> DecompressBuf; }; // SectionPiece represents a piece of splittable section contents. @@ -200,7 +217,7 @@ private: // be found by looking at the next one). struct SectionPiece { SectionPiece(size_t Off, uint32_t Hash, bool Live) - : InputOff(Off), Hash(Hash), OutputOff(-1), + : InputOff(Off), Hash(Hash), OutputOff(0), Live(Live || !Config->GcSections) {} uint32_t InputOff; @@ -223,19 +240,14 @@ public: static bool classof(const SectionBase *S) { return S->kind() == Merge; } void splitIntoPieces(); - // Mark the piece at a given offset live. Used by GC. - void markLiveAt(uint64_t Offset) { - if (this->Flags & llvm::ELF::SHF_ALLOC) - LiveOffsets.insert(Offset); - } - - // Translate an offset in the input section to an offset - // in the output section. - uint64_t getOffset(uint64_t Offset) const; + // Translate an offset in the input section to an offset in the parent + // MergeSyntheticSection. + uint64_t getParentOffset(uint64_t Offset) const; // Splittable sections are handled as a sequence of data // rather than a single large blob of data. std::vector<SectionPiece> Pieces; + llvm::DenseMap<uint32_t, uint32_t> OffsetMap; // Returns I'th piece's data. This function is very hot when // string merging is enabled, so we want to inline. @@ -249,18 +261,15 @@ public: // Returns the SectionPiece at a given input section offset. SectionPiece *getSectionPiece(uint64_t Offset); - const SectionPiece *getSectionPiece(uint64_t Offset) const; + const SectionPiece *getSectionPiece(uint64_t Offset) const { + return const_cast<MergeInputSection *>(this)->getSectionPiece(Offset); + } SyntheticSection *getParent() const; private: void splitStrings(ArrayRef<uint8_t> A, size_t Size); void splitNonStrings(ArrayRef<uint8_t> A, size_t Size); - - mutable llvm::DenseMap<uint32_t, uint32_t> OffsetMap; - mutable llvm::once_flag InitOffsetMap; - - llvm::DenseSet<uint64_t> LiveOffsets; }; struct EhSectionPiece { @@ -310,6 +319,8 @@ public: // beginning of the output section. template <class ELFT> void writeTo(uint8_t *Buf); + uint64_t getOffset(uint64_t Offset) const { return OutSecOff + Offset; } + OutputSection *getParent() const; // This variable has two usages. Initially, it represents an index in the @@ -320,7 +331,7 @@ public: static bool classof(const SectionBase *S); - InputSectionBase *getRelocatedSection(); + InputSectionBase *getRelocatedSection() const; template <class ELFT, class RelTy> void relocateNonAlloc(uint8_t *Buf, llvm::ArrayRef<RelTy> Rels); @@ -342,10 +353,6 @@ private: // The list of all input sections. extern std::vector<InputSectionBase *> InputSections; - -// Builds section order for handling --symbol-ordering-file. -llvm::DenseMap<SectionBase *, int> buildSectionOrder(); - } // namespace elf std::string toString(const elf::InputSectionBase *); diff --git a/contrib/llvm/tools/lld/ELF/LTO.cpp b/contrib/llvm/tools/lld/ELF/LTO.cpp index bfd85288d186..ef58932e86cc 100644 --- a/contrib/llvm/tools/lld/ELF/LTO.cpp +++ b/contrib/llvm/tools/lld/ELF/LTO.cpp @@ -20,6 +20,8 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/Bitcode/BitcodeReader.h" +#include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/LTO/Caching.h" #include "llvm/LTO/Config.h" @@ -29,7 +31,6 @@ #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cstddef> #include <memory> @@ -44,70 +45,92 @@ using namespace llvm::ELF; using namespace lld; using namespace lld::elf; -// This is for use when debugging LTO. -static void saveBuffer(StringRef Buffer, const Twine &Path) { +// Creates an empty file to store a list of object files for final +// linking of distributed ThinLTO. +static std::unique_ptr<raw_fd_ostream> openFile(StringRef File) { std::error_code EC; - raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None); - if (EC) - error("cannot create " + Path + ": " + EC.message()); - OS << Buffer; -} - -static void diagnosticHandler(const DiagnosticInfo &DI) { - SmallString<128> ErrStorage; - raw_svector_ostream OS(ErrStorage); - DiagnosticPrinterRawOStream DP(OS); - DI.print(DP); - warn(ErrStorage); + auto Ret = + llvm::make_unique<raw_fd_ostream>(File, EC, sys::fs::OpenFlags::F_None); + if (EC) { + error("cannot open " + File + ": " + EC.message()); + return nullptr; + } + return Ret; } -static void checkError(Error E) { - handleAllErrors(std::move(E), - [&](ErrorInfoBase &EIB) { error(EIB.message()); }); +static std::string getThinLTOOutputFile(StringRef ModulePath) { + return lto::getThinLTOOutputFile(ModulePath, + Config->ThinLTOPrefixReplace.first, + Config->ThinLTOPrefixReplace.second); } -static std::unique_ptr<lto::LTO> createLTO() { - lto::Config Conf; +static lto::Config createConfig() { + lto::Config C; // LLD supports the new relocations. - Conf.Options = InitTargetOptionsFromCodeGenFlags(); - Conf.Options.RelaxELFRelocations = true; + C.Options = InitTargetOptionsFromCodeGenFlags(); + C.Options.RelaxELFRelocations = true; // Always emit a section per function/datum with LTO. - Conf.Options.FunctionSections = true; - Conf.Options.DataSections = true; + C.Options.FunctionSections = true; + C.Options.DataSections = true; if (Config->Relocatable) - Conf.RelocModel = None; + C.RelocModel = None; else if (Config->Pic) - Conf.RelocModel = Reloc::PIC_; + C.RelocModel = Reloc::PIC_; else - Conf.RelocModel = Reloc::Static; - Conf.CodeModel = GetCodeModelFromCMModel(); - Conf.DisableVerify = Config->DisableVerify; - Conf.DiagHandler = diagnosticHandler; - Conf.OptLevel = Config->LTOO; + C.RelocModel = Reloc::Static; + + C.CodeModel = GetCodeModelFromCMModel(); + C.DisableVerify = Config->DisableVerify; + C.DiagHandler = diagnosticHandler; + C.OptLevel = Config->LTOO; + C.CPU = GetCPUStr(); // Set up a custom pipeline if we've been asked to. - Conf.OptPipeline = Config->LTONewPmPasses; - Conf.AAPipeline = Config->LTOAAPipeline; + C.OptPipeline = Config->LTONewPmPasses; + C.AAPipeline = Config->LTOAAPipeline; // Set up optimization remarks if we've been asked to. - Conf.RemarksFilename = Config->OptRemarksFilename; - Conf.RemarksWithHotness = Config->OptRemarksWithHotness; + C.RemarksFilename = Config->OptRemarksFilename; + C.RemarksWithHotness = Config->OptRemarksWithHotness; + + C.SampleProfile = Config->LTOSampleProfile; + C.UseNewPM = Config->LTONewPassManager; + C.DebugPassManager = Config->LTODebugPassManager; + C.DwoDir = Config->DwoDir; if (Config->SaveTemps) - checkError(Conf.addSaveTemps(std::string(Config->OutputFile) + ".", - /*UseInputModulePath*/ true)); + checkError(C.addSaveTemps(Config->OutputFile.str() + ".", + /*UseInputModulePath*/ true)); + return C; +} +BitcodeCompiler::BitcodeCompiler() { + // Initialize LTOObj. lto::ThinBackend Backend; - if (Config->ThinLTOJobs != -1u) + + if (Config->ThinLTOIndexOnly) { + StringRef Path = Config->ThinLTOIndexOnlyArg; + if (!Path.empty()) + IndexFile = openFile(Path); + + auto OnIndexWrite = [&](const std::string &Identifier) { + ObjectToIndexFileState[Identifier] = true; + }; + + Backend = lto::createWriteIndexesThinBackend( + Config->ThinLTOPrefixReplace.first, Config->ThinLTOPrefixReplace.second, + Config->ThinLTOEmitImportsFiles, IndexFile.get(), OnIndexWrite); + } else if (Config->ThinLTOJobs != -1U) { Backend = lto::createInProcessThinBackend(Config->ThinLTOJobs); - return llvm::make_unique<lto::LTO>(std::move(Conf), Backend, - Config->LTOPartitions); -} + } -BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) { + LTOObj = llvm::make_unique<lto::LTO>(createConfig(), Backend, + Config->LTOPartitions); + + // Initialize UsedStartStop. for (Symbol *Sym : Symtab->getSymbols()) { StringRef Name = Sym->getName(); for (StringRef Prefix : {"__start_", "__stop_"}) @@ -125,20 +148,20 @@ static void undefine(Symbol *S) { void BitcodeCompiler::add(BitcodeFile &F) { lto::InputFile &Obj = *F.Obj; - unsigned SymNum = 0; - std::vector<Symbol *> Syms = F.getSymbols(); - std::vector<lto::SymbolResolution> Resols(Syms.size()); + bool IsExec = !Config->Shared && !Config->Relocatable; - DenseSet<StringRef> ScriptSymbols; - for (BaseCommand *Base : Script->SectionCommands) - if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) - ScriptSymbols.insert(Cmd->Name); + if (Config->ThinLTOIndexOnly) + ObjectToIndexFileState.insert({Obj.getName(), false}); + + ArrayRef<Symbol *> Syms = F.getSymbols(); + ArrayRef<lto::InputFile::Symbol> ObjSyms = Obj.symbols(); + std::vector<lto::SymbolResolution> Resols(Syms.size()); // Provide a resolution to the LTO API for each symbol. - for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) { - Symbol *Sym = Syms[SymNum]; - lto::SymbolResolution &R = Resols[SymNum]; - ++SymNum; + for (size_t I = 0, E = Syms.size(); I != E; ++I) { + Symbol *Sym = Syms[I]; + const lto::InputFile::Symbol &ObjSym = ObjSyms[I]; + lto::SymbolResolution &R = Resols[I]; // Ideally we shouldn't check for SF_Undefined but currently IRObjectFile // reports two symbols for module ASM defined. Without this check, lld @@ -156,25 +179,46 @@ void BitcodeCompiler::add(BitcodeFile &F) { R.VisibleToRegularObj = Config->Relocatable || Sym->IsUsedInRegularObj || (R.Prevailing && Sym->includeInDynsym()) || UsedStartStop.count(ObjSym.getSectionName()); + const auto *DR = dyn_cast<Defined>(Sym); + R.FinalDefinitionInLinkageUnit = + (IsExec || Sym->Visibility != STV_DEFAULT) && DR && + // Skip absolute symbols from ELF objects, otherwise PC-rel relocations + // will be generated by for them, triggering linker errors. + // Symbol section is always null for bitcode symbols, hence the check + // for isElf(). Skip linker script defined symbols as well: they have + // no File defined. + !(DR->Section == nullptr && (!Sym->File || Sym->File->isElf())); + if (R.Prevailing) undefine(Sym); - // We tell LTO to not apply interprocedural optimization for following - // symbols because otherwise LTO would inline them while their values are - // still not final: - // 1) Aliased (with --defsym) or wrapped (with --wrap) symbols. - // 2) Symbols redefined in linker script. - R.LinkerRedefined = !Sym->CanInline || ScriptSymbols.count(Sym->getName()); + // We tell LTO to not apply interprocedural optimization for wrapped + // (with --wrap) symbols because otherwise LTO would inline them while + // their values are still not final. + R.LinkerRedefined = !Sym->CanInline; } checkError(LTOObj->add(std::move(F.Obj), Resols)); } +static void createEmptyIndex(StringRef ModulePath) { + std::string Path = replaceThinLTOSuffix(getThinLTOOutputFile(ModulePath)); + std::unique_ptr<raw_fd_ostream> OS = openFile(Path + ".thinlto.bc"); + if (!OS) + return; + + ModuleSummaryIndex M(/*HaveGVs*/ false); + M.setSkipModuleByDistributedBackend(); + WriteIndexToFile(M, *OS); + + if (Config->ThinLTOEmitImportsFiles) + openFile(Path + ".imports"); +} + // Merge all the bitcode files we have seen, codegen the result // and return the resulting ObjectFile(s). std::vector<InputFile *> BitcodeCompiler::compile() { - std::vector<InputFile *> Ret; unsigned MaxTasks = LTOObj->getMaxTasks(); - Buff.resize(MaxTasks); + Buf.resize(MaxTasks); Files.resize(MaxTasks); // The --thinlto-cache-dir option specifies the path to a directory in which @@ -184,35 +228,67 @@ std::vector<InputFile *> BitcodeCompiler::compile() { if (!Config->ThinLTOCacheDir.empty()) Cache = check( lto::localCache(Config->ThinLTOCacheDir, - [&](size_t Task, std::unique_ptr<MemoryBuffer> MB, - StringRef Path) { Files[Task] = std::move(MB); })); + [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) { + Files[Task] = std::move(MB); + })); checkError(LTOObj->run( [&](size_t Task) { return llvm::make_unique<lto::NativeObjectStream>( - llvm::make_unique<raw_svector_ostream>(Buff[Task])); + llvm::make_unique<raw_svector_ostream>(Buf[Task])); }, Cache)); + // Emit empty index files for non-indexed files + if (Config->ThinLTOIndexOnly) { + for (auto &Identifier : ObjectToIndexFileState) + if (!Identifier.getValue()) { + std::string Path = getThinLTOOutputFile(Identifier.getKey()); + openFile(Path + ".thinlto.bc"); + + if (Config->ThinLTOEmitImportsFiles) + openFile(Path + ".imports"); + } + } + + // If LazyObjFile has not been added to link, emit empty index files. + // This is needed because this is what GNU gold plugin does and we have a + // distributed build system that depends on that behavior. + if (Config->ThinLTOIndexOnly) { + for (LazyObjFile *F : LazyObjFiles) + if (!F->AddedToLink && isBitcode(F->MB)) + createEmptyIndex(F->getName()); + + if (!Config->LTOObjPath.empty()) + saveBuffer(Buf[0], Config->LTOObjPath); + + // ThinLTO with index only option is required to generate only the index + // files. After that, we exit from linker and ThinLTO backend runs in a + // distributed environment. + if (IndexFile) + IndexFile->close(); + return {}; + } + if (!Config->ThinLTOCacheDir.empty()) pruneCache(Config->ThinLTOCacheDir, Config->ThinLTOCachePolicy); + std::vector<InputFile *> Ret; for (unsigned I = 0; I != MaxTasks; ++I) { - if (Buff[I].empty()) + if (Buf[I].empty()) continue; if (Config->SaveTemps) { if (I == 0) - saveBuffer(Buff[I], Config->OutputFile + ".lto.o"); + saveBuffer(Buf[I], Config->OutputFile + ".lto.o"); else - saveBuffer(Buff[I], Config->OutputFile + Twine(I) + ".lto.o"); + saveBuffer(Buf[I], Config->OutputFile + Twine(I) + ".lto.o"); } - InputFile *Obj = createObjectFile(MemoryBufferRef(Buff[I], "lto.tmp")); + InputFile *Obj = createObjectFile(MemoryBufferRef(Buf[I], "lto.tmp")); Ret.push_back(Obj); } for (std::unique_ptr<MemoryBuffer> &File : Files) if (File) Ret.push_back(createObjectFile(*File)); - return Ret; } diff --git a/contrib/llvm/tools/lld/ELF/LTO.h b/contrib/llvm/tools/lld/ELF/LTO.h index 223af507a97d..8803078eb1df 100644 --- a/contrib/llvm/tools/lld/ELF/LTO.h +++ b/contrib/llvm/tools/lld/ELF/LTO.h @@ -24,6 +24,7 @@ #include "lld/Common/LLVM.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" #include <memory> #include <vector> @@ -38,6 +39,7 @@ namespace elf { class BitcodeFile; class InputFile; +class LazyObjFile; class BitcodeCompiler { public: @@ -49,9 +51,11 @@ public: private: std::unique_ptr<llvm::lto::LTO> LTOObj; - std::vector<SmallString<0>> Buff; + std::vector<SmallString<0>> Buf; std::vector<std::unique_ptr<MemoryBuffer>> Files; llvm::DenseSet<StringRef> UsedStartStop; + std::unique_ptr<llvm::raw_fd_ostream> IndexFile; + llvm::StringMap<bool> ObjectToIndexFileState; }; } // namespace elf } // namespace lld diff --git a/contrib/llvm/tools/lld/ELF/LinkerScript.cpp b/contrib/llvm/tools/lld/ELF/LinkerScript.cpp index 23c3a11a9d1d..abdd899da487 100644 --- a/contrib/llvm/tools/lld/ELF/LinkerScript.cpp +++ b/contrib/llvm/tools/lld/ELF/LinkerScript.cpp @@ -15,13 +15,13 @@ #include "Config.h" #include "InputSection.h" #include "OutputSections.h" -#include "Strings.h" #include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "Writer.h" #include "lld/Common/Memory.h" +#include "lld/Common/Strings.h" #include "lld/Common/Threads.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" @@ -74,7 +74,7 @@ uint64_t ExprValue::getSectionOffset() const { // If the alignment is trivial, we don't have to compute the full // value to know the offset. This allows this function to succeed in // cases where the output section is not yet known. - if (Alignment == 1) + if (Alignment == 1 && (!Sec || !Sec->getOutputSection())) return Val; return getValue() - getSecAddr(); } @@ -102,28 +102,67 @@ OutputSection *LinkerScript::getOrCreateOutputSection(StringRef Name) { return CmdRef; } +// Expands the memory region by the specified size. +static void expandMemoryRegion(MemoryRegion *MemRegion, uint64_t Size, + StringRef RegionName, StringRef SecName) { + MemRegion->CurPos += Size; + uint64_t NewSize = MemRegion->CurPos - MemRegion->Origin; + if (NewSize > MemRegion->Length) + error("section '" + SecName + "' will not fit in region '" + RegionName + + "': overflowed by " + Twine(NewSize - MemRegion->Length) + " bytes"); +} + +void LinkerScript::expandMemoryRegions(uint64_t Size) { + if (Ctx->MemRegion) + expandMemoryRegion(Ctx->MemRegion, Size, Ctx->MemRegion->Name, + Ctx->OutSec->Name); + if (Ctx->LMARegion) + expandMemoryRegion(Ctx->LMARegion, Size, Ctx->LMARegion->Name, + Ctx->OutSec->Name); +} + +void LinkerScript::expandOutputSection(uint64_t Size) { + Ctx->OutSec->Size += Size; + expandMemoryRegions(Size); +} + void LinkerScript::setDot(Expr E, const Twine &Loc, bool InSec) { uint64_t Val = E().getValue(); if (Val < Dot && InSec) error(Loc + ": unable to move location counter backward for: " + Ctx->OutSec->Name); - Dot = Val; // Update to location counter means update to section size. if (InSec) - Ctx->OutSec->Size = Dot - Ctx->OutSec->Addr; + expandOutputSection(Val - Dot); + else + expandMemoryRegions(Val - Dot); + + Dot = Val; } -// This function is called from processSectionCommands, -// while we are fixing the output section layout. -void LinkerScript::addSymbol(SymbolAssignment *Cmd) { +// Used for handling linker symbol assignments, for both finalizing +// their values and doing early declarations. Returns true if symbol +// should be defined from linker script. +static bool shouldDefineSym(SymbolAssignment *Cmd) { if (Cmd->Name == ".") - return; + return false; + + if (!Cmd->Provide) + return true; - // If a symbol was in PROVIDE(), we need to define it only when - // it is a referenced undefined symbol. + // If a symbol was in PROVIDE(), we need to define it only + // when it is a referenced undefined symbol. Symbol *B = Symtab->find(Cmd->Name); - if (Cmd->Provide && (!B || B->isDefined())) + if (B && !B->isDefined()) + return true; + return false; +} + +// This function is called from processSectionCommands, +// while we are fixing the output section layout. +void LinkerScript::addSymbol(SymbolAssignment *Cmd) { + if (!shouldDefineSym(Cmd)) return; // Define a symbol. @@ -153,6 +192,76 @@ void LinkerScript::addSymbol(SymbolAssignment *Cmd) { Cmd->Sym = cast<Defined>(Sym); } +// This function is called from LinkerScript::declareSymbols. +// It creates a placeholder symbol if needed. +static void declareSymbol(SymbolAssignment *Cmd) { + if (!shouldDefineSym(Cmd)) + return; + + // We can't calculate final value right now. + Symbol *Sym; + uint8_t Visibility = Cmd->Hidden ? STV_HIDDEN : STV_DEFAULT; + std::tie(Sym, std::ignore) = Symtab->insert(Cmd->Name, /*Type*/ 0, Visibility, + /*CanOmitFromDynSym*/ false, + /*File*/ nullptr); + replaceSymbol<Defined>(Sym, nullptr, Cmd->Name, STB_GLOBAL, Visibility, + STT_NOTYPE, 0, 0, nullptr); + Cmd->Sym = cast<Defined>(Sym); + Cmd->Provide = false; +} + +// This method is used to handle INSERT AFTER statement. Here we rebuild +// the list of script commands to mix sections inserted into. +void LinkerScript::processInsertCommands() { + std::vector<BaseCommand *> V; + auto Insert = [&](std::vector<BaseCommand *> &From) { + V.insert(V.end(), From.begin(), From.end()); + From.clear(); + }; + + for (BaseCommand *Base : SectionCommands) { + if (auto *OS = dyn_cast<OutputSection>(Base)) { + Insert(InsertBeforeCommands[OS->Name]); + V.push_back(Base); + Insert(InsertAfterCommands[OS->Name]); + continue; + } + V.push_back(Base); + } + + for (auto &Cmds : {InsertBeforeCommands, InsertAfterCommands}) + for (const std::pair<StringRef, std::vector<BaseCommand *>> &P : Cmds) + if (!P.second.empty()) + error("unable to INSERT AFTER/BEFORE " + P.first + + ": section not defined"); + + SectionCommands = std::move(V); +} + +// Symbols defined in script should not be inlined by LTO. At the same time +// we don't know their final values until late stages of link. Here we scan +// over symbol assignment commands and create placeholder symbols if needed. +void LinkerScript::declareSymbols() { + assert(!Ctx); + for (BaseCommand *Base : SectionCommands) { + if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) { + declareSymbol(Cmd); + continue; + } + + // If the output section directive has constraints, + // we can't say for sure if it is going to be included or not. + // Skip such sections for now. Improve the checks if we ever + // need symbols from that sections to be declared early. + auto *Sec = cast<OutputSection>(Base); + if (Sec->Constraint != ConstraintKind::NoConstraint) + continue; + for (BaseCommand *Base2 : Sec->SectionCommands) + if (auto *Cmd = dyn_cast<SymbolAssignment>(Base2)) + declareSymbol(Cmd); + } +} + // This function is called from assignAddresses, while we are // fixing the output section addresses. This function is supposed // to set the final value for a given symbol assignment. @@ -249,23 +358,11 @@ static void sortSections(MutableArrayRef<InputSection *> Vec, // --sort-section is handled as an inner SORT command. // 3. If one SORT command is given, and if it is SORT_NONE, don't sort. // 4. If no SORT command is given, sort according to --sort-section. -// 5. If no SORT commands are given and --sort-section is not specified, -// apply sorting provided by --symbol-ordering-file if any exist. -static void sortInputSections( - MutableArrayRef<InputSection *> Vec, const SectionPattern &Pat, - const DenseMap<SectionBase *, int> &Order) { +static void sortInputSections(MutableArrayRef<InputSection *> Vec, + const SectionPattern &Pat) { if (Pat.SortOuter == SortSectionPolicy::None) return; - if (Pat.SortOuter == SortSectionPolicy::Default && - Config->SortSection == SortSectionPolicy::Default) { - // If -symbol-ordering-file was given, sort accordingly. - // Usually, Order is empty. - if (!Order.empty()) - sortByOrder(Vec, [&](InputSectionBase *S) { return Order.lookup(S); }); - return; - } - if (Pat.SortInner == SortSectionPolicy::Default) sortSections(Vec, Config->SortSection); else @@ -275,8 +372,7 @@ static void sortInputSections( // Compute and remember which sections the InputSectionDescription matches. std::vector<InputSection *> -LinkerScript::computeInputSections(const InputSectionDescription *Cmd, - const DenseMap<SectionBase *, int> &Order) { +LinkerScript::computeInputSections(const InputSectionDescription *Cmd) { std::vector<InputSection *> Ret; // Collects all sections that satisfy constraints of Cmd. @@ -290,8 +386,11 @@ LinkerScript::computeInputSections(const InputSectionDescription *Cmd, // For -emit-relocs we have to ignore entries like // .rela.dyn : { *(.rela.data) } // which are common because they are in the default bfd script. - if (Sec->Type == SHT_REL || Sec->Type == SHT_RELA) - continue; + // We do not ignore SHT_REL[A] linker-synthesized sections here because + // want to support scripts that do custom layout for them. + if (auto *IS = dyn_cast<InputSection>(Sec)) + if (IS->getRelocatedSection()) + continue; std::string Filename = getFilename(Sec->File); if (!Cmd->FilePat.match(Filename) || @@ -307,7 +406,7 @@ LinkerScript::computeInputSections(const InputSectionDescription *Cmd, } sortInputSections(MutableArrayRef<InputSection *>(Ret).slice(SizeBefore), - Pat, Order); + Pat); } return Ret; } @@ -315,22 +414,31 @@ LinkerScript::computeInputSections(const InputSectionDescription *Cmd, void LinkerScript::discard(ArrayRef<InputSection *> V) { for (InputSection *S : V) { if (S == InX::ShStrTab || S == InX::Dynamic || S == InX::DynSymTab || - S == InX::DynStrTab) + S == InX::DynStrTab || S == InX::RelaPlt || S == InX::RelaDyn || + S == InX::RelrDyn) error("discarding " + S->Name + " section is not allowed"); + // You can discard .hash and .gnu.hash sections by linker scripts. Since + // they are synthesized sections, we need to handle them differently than + // other regular sections. + if (S == InX::GnuHashTab) + InX::GnuHashTab = nullptr; + if (S == InX::HashTab) + InX::HashTab = nullptr; + S->Assigned = false; S->Live = false; discard(S->DependentSections); } } -std::vector<InputSection *> LinkerScript::createInputSectionList( - OutputSection &OutCmd, const DenseMap<SectionBase *, int> &Order) { +std::vector<InputSection *> +LinkerScript::createInputSectionList(OutputSection &OutCmd) { std::vector<InputSection *> Ret; for (BaseCommand *Base : OutCmd.SectionCommands) { if (auto *Cmd = dyn_cast<InputSectionDescription>(Base)) { - Cmd->Sections = computeInputSections(Cmd, Order); + Cmd->Sections = computeInputSections(Cmd); Ret.insert(Ret.end(), Cmd->Sections.begin(), Cmd->Sections.end()); } } @@ -359,7 +467,6 @@ void LinkerScript::processSectionCommands() { Ctx->OutSec = Aether; size_t I = 0; - DenseMap<SectionBase *, int> Order = buildSectionOrder(); // Add input sections to output sections. for (BaseCommand *Base : SectionCommands) { // Handle symbol assignments outside of any output section. @@ -369,12 +476,13 @@ void LinkerScript::processSectionCommands() { } if (auto *Sec = dyn_cast<OutputSection>(Base)) { - std::vector<InputSection *> V = createInputSectionList(*Sec, Order); + std::vector<InputSection *> V = createInputSectionList(*Sec); // The output section name `/DISCARD/' is special. // Any input section assigned to it is discarded. if (Sec->Name == "/DISCARD/") { discard(V); + Sec->SectionCommands.clear(); continue; } @@ -414,6 +522,8 @@ void LinkerScript::processSectionCommands() { Sec->SectionIndex = I++; if (Sec->Noload) Sec->Type = SHT_NOBITS; + if (Sec->NonAlloc) + Sec->Flags &= ~(uint64_t)SHF_ALLOC; } } Ctx = nullptr; @@ -484,7 +594,7 @@ static OutputSection *addInputSec(StringMap<OutputSection *> &Map, // ignored. We should not have two output .text sections just because one was // in a group and another was not for example. // - // It also seems that that wording was a late addition and didn't get the + // It also seems that wording was a late addition and didn't get the // necessary scrutiny. // // Merging sections with different flags is expected by some users. One @@ -529,11 +639,11 @@ static OutputSection *addInputSec(StringMap<OutputSection *> &Map, void LinkerScript::addOrphanSections() { unsigned End = SectionCommands.size(); StringMap<OutputSection *> Map; - std::vector<OutputSection *> V; - for (InputSectionBase *S : InputSections) { + + auto Add = [&](InputSectionBase *S) { if (!S->Live || S->Parent) - continue; + return; StringRef Name = getOutputSectionName(S); @@ -545,12 +655,24 @@ void LinkerScript::addOrphanSections() { if (OutputSection *Sec = findByName(makeArrayRef(SectionCommands).slice(0, End), Name)) { Sec->addSection(cast<InputSection>(S)); - continue; + return; } if (OutputSection *OS = addInputSec(Map, S, Name)) V.push_back(OS); - assert(S->getOutputSection()->SectionIndex == INT_MAX); + assert(S->getOutputSection()->SectionIndex == UINT32_MAX); + }; + + // For futher --emit-reloc handling code we need target output section + // to be created before we create relocation output section, so we want + // to create target sections first. We do not want priority handling + // for synthetic sections because them are special. + for (InputSectionBase *IS : InputSections) { + if (auto *Sec = dyn_cast<InputSection>(IS)) + if (InputSectionBase *Rel = Sec->getRelocatedSection()) + if (auto *RelIS = dyn_cast_or_null<InputSectionBase>(Rel->Parent)) + Add(RelIS); + Add(IS); } // If no SECTIONS command was given, we should insert sections commands @@ -585,33 +707,15 @@ void LinkerScript::output(InputSection *S) { // Update output section size after adding each section. This is so that // SIZEOF works correctly in the case below: // .foo { *(.aaa) a = SIZEOF(.foo); *(.bbb) } - Ctx->OutSec->Size = Pos - Ctx->OutSec->Addr; - - // If there is a memory region associated with this input section, then - // place the section in that region and update the region index. - if (Ctx->LMARegion) - Ctx->LMARegion->CurPos += Pos - Before; - // FIXME: should we also produce overflow errors for LMARegion? - - if (Ctx->MemRegion) { - uint64_t &CurOffset = Ctx->MemRegion->CurPos; - CurOffset += Pos - Before; - uint64_t CurSize = CurOffset - Ctx->MemRegion->Origin; - if (CurSize > Ctx->MemRegion->Length) { - uint64_t OverflowAmt = CurSize - Ctx->MemRegion->Length; - error("section '" + Ctx->OutSec->Name + "' will not fit in region '" + - Ctx->MemRegion->Name + "': overflowed by " + Twine(OverflowAmt) + - " bytes"); - } - } + expandOutputSection(Pos - Before); } void LinkerScript::switchTo(OutputSection *Sec) { - if (Ctx->OutSec == Sec) - return; - Ctx->OutSec = Sec; + + uint64_t Before = advance(0, 1); Ctx->OutSec->Addr = advance(0, Ctx->OutSec->Alignment); + expandMemoryRegions(Ctx->OutSec->Addr - Before); } // This function searches for a memory region to place the given output @@ -674,9 +778,8 @@ void LinkerScript::assignOffsets(OutputSection *Sec) { if (PhdrEntry *L = Ctx->OutSec->PtLoad) L->LMAOffset = Ctx->LMAOffset; - // The Size previously denoted how many InputSections had been added to this - // section, and was used for sorting SHF_LINK_ORDER sections. Reset it to - // compute the actual size value. + // We can call this method multiple times during the creation of + // thunks and want to start over calculation each time. Sec->Size = 0; // We visited SectionsCommands from processSectionCommands to @@ -685,7 +788,9 @@ void LinkerScript::assignOffsets(OutputSection *Sec) { for (BaseCommand *Base : Sec->SectionCommands) { // This handles the assignments to symbol or to the dot. if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) { + Cmd->Addr = Dot; assignSymbol(Cmd, true); + Cmd->Size = Dot - Cmd->Addr; continue; } @@ -693,17 +798,7 @@ void LinkerScript::assignOffsets(OutputSection *Sec) { if (auto *Cmd = dyn_cast<ByteCommand>(Base)) { Cmd->Offset = Dot - Ctx->OutSec->Addr; Dot += Cmd->Size; - if (Ctx->MemRegion) - Ctx->MemRegion->CurPos += Cmd->Size; - if (Ctx->LMARegion) - Ctx->LMARegion->CurPos += Cmd->Size; - Ctx->OutSec->Size = Dot - Ctx->OutSec->Addr; - continue; - } - - // Handle ASSERT(). - if (auto *Cmd = dyn_cast<AssertCommand>(Base)) { - Cmd->Expression(); + expandOutputSection(Cmd->Size); continue; } @@ -728,24 +823,28 @@ void LinkerScript::assignOffsets(OutputSection *Sec) { } } -void LinkerScript::removeEmptyCommands() { - // It is common practice to use very generic linker scripts. So for any - // given run some of the output sections in the script will be empty. - // We could create corresponding empty output sections, but that would - // clutter the output. - // We instead remove trivially empty sections. The bfd linker seems even - // more aggressive at removing them. - llvm::erase_if(SectionCommands, [&](BaseCommand *Base) { - if (auto *Sec = dyn_cast<OutputSection>(Base)) - return !Sec->Live; +static bool isDiscardable(OutputSection &Sec) { + // We do not remove empty sections that are explicitly + // assigned to any segment. + if (!Sec.Phdrs.empty()) return false; - }); -} -static bool isAllSectionDescription(const OutputSection &Cmd) { - for (BaseCommand *Base : Cmd.SectionCommands) + // We do not want to remove sections that reference symbols in address and + // other expressions. We add script symbols as undefined, and want to ensure + // all of them are defined in the output, hence have to keep them. + if (Sec.ExpressionsUseSymbols) + return false; + + for (BaseCommand *Base : Sec.SectionCommands) { + if (auto Cmd = dyn_cast<SymbolAssignment>(Base)) + // Don't create empty output sections just for unreferenced PROVIDE + // symbols. + if (Cmd->Name != "." && !Cmd->Sym) + continue; + if (!isa<InputSectionDescription>(*Base)) return false; + } return true; } @@ -761,7 +860,7 @@ void LinkerScript::adjustSectionsBeforeSorting() { // Given that we want to create the section, we have to worry what impact // it will have on the link. For example, if we just create a section with // 0 for flags, it would change which PT_LOADs are created. - // We could remember that that particular section is dummy and ignore it in + // We could remember that particular section is dummy and ignore it in // other parts of the linker, but unfortunately there are quite a few places // that would need to change: // * The program header creation. @@ -772,29 +871,47 @@ void LinkerScript::adjustSectionsBeforeSorting() { // the previous sections. Only a few flags are needed to keep the impact low. uint64_t Flags = SHF_ALLOC; - for (BaseCommand *Cmd : SectionCommands) { + for (BaseCommand *&Cmd : SectionCommands) { auto *Sec = dyn_cast<OutputSection>(Cmd); if (!Sec) continue; - if (Sec->Live) { - Flags = Sec->Flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR); - continue; - } - - if (isAllSectionDescription(*Sec)) - continue; - Sec->Live = true; - Sec->Flags = Flags; + // Handle align (e.g. ".foo : ALIGN(16) { ... }"). + if (Sec->AlignExpr) + Sec->Alignment = + std::max<uint32_t>(Sec->Alignment, Sec->AlignExpr().getValue()); + + // A live output section means that some input section was added to it. It + // might have been removed (if it was empty synthetic section), but we at + // least know the flags. + if (Sec->Live) + Flags = Sec->Flags; + + // We do not want to keep any special flags for output section + // in case it is empty. + bool IsEmpty = getInputSections(Sec).empty(); + if (IsEmpty) + Sec->Flags = Flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR); + + if (IsEmpty && isDiscardable(*Sec)) { + Sec->Live = false; + Cmd = nullptr; + } } + + // It is common practice to use very generic linker scripts. So for any + // given run some of the output sections in the script will be empty. + // We could create corresponding empty output sections, but that would + // clutter the output. + // We instead remove trivially empty sections. The bfd linker seems even + // more aggressive at removing them. + llvm::erase_if(SectionCommands, [&](BaseCommand *Base) { return !Base; }); } void LinkerScript::adjustSectionsAfterSorting() { // Try and find an appropriate memory region to assign offsets in. for (BaseCommand *Base : SectionCommands) { if (auto *Sec = dyn_cast<OutputSection>(Base)) { - if (!Sec->Live) - continue; if (!Sec->LMARegionName.empty()) { if (MemoryRegion *M = MemoryRegions.lookup(Sec->LMARegionName)) Sec->LMARegion = M; @@ -802,10 +919,6 @@ void LinkerScript::adjustSectionsAfterSorting() { error("memory region '" + Sec->LMARegionName + "' not declared"); } Sec->MemRegion = findMemoryRegion(Sec); - // Handle align (e.g. ".foo : ALIGN(16) { ... }"). - if (Sec->AlignExpr) - Sec->Alignment = - std::max<uint32_t>(Sec->Alignment, Sec->AlignExpr().getValue()); } } @@ -816,9 +929,9 @@ void LinkerScript::adjustSectionsAfterSorting() { // PHDRS { seg PT_LOAD; } // SECTIONS { .aaa : { *(.aaa) } } std::vector<StringRef> DefPhdrs; - auto FirstPtLoad = - std::find_if(PhdrsCommands.begin(), PhdrsCommands.end(), - [](const PhdrsCommand &Cmd) { return Cmd.Type == PT_LOAD; }); + auto FirstPtLoad = llvm::find_if(PhdrsCommands, [](const PhdrsCommand &Cmd) { + return Cmd.Type == PT_LOAD; + }); if (FirstPtLoad != PhdrsCommands.end()) DefPhdrs.push_back(FirstPtLoad->Name); @@ -847,6 +960,15 @@ static OutputSection *findFirstSection(PhdrEntry *Load) { return nullptr; } +static uint64_t computeBase(uint64_t Min, bool AllocateHeaders) { + // If there is no SECTIONS or if the linkerscript is explicit about program + // headers, do our best to allocate them. + if (!Script->HasSectionsCommand || AllocateHeaders) + return 0; + // Otherwise only allocate program headers if that would not add a page. + return alignDown(Min, Config->MaxPageSize); +} + // Try to find an address for the file and program headers output sections, // which were unconditionally added to the first PT_LOAD segment earlier. // @@ -870,17 +992,22 @@ void LinkerScript::allocateHeaders(std::vector<PhdrEntry *> &Phdrs) { return; PhdrEntry *FirstPTLoad = *It; + bool HasExplicitHeaders = + llvm::any_of(PhdrsCommands, [](const PhdrsCommand &Cmd) { + return Cmd.HasPhdrs || Cmd.HasFilehdr; + }); uint64_t HeaderSize = getHeaderSize(); - // When linker script with SECTIONS is being used, don't output headers - // unless there's a space for them. - uint64_t Base = HasSectionsCommand ? alignDown(Min, Config->MaxPageSize) : 0; - if (HeaderSize <= Min - Base || Script->hasPhdrsCommands()) { + if (HeaderSize <= Min - computeBase(Min, HasExplicitHeaders)) { Min = alignDown(Min - HeaderSize, Config->MaxPageSize); Out::ElfHeader->Addr = Min; Out::ProgramHeaders->Addr = Min + Out::ElfHeader->Size; return; } + // Error if we were explicitly asked to allocate headers. + if (HasExplicitHeaders) + error("could not allocate headers"); + Out::ElfHeader->PtLoad = nullptr; Out::ProgramHeaders->PtLoad = nullptr; FirstPTLoad->FirstSec = findFirstSection(FirstPTLoad); @@ -924,15 +1051,11 @@ void LinkerScript::assignAddresses() { for (BaseCommand *Base : SectionCommands) { if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) { + Cmd->Addr = Dot; assignSymbol(Cmd, false); + Cmd->Size = Dot - Cmd->Addr; continue; } - - if (auto *Cmd = dyn_cast<AssertCommand>(Base)) { - Cmd->Expression(); - continue; - } - assignOffsets(cast<OutputSection>(Base)); } Ctx = nullptr; @@ -996,9 +1119,9 @@ ExprValue LinkerScript::getSymbolValue(StringRef Name, const Twine &Loc) { if (Symbol *Sym = Symtab->find(Name)) { if (auto *DS = dyn_cast<Defined>(Sym)) return {DS->Section, false, DS->Value, Loc}; - if (auto *SS = dyn_cast<SharedSymbol>(Sym)) - if (!ErrorOnMissingSection || SS->CopyRelSec) - return {SS->CopyRelSec, false, 0, Loc}; + if (isa<SharedSymbol>(Sym)) + if (!ErrorOnMissingSection) + return {nullptr, false, 0, Loc}; } error(Loc + ": symbol not found: " + Name); diff --git a/contrib/llvm/tools/lld/ELF/LinkerScript.h b/contrib/llvm/tools/lld/ELF/LinkerScript.h index 0d7578cd2f1d..3b790dd4669f 100644 --- a/contrib/llvm/tools/lld/ELF/LinkerScript.h +++ b/contrib/llvm/tools/lld/ELF/LinkerScript.h @@ -11,9 +11,9 @@ #define LLD_ELF_LINKER_SCRIPT_H #include "Config.h" -#include "Strings.h" #include "Writer.h" #include "lld/Common/LLVM.h" +#include "lld/Common/Strings.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" @@ -75,7 +75,6 @@ enum SectionsCommandKind { AssignmentKind, // . = expr or <sym> = expr OutputSectionKind, InputSectionKind, - AssertKind, // ASSERT(expr) ByteKind // BYTE(expr), SHORT(expr), LONG(expr) or QUAD(expr) }; @@ -106,6 +105,16 @@ struct SymbolAssignment : BaseCommand { // Holds file name and line number for error reporting. std::string Location; + + // A string representation of this command. We use this for -Map. + std::string CommandString; + + // Address of this assignment command. + unsigned Addr; + + // Size of this assignment command. This is usually 0, but if + // you move '.' this may be greater than 0. + unsigned Size; }; // Linker scripts allow additional constraints to be put on ouput sections. @@ -167,24 +176,23 @@ struct InputSectionDescription : BaseCommand { std::vector<std::pair<ThunkSection *, uint32_t>> ThunkSections; }; -// Represents an ASSERT(). -struct AssertCommand : BaseCommand { - AssertCommand(Expr E) : BaseCommand(AssertKind), Expression(E) {} - - static bool classof(const BaseCommand *C) { return C->Kind == AssertKind; } - - Expr Expression; -}; - // Represents BYTE(), SHORT(), LONG(), or QUAD(). struct ByteCommand : BaseCommand { - ByteCommand(Expr E, unsigned Size) - : BaseCommand(ByteKind), Expression(E), Size(Size) {} + ByteCommand(Expr E, unsigned Size, std::string CommandString) + : BaseCommand(ByteKind), CommandString(CommandString), Expression(E), + Size(Size) {} static bool classof(const BaseCommand *C) { return C->Kind == ByteKind; } + // Keeps string representing the command. Used for -Map" is perhaps better. + std::string CommandString; + Expr Expression; + + // This is just an offset of this assignment command in the output section. unsigned Offset; + + // Size of this data command. unsigned Size; }; @@ -215,14 +223,13 @@ class LinkerScript final { void addSymbol(SymbolAssignment *Cmd); void assignSymbol(SymbolAssignment *Cmd, bool InSec); void setDot(Expr E, const Twine &Loc, bool InSec); + void expandOutputSection(uint64_t Size); + void expandMemoryRegions(uint64_t Size); std::vector<InputSection *> - computeInputSections(const InputSectionDescription *, - const llvm::DenseMap<SectionBase *, int> &Order); + computeInputSections(const InputSectionDescription *); - std::vector<InputSection *> - createInputSectionList(OutputSection &Cmd, - const llvm::DenseMap<SectionBase *, int> &Order); + std::vector<InputSection *> createInputSectionList(OutputSection &Cmd); std::vector<size_t> getPhdrIndices(OutputSection *Sec); @@ -257,7 +264,6 @@ public: ExprValue getSymbolValue(StringRef Name, const Twine &Loc); void addOrphanSections(); - void removeEmptyCommands(); void adjustSectionsBeforeSorting(); void adjustSectionsAfterSorting(); @@ -268,6 +274,10 @@ public: void assignAddresses(); void allocateHeaders(std::vector<PhdrEntry *> &Phdrs); void processSectionCommands(); + void declareSymbols(); + + // Used to handle INSERT AFTER statements. + void processInsertCommands(); // SECTIONS command list. std::vector<BaseCommand *> SectionCommands; @@ -287,6 +297,11 @@ public: // A list of symbols referenced by the script. std::vector<llvm::StringRef> ReferencedSymbols; + + // Used to implement INSERT [AFTER|BEFORE]. Contains commands that need + // to be inserted into SECTIONS commands list. + llvm::DenseMap<StringRef, std::vector<BaseCommand *>> InsertAfterCommands; + llvm::DenseMap<StringRef, std::vector<BaseCommand *>> InsertBeforeCommands; }; extern LinkerScript *Script; diff --git a/contrib/llvm/tools/lld/ELF/MapFile.cpp b/contrib/llvm/tools/lld/ELF/MapFile.cpp index dcc829315e64..54fddfb7b299 100644 --- a/contrib/llvm/tools/lld/ELF/MapFile.cpp +++ b/contrib/llvm/tools/lld/ELF/MapFile.cpp @@ -23,11 +23,13 @@ #include "InputFiles.h" #include "LinkerScript.h" #include "OutputSections.h" -#include "Strings.h" #include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" +#include "lld/Common/Strings.h" #include "lld/Common/Threads.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/SetVector.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -36,57 +38,46 @@ using namespace llvm::object; using namespace lld; using namespace lld::elf; -typedef DenseMap<const SectionBase *, SmallVector<Symbol *, 4>> SymbolMapTy; +typedef DenseMap<const SectionBase *, SmallVector<Defined *, 4>> SymbolMapTy; + +static const std::string Indent8 = " "; // 8 spaces +static const std::string Indent16 = " "; // 16 spaces // Print out the first three columns of a line. -static void writeHeader(raw_ostream &OS, uint64_t Addr, uint64_t Size, - uint64_t Align) { - int W = Config->Is64 ? 16 : 8; - OS << format("%0*llx %0*llx %5lld ", W, Addr, W, Size, Align); +static void writeHeader(raw_ostream &OS, uint64_t VMA, uint64_t LMA, + uint64_t Size, uint64_t Align) { + if (Config->Is64) + OS << format("%16llx %16llx %8llx %5lld ", VMA, LMA, Size, Align); + else + OS << format("%8llx %8llx %8llx %5lld ", VMA, LMA, Size, Align); } -static std::string indent(int Depth) { return std::string(Depth * 8, ' '); } - // Returns a list of all symbols that we want to print out. -static std::vector<Symbol *> getSymbols() { - std::vector<Symbol *> V; - for (InputFile *File : ObjectFiles) { - for (Symbol *B : File->getSymbols()) { - if (auto *SS = dyn_cast<SharedSymbol>(B)) - if (SS->CopyRelSec || SS->NeedsPltAddr) - V.push_back(SS); +static std::vector<Defined *> getSymbols() { + std::vector<Defined *> V; + for (InputFile *File : ObjectFiles) + for (Symbol *B : File->getSymbols()) if (auto *DR = dyn_cast<Defined>(B)) - if (DR->File == File && !DR->isSection() && DR->Section && - DR->Section->Live) + if (!DR->isSection() && DR->Section && DR->Section->Live && + (DR->File == File || DR->NeedsPltAddr || DR->Section->Bss)) V.push_back(DR); - } - } return V; } // Returns a map from sections to their symbols. -static SymbolMapTy getSectionSyms(ArrayRef<Symbol *> Syms) { +static SymbolMapTy getSectionSyms(ArrayRef<Defined *> Syms) { SymbolMapTy Ret; - for (Symbol *S : Syms) { - if (auto *DR = dyn_cast<Defined>(S)) { - Ret[DR->Section].push_back(S); - continue; - } - - SharedSymbol *SS = cast<SharedSymbol>(S); - if (SS->CopyRelSec) - Ret[SS->CopyRelSec].push_back(S); - else - Ret[InX::Plt].push_back(S); - } + for (Defined *DR : Syms) + Ret[DR->Section].push_back(DR); // Sort symbols by address. We want to print out symbols in the // order in the output file rather than the order they appeared // in the input files. for (auto &It : Ret) { - SmallVectorImpl<Symbol *> &V = It.second; - std::sort(V.begin(), V.end(), - [](Symbol *A, Symbol *B) { return A->getVA() < B->getVA(); }); + SmallVectorImpl<Defined *> &V = It.second; + std::stable_sort(V.begin(), V.end(), [](Defined *A, Defined *B) { + return A->getVA() < B->getVA(); + }); } return Ret; } @@ -95,12 +86,15 @@ static SymbolMapTy getSectionSyms(ArrayRef<Symbol *> Syms) { // Demangling symbols (which is what toString() does) is slow, so // we do that in batch using parallel-for. static DenseMap<Symbol *, std::string> -getSymbolStrings(ArrayRef<Symbol *> Syms) { +getSymbolStrings(ArrayRef<Defined *> Syms) { std::vector<std::string> Str(Syms.size()); parallelForEachN(0, Syms.size(), [&](size_t I) { raw_string_ostream OS(Str[I]); - writeHeader(OS, Syms[I]->getVA(), Syms[I]->getSize(), 0); - OS << indent(2) << toString(*Syms[I]); + OutputSection *OSec = Syms[I]->getOutputSection(); + uint64_t VMA = Syms[I]->getVA(); + uint64_t LMA = OSec ? OSec->getLMA() + VMA - OSec->getVA(0) : 0; + writeHeader(OS, VMA, LMA, Syms[I]->getSize(), 1); + OS << Indent16 << toString(*Syms[I]); }); DenseMap<Symbol *, std::string> Ret; @@ -109,6 +103,44 @@ getSymbolStrings(ArrayRef<Symbol *> Syms) { return Ret; } +// Print .eh_frame contents. Since the section consists of EhSectionPieces, +// we need a specialized printer for that section. +// +// .eh_frame tend to contain a lot of section pieces that are contiguous +// both in input file and output file. Such pieces are squashed before +// being displayed to make output compact. +static void printEhFrame(raw_ostream &OS, OutputSection *OSec) { + std::vector<EhSectionPiece> Pieces; + + auto Add = [&](const EhSectionPiece &P) { + // If P is adjacent to Last, squash the two. + if (!Pieces.empty()) { + EhSectionPiece &Last = Pieces.back(); + if (Last.Sec == P.Sec && Last.InputOff + Last.Size == P.InputOff && + Last.OutputOff + Last.Size == P.OutputOff) { + Last.Size += P.Size; + return; + } + } + Pieces.push_back(P); + }; + + // Gather section pieces. + for (const CieRecord *Rec : InX::EhFrame->getCieRecords()) { + Add(*Rec->Cie); + for (const EhSectionPiece *Fde : Rec->Fdes) + Add(*Fde); + } + + // Print out section pieces. + for (EhSectionPiece &P : Pieces) { + writeHeader(OS, OSec->Addr + P.OutputOff, OSec->getLMA() + P.OutputOff, + P.Size, 1); + OS << Indent8 << toString(P.Sec->File) << ":(" << P.Sec->Name << "+0x" + << Twine::utohexstr(P.InputOff) + ")\n"; + } +} + void elf::writeMapFile() { if (Config->MapFile.empty()) return; @@ -122,32 +154,109 @@ void elf::writeMapFile() { } // Collect symbol info that we want to print out. - std::vector<Symbol *> Syms = getSymbols(); + std::vector<Defined *> Syms = getSymbols(); SymbolMapTy SectionSyms = getSectionSyms(Syms); DenseMap<Symbol *, std::string> SymStr = getSymbolStrings(Syms); // Print out the header line. int W = Config->Is64 ? 16 : 8; - OS << left_justify("Address", W) << ' ' << left_justify("Size", W) - << " Align Out In Symbol\n"; + OS << right_justify("VMA", W) << ' ' << right_justify("LMA", W) + << " Size Align Out In Symbol\n"; + + for (BaseCommand *Base : Script->SectionCommands) { + if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) { + if (Cmd->Provide && !Cmd->Sym) + continue; + //FIXME: calculate and print LMA. + writeHeader(OS, Cmd->Addr, 0, Cmd->Size, 1); + OS << Cmd->CommandString << '\n'; + continue; + } - // Print out file contents. - for (OutputSection *OSec : OutputSections) { - writeHeader(OS, OSec->Addr, OSec->Size, OSec->Alignment); + auto *OSec = cast<OutputSection>(Base); + writeHeader(OS, OSec->Addr, OSec->getLMA(), OSec->Size, OSec->Alignment); OS << OSec->Name << '\n'; // Dump symbols for each input section. for (BaseCommand *Base : OSec->SectionCommands) { - auto *ISD = dyn_cast<InputSectionDescription>(Base); - if (!ISD) + if (auto *ISD = dyn_cast<InputSectionDescription>(Base)) { + for (InputSection *IS : ISD->Sections) { + if (IS == InX::EhFrame) { + printEhFrame(OS, OSec); + continue; + } + + writeHeader(OS, IS->getVA(0), OSec->getLMA() + IS->getOffset(0), + IS->getSize(), IS->Alignment); + OS << Indent8 << toString(IS) << '\n'; + for (Symbol *Sym : SectionSyms[IS]) + OS << SymStr[Sym] << '\n'; + } + continue; + } + + if (auto *Cmd = dyn_cast<ByteCommand>(Base)) { + writeHeader(OS, OSec->Addr + Cmd->Offset, OSec->getLMA() + Cmd->Offset, + Cmd->Size, 1); + OS << Indent8 << Cmd->CommandString << '\n'; + continue; + } + + if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) { + if (Cmd->Provide && !Cmd->Sym) + continue; + writeHeader(OS, Cmd->Addr, OSec->getLMA() + Cmd->Addr - OSec->getVA(0), + Cmd->Size, 1); + OS << Indent8 << Cmd->CommandString << '\n'; continue; - for (InputSection *IS : ISD->Sections) { - writeHeader(OS, OSec->Addr + IS->OutSecOff, IS->getSize(), - IS->Alignment); - OS << indent(1) << toString(IS) << '\n'; - for (Symbol *Sym : SectionSyms[IS]) - OS << SymStr[Sym] << '\n'; } } } } + +static void print(StringRef A, StringRef B) { + outs() << left_justify(A, 49) << " " << B << "\n"; +} + +// Output a cross reference table to stdout. This is for --cref. +// +// For each global symbol, we print out a file that defines the symbol +// followed by files that uses that symbol. Here is an example. +// +// strlen /lib/x86_64-linux-gnu/libc.so.6 +// tools/lld/tools/lld/CMakeFiles/lld.dir/lld.cpp.o +// lib/libLLVMSupport.a(PrettyStackTrace.cpp.o) +// +// In this case, strlen is defined by libc.so.6 and used by other two +// files. +void elf::writeCrossReferenceTable() { + if (!Config->Cref) + return; + + // Collect symbols and files. + MapVector<Symbol *, SetVector<InputFile *>> Map; + for (InputFile *File : ObjectFiles) { + for (Symbol *Sym : File->getSymbols()) { + if (isa<SharedSymbol>(Sym)) + Map[Sym].insert(File); + if (auto *D = dyn_cast<Defined>(Sym)) + if (!D->isLocal() && (!D->Section || D->Section->Live)) + Map[D].insert(File); + } + } + + // Print out a header. + outs() << "Cross Reference Table\n\n"; + print("Symbol", "File"); + + // Print out a table. + for (auto KV : Map) { + Symbol *Sym = KV.first; + SetVector<InputFile *> &Files = KV.second; + + print(toString(*Sym), toString(Sym->File)); + for (InputFile *File : Files) + if (File != Sym->File) + print("", toString(File)); + } +} diff --git a/contrib/llvm/tools/lld/ELF/MapFile.h b/contrib/llvm/tools/lld/ELF/MapFile.h index 2d93e26d4cf8..0282425888b7 100644 --- a/contrib/llvm/tools/lld/ELF/MapFile.h +++ b/contrib/llvm/tools/lld/ELF/MapFile.h @@ -13,6 +13,7 @@ namespace lld { namespace elf { void writeMapFile(); +void writeCrossReferenceTable(); } // namespace elf } // namespace lld diff --git a/contrib/llvm/tools/lld/ELF/MarkLive.cpp b/contrib/llvm/tools/lld/ELF/MarkLive.cpp index 88f558c7a3c6..a8371e212c3e 100644 --- a/contrib/llvm/tools/lld/ELF/MarkLive.cpp +++ b/contrib/llvm/tools/lld/ELF/MarkLive.cpp @@ -20,15 +20,15 @@ // //===----------------------------------------------------------------------===// +#include "MarkLive.h" #include "InputSection.h" #include "LinkerScript.h" #include "OutputSections.h" -#include "Strings.h" #include "SymbolTable.h" #include "Symbols.h" #include "Target.h" -#include "Writer.h" #include "lld/Common/Memory.h" +#include "lld/Common/Strings.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Object/ELF.h" #include <functional> @@ -60,8 +60,9 @@ static typename ELFT::uint getAddend(InputSectionBase &Sec, static DenseMap<StringRef, std::vector<InputSectionBase *>> CNamedSections; template <class ELFT, class RelT> -static void resolveReloc(InputSectionBase &Sec, RelT &Rel, - std::function<void(InputSectionBase *, uint64_t)> Fn) { +static void +resolveReloc(InputSectionBase &Sec, RelT &Rel, + llvm::function_ref<void(InputSectionBase *, uint64_t)> Fn) { Symbol &B = Sec.getFile<ELFT>()->getRelocTargetSym(Rel); // If a symbol is referenced in a live section, it is used. @@ -90,7 +91,7 @@ static void resolveReloc(InputSectionBase &Sec, RelT &Rel, template <class ELFT> static void forEachSuccessor(InputSection &Sec, - std::function<void(InputSectionBase *, uint64_t)> Fn) { + llvm::function_ref<void(InputSectionBase *, uint64_t)> Fn) { if (Sec.AreRelocsRela) { for (const typename ELFT::Rela &Rel : Sec.template relas<ELFT>()) resolveReloc<ELFT>(Sec, Rel, Fn); @@ -120,7 +121,7 @@ forEachSuccessor(InputSection &Sec, template <class ELFT, class RelTy> static void scanEhFrameSection(EhInputSection &EH, ArrayRef<RelTy> Rels, - std::function<void(InputSectionBase *, uint64_t)> Fn) { + llvm::function_ref<void(InputSectionBase *, uint64_t)> Fn) { const endianness E = ELFT::TargetEndianness; for (unsigned I = 0, N = EH.Pieces.size(); I < N; ++I) { @@ -155,14 +156,10 @@ scanEhFrameSection(EhInputSection &EH, ArrayRef<RelTy> Rels, template <class ELFT> static void scanEhFrameSection(EhInputSection &EH, - std::function<void(InputSectionBase *, uint64_t)> Fn) { + llvm::function_ref<void(InputSectionBase *, uint64_t)> Fn) { if (!EH.NumRelocations) return; - // Unfortunately we need to split .eh_frame early since some relocations in - // .eh_frame keep other section alive and some don't. - EH.split<ELFT>(); - if (EH.AreRelocsRela) scanEhFrameSection<ELFT>(EH, EH.template relas<ELFT>(), Fn); else @@ -207,7 +204,7 @@ template <class ELFT> static void doGcSections() { // (splittable) sections, each piece of data has independent liveness bit. // So we explicitly tell it which offset is in use. if (auto *MS = dyn_cast<MergeInputSection>(Sec)) - MS->markLiveAt(Offset); + MS->getSectionPiece(Offset)->Live = true; if (Sec->Live) return; @@ -279,13 +276,18 @@ template <class ELFT> void elf::markLive() { // The -gc-sections option works only for SHF_ALLOC sections // (sections that are memory-mapped at runtime). So we can - // unconditionally make non-SHF_ALLOC sections alive. + // unconditionally make non-SHF_ALLOC sections alive except + // SHF_LINK_ORDER and SHT_REL/SHT_RELA sections. // - // Non SHF_ALLOC sections are not removed even if they are + // Usually, SHF_ALLOC sections are not removed even if they are // unreachable through relocations because reachability is not // a good signal whether they are garbage or not (e.g. there is // usually no section referring to a .comment section, but we - // want to keep it.) + // want to keep it.). + // + // Note on SHF_LINK_ORDER: Such sections contain metadata and they + // have a reverse dependency on the InputSection they are linked with. + // We are able to garbage collect them. // // Note on SHF_REL{,A}: Such sections reach here only when -r // or -emit-reloc were given. And they are subject of garbage @@ -293,8 +295,9 @@ template <class ELFT> void elf::markLive() { // remove its relocation section. for (InputSectionBase *Sec : InputSections) { bool IsAlloc = (Sec->Flags & SHF_ALLOC); + bool IsLinkOrder = (Sec->Flags & SHF_LINK_ORDER); bool IsRel = (Sec->Type == SHT_REL || Sec->Type == SHT_RELA); - if (!IsAlloc && !IsRel) + if (!IsAlloc && !IsLinkOrder && !IsRel) Sec->Live = true; } @@ -305,8 +308,7 @@ template <class ELFT> void elf::markLive() { if (Config->PrintGcSections) for (InputSectionBase *Sec : InputSections) if (!Sec->Live) - message("removing unused section from '" + Sec->Name + "' in file '" + - Sec->File->getName() + "'"); + message("removing unused section " + toString(Sec)); } template void elf::markLive<ELF32LE>(); diff --git a/contrib/llvm/tools/lld/ELF/MarkLive.h b/contrib/llvm/tools/lld/ELF/MarkLive.h new file mode 100644 index 000000000000..c9b99add34de --- /dev/null +++ b/contrib/llvm/tools/lld/ELF/MarkLive.h @@ -0,0 +1,21 @@ +//===- MarkLive.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_ELF_MARKLIVE_H +#define LLD_ELF_MARKLIVE_H + +namespace lld { +namespace elf { + +template <class ELFT> void markLive(); + +} // namespace elf +} // namespace lld + +#endif // LLD_ELF_MARKLIVE_H diff --git a/contrib/llvm/tools/lld/ELF/Options.td b/contrib/llvm/tools/lld/ELF/Options.td index 20027e90aefd..04a4f8ffbe28 100644 --- a/contrib/llvm/tools/lld/ELF/Options.td +++ b/contrib/llvm/tools/lld/ELF/Options.td @@ -4,66 +4,91 @@ include "llvm/Option/OptParser.td" // two can precede the option name except those that start with 'o'. class F<string name>: Flag<["--", "-"], name>; class J<string name>: Joined<["--", "-"], name>; -class S<string name>: Separate<["--", "-"], name>; -multiclass Eq<string name> { - def "": Separate<["--", "-"], name>; - def _eq: Joined<["--", "-"], name # "=">, Alias<!cast<Separate>(NAME)>; +multiclass Eq<string name, string help> { + def NAME: Separate<["--", "-"], name>; + def NAME # _eq: Joined<["--", "-"], name # "=">, Alias<!cast<Separate>(NAME)>, + HelpText<help>; } -def auxiliary: S<"auxiliary">, HelpText<"Set DT_AUXILIARY field to the specified name">; +multiclass B<string name, string help1, string help2> { + def NAME: Flag<["--", "-"], name>, HelpText<help1>; + def no_ # NAME: Flag<["--", "-"], "no-" # name>, HelpText<help2>; +} + +defm auxiliary: Eq<"auxiliary", "Set DT_AUXILIARY field to the specified name">; def Bsymbolic: F<"Bsymbolic">, HelpText<"Bind defined symbols locally">; def Bsymbolic_functions: F<"Bsymbolic-functions">, HelpText<"Bind defined function symbols locally">; -def Bdynamic: F<"Bdynamic">, HelpText<"Link against shared libraries">; +def Bdynamic: F<"Bdynamic">, HelpText<"Link against shared libraries (default)">; def Bstatic: F<"Bstatic">, HelpText<"Do not link against shared libraries">; -def build_id: F<"build-id">, HelpText<"Generate build ID note">; +def build_id: F<"build-id">, HelpText<"Alias for --build-id=fast">; + +def build_id_eq: J<"build-id=">, HelpText<"Generate build ID note">, + MetaVarName<"[fast,md5,sha,uuid,0x<hexstring>]">; -def build_id_eq: J<"build-id=">, HelpText<"Generate build ID note">; +defm check_sections: B<"check-sections", + "Check section addresses for overlaps (default)", + "Do not check section addresses for overlaps">; -defm compress_debug_sections : Eq<"compress-debug-sections">, - HelpText<"Compress DWARF debug sections">; +defm compress_debug_sections: + Eq<"compress-debug-sections", "Compress DWARF debug sections">, + MetaVarName<"[none,zlib]">; -defm defsym: Eq<"defsym">, HelpText<"Define a symbol alias">; +defm defsym: Eq<"defsym", "Define a symbol alias">, MetaVarName<"<symbol>=<value>">; -defm library_path: Eq<"library-path">, - HelpText<"Add a directory to the library search path">, MetaVarName<"<dir>">; +defm library_path: + Eq<"library-path", "Add a directory to the library search path">, MetaVarName<"<dir>">; def O: JoinedOrSeparate<["-"], "O">, HelpText<"Optimize output file size">; -defm Tbss: Eq<"Tbss">, - HelpText<"Same as --section-start with .bss as the sectionname">; +defm Tbss: Eq<"Tbss", "Same as --section-start with .bss as the sectionname">; -defm Tdata: Eq<"Tdata">, - HelpText<"Same as --section-start with .data as the sectionname">; +defm Tdata: Eq<"Tdata", "Same as --section-start with .data as the sectionname">; -defm Ttext: Eq<"Ttext">, - HelpText<"Same as --section-start with .text as the sectionname">; +defm Ttext: Eq<"Ttext", "Same as --section-start with .text as the sectionname">; -def allow_multiple_definition: F<"allow-multiple-definition">, - HelpText<"Allow multiple definitions">; +defm allow_multiple_definition: B<"allow-multiple-definition", + "Allow multiple definitions", + "Do not allow multiple definitions (default)">; -def as_needed: F<"as-needed">, - HelpText<"Only set DT_NEEDED for shared libraries if used">; +defm apply_dynamic_relocs: B<"apply-dynamic-relocs", + "Apply link-time values for dynamic relocations", + "Do not apply link-time values for dynamic relocations (default)">; + +defm as_needed: B<"as-needed", + "Only set DT_NEEDED for shared libraries if used", + "Always set DT_NEEDED for shared libraries (default)">; + +defm call_graph_ordering_file: + Eq<"call-graph-ordering-file", "Layout sections to optimize the given callgraph">; // -chroot doesn't have a help text because it is an internal option. -def chroot: S<"chroot">; +def chroot: Separate<["--", "-"], "chroot">; def color_diagnostics: F<"color-diagnostics">, - HelpText<"Use colors in diagnostics">; + HelpText<"Alias for --color-diagnostics=always">; def color_diagnostics_eq: J<"color-diagnostics=">, - HelpText<"Use colors in diagnostics">; + HelpText<"Use colors in diagnostics">, + MetaVarName<"[auto,always,never]">; + +defm cref: B<"cref", + "Output cross reference table", + "Do not output cross reference table">; -def define_common: F<"define-common">, - HelpText<"Assign space to common symbols">; +defm define_common: B<"define-common", + "Assign space to common symbols", + "Do not assign space to common symbols">; -def demangle: F<"demangle">, HelpText<"Demangle symbol names">; +defm demangle: B<"demangle", + "Demangle symbol names (default)", + "Do not demangle symbol names">; def disable_new_dtags: F<"disable-new-dtags">, HelpText<"Disable new dynamic tags">; @@ -76,158 +101,130 @@ def discard_locals: F<"discard-locals">, def discard_none: F<"discard-none">, HelpText<"Keep all symbols in the symbol table">; -def dynamic_linker: S<"dynamic-linker">, - HelpText<"Which dynamic linker to use">; +defm dynamic_linker: Eq<"dynamic-linker", "Which dynamic linker to use">; -defm dynamic_list: Eq<"dynamic-list">, - HelpText<"Read a list of dynamic symbols">; +defm dynamic_list: Eq<"dynamic-list", "Read a list of dynamic symbols">; -def eh_frame_hdr: F<"eh-frame-hdr">, - HelpText<"Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header">; +defm eh_frame_hdr: B<"eh-frame-hdr", + "Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header", + "Do not create .eh_frame_hdr section">; def emit_relocs: F<"emit-relocs">, HelpText<"Generate relocations in output">; def enable_new_dtags: F<"enable-new-dtags">, - HelpText<"Enable new dynamic tags">; + HelpText<"Enable new dynamic tags (default)">; + +def end_group: F<"end-group">, + HelpText<"Ignored for compatibility with GNU unless you pass --warn-backrefs">; def end_lib: F<"end-lib">, HelpText<"End a grouping of objects that should be treated as if they were together in an archive">; -defm entry: Eq<"entry">, HelpText<"Name of entry point symbol">, +defm entry: Eq<"entry", "Name of entry point symbol">, MetaVarName<"<entry>">; -defm error_limit: Eq<"error-limit">, - HelpText<"Maximum number of errors to emit before stopping (0 = no limit)">; +defm error_limit: + Eq<"error-limit", "Maximum number of errors to emit before stopping (0 = no limit)">; def error_unresolved_symbols: F<"error-unresolved-symbols">, HelpText<"Report unresolved symbols as errors">; -defm exclude_libs: Eq<"exclude-libs">, - HelpText<"Exclude static libraries from automatic export">; +defm exclude_libs: Eq<"exclude-libs", "Exclude static libraries from automatic export">; + +defm execute_only: B<"execute-only", + "Do not mark executable sections readable", + "Mark executable sections readable (default)">; -def export_dynamic: F<"export-dynamic">, - HelpText<"Put symbols in the dynamic symbol table">; +defm export_dynamic: B<"export-dynamic", + "Put symbols in the dynamic symbol table", + "Do not put symbols in the dynamic symbol table (default)">; -defm export_dynamic_symbol: Eq<"export-dynamic-symbol">, - HelpText<"Put a symbol in the dynamic symbol table">; +defm export_dynamic_symbol: + Eq<"export-dynamic-symbol", "Put a symbol in the dynamic symbol table">; -def fatal_warnings: F<"fatal-warnings">, - HelpText<"Treat warnings as errors">; +defm fatal_warnings: B<"fatal-warnings", + "Treat warnings as errors", + "Do not treat warnings as errors (default)">; -defm filter: Eq<"filter">, - HelpText<"Set DT_FILTER field to the specified name">; +defm filter: Eq<"filter", "Set DT_FILTER field to the specified name">; -defm fini: Eq<"fini">, - HelpText<"Specify a finalizer function">, MetaVarName<"<symbol>">; +defm fini: Eq<"fini", "Specify a finalizer function">, MetaVarName<"<symbol>">; def fix_cortex_a53_843419: F<"fix-cortex-a53-843419">, HelpText<"Apply fixes for AArch64 Cortex-A53 erratum 843419">; -def full_shutdown : F<"full-shutdown">, - HelpText<"Perform a full shutdown instead of calling _exit">; +defm format: Eq<"format", "Change the input format of the inputs following this option">, + MetaVarName<"[default,elf,binary]">; -defm format: Eq<"format">, - HelpText<"Change the input format of the inputs following this option">, - MetaVarName<"<input-format>">; +defm gc_sections: B<"gc-sections", + "Enable garbage collection of unused sections", + "Disable garbage collection of unused sections (default)">; -def gc_sections: F<"gc-sections">, - HelpText<"Enable garbage collection of unused sections">; +defm gdb_index: B<"gdb-index", + "Generate .gdb_index section", + "Do not generate .gdb_index section (default)">; -def gdb_index: F<"gdb-index">, - HelpText<"Generate .gdb_index section">; +defm gnu_unique: B<"gnu-unique", + "Enable STB_GNU_UNIQUE symbol binding (default)", + "Disable STB_GNU_UNIQUE symbol binding">; -defm hash_style: Eq<"hash-style">, - HelpText<"Specify hash style (sysv, gnu or both)">; +defm hash_style: Eq<"hash-style", "Specify hash style (sysv, gnu or both)">; def help: F<"help">, HelpText<"Print option help">; def icf_all: F<"icf=all">, HelpText<"Enable identical code folding">; -def icf_data: F<"icf-data">, - HelpText<"Enable ICF to also fold identical read only data">; +def icf_safe: F<"icf=safe">, HelpText<"Enable safe identical code folding">; + +def icf_none: F<"icf=none">, HelpText<"Disable identical code folding (default)">; -def icf_none: F<"icf=none">, HelpText<"Disable identical code folding">; +def ignore_function_address_equality: F<"ignore-function-address-equality">, + HelpText<"lld can break the address equality of functions">; -defm image_base : Eq<"image-base">, HelpText<"Set the base address">; +def ignore_data_address_equality: F<"ignore-data-address-equality">, + HelpText<"lld can break the address equality of data">; -defm init: Eq<"init">, HelpText<"Specify an initializer function">, +defm image_base: Eq<"image-base", "Set the base address">; + +defm init: Eq<"init", "Specify an initializer function">, MetaVarName<"<symbol>">; -defm library: Eq<"library">, HelpText<"Root name of library to use">, - MetaVarName<"<libName>">; +defm just_symbols: Eq<"just-symbols", "Just link symbols">; -def lto_O: J<"lto-O">, MetaVarName<"<opt-level>">, - HelpText<"Optimization level for LTO">; +defm keep_unique: Eq<"keep-unique", "Do not fold this symbol during ICF">; + +defm library: Eq<"library", "Root name of library to use">, + MetaVarName<"<libName>">; def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">; -defm Map: Eq<"Map">, HelpText<"Print a link map to the specified file">; +defm Map: Eq<"Map", "Print a link map to the specified file">; -def merge_exidx_entries: F<"merge-exidx-entries">, - HelpText<"Enable merging .ARM.exidx entries">; +defm merge_exidx_entries: B<"merge-exidx-entries", + "Enable merging .ARM.exidx entries (default)", + "Disable merging .ARM.exidx entries">; def nostdlib: F<"nostdlib">, HelpText<"Only search directories specified on the command line">; -def no_as_needed: F<"no-as-needed">, - HelpText<"Always DT_NEEDED for shared libraries">; - def no_color_diagnostics: F<"no-color-diagnostics">, HelpText<"Do not use colors in diagnostics">; -def no_define_common: F<"no-define-common">, - HelpText<"Do not assign space to common symbols">; - -def no_demangle: F<"no-demangle">, - HelpText<"Do not demangle symbol names">; - def no_dynamic_linker: F<"no-dynamic-linker">, HelpText<"Inhibit output of .interp section">; -def no_eh_frame_hdr: F<"no-eh-frame-hdr">, - HelpText<"Do not create .eh_frame_hdr section">; - -def no_export_dynamic: F<"no-export-dynamic">; -def no_fatal_warnings: F<"no-fatal-warnings">; - -def no_gc_sections: F<"no-gc-sections">, - HelpText<"Disable garbage collection of unused sections">; - -def no_gdb_index: F<"no-gdb-index">, - HelpText<"Do not generate .gdb_index section">; - -def no_gnu_unique: F<"no-gnu-unique">, - HelpText<"Disable STB_GNU_UNIQUE symbol binding">; - -def no_merge_exidx_entries: F<"no-merge-exidx-entries">, - HelpText<"Disable merging .ARM.exidx entries">; - -def no_threads: F<"no-threads">, - HelpText<"Do not run the linker multi-threaded">; - -def no_whole_archive: F<"no-whole-archive">, - HelpText<"Restores the default behavior of loading archive members">; - def noinhibit_exec: F<"noinhibit-exec">, HelpText<"Retain the executable output file whenever it is still usable">; -def nopie: F<"nopie">, HelpText<"Do not create a position independent executable">; - -def no_omagic: Flag<["--"], "no-omagic">, MetaVarName<"<magic>">, +def no_omagic: F<"no-omagic">, MetaVarName<"<magic>">, HelpText<"Do not set the text data sections to be writable">; -def no_print_gc_sections: F<"no-print-gc-sections">, - HelpText<"Do not list removed unused sections">; - def no_rosegment: F<"no-rosegment">, HelpText<"Do not put read-only non-executable sections in their own segment">; def no_undefined: F<"no-undefined">, HelpText<"Report unresolved symbols even if the linker is creating a shared library">; -def no_undefined_version: F<"no-undefined-version">, - HelpText<"Report version scripts that refer undefined symbols">; - def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"<path>">, HelpText<"Path to file to write output">; @@ -237,42 +234,62 @@ def oformat: Separate<["--"], "oformat">, MetaVarName<"<format>">, def omagic: Flag<["--"], "omagic">, MetaVarName<"<magic>">, HelpText<"Set the text and data sections to be readable and writable">; -defm orphan_handling: Eq<"orphan-handling">, - HelpText<"Control how orphan sections are handled when linker script used">; +defm orphan_handling: + Eq<"orphan-handling", "Control how orphan sections are handled when linker script used">; + +defm pack_dyn_relocs: + Eq<"pack-dyn-relocs", "Pack dynamic relocations in the given format">, + MetaVarName<"[none,android,relr,android+relr]">; + +defm use_android_relr_tags: B<"use-android-relr-tags", + "Use SHT_ANDROID_RELR / DT_ANDROID_RELR* tags instead of SHT_RELR / DT_RELR*", + "Use SHT_RELR / DT_RELR* tags (default)">; + +defm pie: B<"pie", + "Create a position independent executable", + "Do not create a position independent executable (default)">; + +defm print_gc_sections: B<"print-gc-sections", + "List removed unused sections", + "Do not list removed unused sections (default)">; -def pack_dyn_relocs_eq: J<"pack-dyn-relocs=">, MetaVarName<"<format>">, - HelpText<"Pack dynamic relocations in the given format (none or android)">; +defm print_icf_sections: B<"print-icf-sections", + "List identical folded sections", + "Do not list identical folded sections (default)">; -def pie: F<"pie">, HelpText<"Create a position independent executable">; +def pop_state: F<"pop-state">, + HelpText<"Undo the effect of -push-state">; -def print_gc_sections: F<"print-gc-sections">, - HelpText<"List removed unused sections">; +def push_state: F<"push-state">, + HelpText<"Save the current state of -as-needed, -static and -whole-archive">; def print_map: F<"print-map">, HelpText<"Print a link map to the standard output">; -defm reproduce: Eq<"reproduce">, - HelpText<"Dump linker invocation and input files for debugging">; +defm reproduce: Eq<"reproduce", "Dump linker invocation and input files for debugging">; -defm rpath: Eq<"rpath">, HelpText<"Add a DT_RUNPATH to the output">; +defm rpath: Eq<"rpath", "Add a DT_RUNPATH to the output">; def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">; -defm retain_symbols_file: Eq<"retain-symbols-file">, - HelpText<"Retain only the symbols listed in the file">, +defm retain_symbols_file: + Eq<"retain-symbols-file", "Retain only the symbols listed in the file">, MetaVarName<"<file>">; -defm script: Eq<"script">, HelpText<"Read linker script">; +defm script: Eq<"script", "Read linker script">; -def section_start: S<"section-start">, MetaVarName<"<address>">, - HelpText<"Set address of section">; +defm section_start: Eq<"section-start", "Set address of section">, + MetaVarName<"<address>">; def shared: F<"shared">, HelpText<"Build a shared object">; -defm soname: Eq<"soname">, HelpText<"Set DT_SONAME">; +defm soname: Eq<"soname", "Set DT_SONAME">; -defm sort_section: Eq<"sort-section">, - HelpText<"Specifies sections sorting rule when linkerscript is used">; +defm sort_section: + Eq<"sort-section", "Specifies sections sorting rule when linkerscript is used">; + +def start_group: F<"start-group">, + HelpText<"Ignored for compatibility with GNU unless you pass --warn-backrefs">; def start_lib: F<"start-lib">, HelpText<"Start a grouping of objects that should be treated as if they were together in an archive">; @@ -281,33 +298,39 @@ def strip_all: F<"strip-all">, HelpText<"Strip all symbols">; def strip_debug: F<"strip-debug">, HelpText<"Strip debugging information">; -def symbol_ordering_file: S<"symbol-ordering-file">, - HelpText<"Layout sections in the order specified by symbol file">; +defm symbol_ordering_file: + Eq<"symbol-ordering-file", "Layout sections to place symbols in the order specified by symbol ordering file">; -defm sysroot: Eq<"sysroot">, HelpText<"Set the system root">; +defm sysroot: Eq<"sysroot", "Set the system root">; def target1_rel: F<"target1-rel">, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_REL32">; -def target1_abs: F<"target1-abs">, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_ABS32">; +def target1_abs: F<"target1-abs">, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_ABS32 (default)">; -defm target2: Eq<"target2">, - HelpText<"Interpret R_ARM_TARGET2 as <type>, where <type> is one of rel, abs, or got-rel">, +defm target2: + Eq<"target2", "Interpret R_ARM_TARGET2 as <type>, where <type> is one of rel, abs, or got-rel">, MetaVarName<"<type>">; -def threads: F<"threads">, HelpText<"Run the linker multi-threaded">; +defm threads: B<"threads", + "Run the linker multi-threaded (default)", + "Do not run the linker multi-threaded">; def trace: F<"trace">, HelpText<"Print the names of the input files">; -defm trace_symbol : Eq<"trace-symbol">, HelpText<"Trace references to symbols">; +defm trace_symbol: Eq<"trace-symbol", "Trace references to symbols">; + +defm undefined: Eq<"undefined", "Force undefined symbol during linking">, + MetaVarName<"<symbol>">; -defm undefined: Eq<"undefined">, - HelpText<"Force undefined symbol during linking">; +defm unresolved_symbols: + Eq<"unresolved-symbols", "Determine how to handle unresolved symbols">; -defm unresolved_symbols: Eq<"unresolved-symbols">, - HelpText<"Determine how to handle unresolved symbols">; +defm undefined_version: B<"undefined-version", + "Allow unused version in version script (default)", + "Report version scripts that refer undefined symbols">; -defm rsp_quoting: Eq<"rsp-quoting">, - HelpText<"Quoting style for response files. Values supported: windows|posix">; +defm rsp_quoting: Eq<"rsp-quoting", "Quoting style for response files">, + MetaVarName<"[posix,windows]">; def v: Flag<["-"], "v">, HelpText<"Display the version number">; @@ -315,91 +338,123 @@ def verbose: F<"verbose">, HelpText<"Verbose mode">; def version: F<"version">, HelpText<"Display the version number and exit">; -defm version_script: Eq<"version-script">, HelpText<"Read a version script">; +defm version_script: Eq<"version-script", "Read a version script">; + +defm warn_backrefs: B<"warn-backrefs", + "Warn about backward symbol references to fetch archive members", + "Do not warn about backward symbol references to fetch archive members (default)">; + +defm warn_common: B<"warn-common", + "Warn about duplicate common symbols", + "Do not warn about duplicate common symbols (default)">; -def warn_common: F<"warn-common">, - HelpText<"Warn about duplicate common symbols">; +defm warn_symbol_ordering: B<"warn-symbol-ordering", + "Warn about problems with the symbol ordering file (default)", + "Do not warn about problems with the symbol ordering file">; def warn_unresolved_symbols: F<"warn-unresolved-symbols">, HelpText<"Report unresolved symbols as warnings">; -def whole_archive: F<"whole-archive">, - HelpText<"Force load of all members in a static library">; +defm whole_archive: B<"whole-archive", + "Force load of all members in a static library", + "Do not force load of all members in a static library (default)">; -defm wrap: Eq<"wrap">, HelpText<"Use wrapper functions for symbol">, - MetaVarName<"<symbol>">; +defm wrap: Eq<"wrap", "Use wrapper functions for symbol">, + MetaVarName<"<symbol>=<symbol>">; def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">, HelpText<"Linker option extensions">; // Aliases -def alias_auxiliary: Separate<["-"], "f">, Alias<auxiliary>; -def alias_Bdynamic_call_shared: F<"call_shared">, Alias<Bdynamic>; -def alias_Bdynamic_dy: F<"dy">, Alias<Bdynamic>; -def alias_Bstatic_dn: F<"dn">, Alias<Bstatic>; -def alias_Bstatic_non_shared: F<"non_shared">, Alias<Bstatic>; -def alias_Bstatic_static: F<"static">, Alias<Bstatic>; -def alias_define_common_d: Flag<["-"], "d">, Alias<define_common>; -def alias_define_common_dc: F<"dc">, Alias<define_common>; -def alias_define_common_dp: F<"dp">, Alias<define_common>; -def alias_discard_all_x: Flag<["-"], "x">, Alias<discard_all>; -def alias_discard_locals_X: Flag<["-"], "X">, Alias<discard_locals>; -def alias_emit_relocs: Flag<["-"], "q">, Alias<emit_relocs>; -def alias_entry_e: JoinedOrSeparate<["-"], "e">, Alias<entry>; -def alias_export_dynamic_E: Flag<["-"], "E">, Alias<export_dynamic>; -def alias_filter: Separate<["-"], "F">, Alias<filter>; -def alias_format_b: S<"b">, Alias<format>; -def alias_library: JoinedOrSeparate<["-"], "l">, Alias<library>; -def alias_library_path: JoinedOrSeparate<["-"], "L">, Alias<library_path>; -def alias_omagic: Flag<["-"], "N">, Alias<omagic>; -def alias_o_output: Joined<["--"], "output=">, Alias<o>; -def alias_o_output2 : Separate<["--"], "output">, Alias<o>; -def alias_pie_pic_executable: F<"pic-executable">, Alias<pie>; -def alias_print_map_M: Flag<["-"], "M">, Alias<print_map>; -def alias_relocatable_r: Flag<["-"], "r">, Alias<relocatable>; -def alias_rpath_R: JoinedOrSeparate<["-"], "R">, Alias<rpath>; -def alias_script_T: JoinedOrSeparate<["-"], "T">, Alias<script>; -def alias_shared_Bshareable: F<"Bshareable">, Alias<shared>; -def alias_soname_h: JoinedOrSeparate<["-"], "h">, Alias<soname>; -def alias_strip_all: Flag<["-"], "s">, Alias<strip_all>; -def alias_strip_debug_S: Flag<["-"], "S">, Alias<strip_debug>; -def alias_trace: Flag<["-"], "t">, Alias<trace>; -def alias_trace_symbol_y : JoinedOrSeparate<["-"], "y">, Alias<trace_symbol>; -def alias_Ttext_segment: S<"Ttext-segment">, Alias<Ttext>; -def alias_Ttext_segment_eq: J<"Ttext-segment=">, Alias<Ttext>; -def alias_undefined_u: JoinedOrSeparate<["-"], "u">, Alias<undefined>; -def alias_version_V: Flag<["-"], "V">, Alias<version>; - -// Our symbol resolution algorithm handles symbols in archive files differently -// than traditional linkers, so we don't need --start-group and --end-group. -// These options are recongized for compatibility but ignored. -def end_group: F<"end-group">; -def end_group_paren: Flag<["-"], ")">; -def start_group: F<"start-group">; -def start_group_paren: Flag<["-"], "(">; +def: Separate<["-"], "f">, Alias<auxiliary>, HelpText<"Alias for --auxiliary">; +def: F<"call_shared">, Alias<Bdynamic>, HelpText<"Alias for --Bdynamic">; +def: F<"dy">, Alias<Bdynamic>, HelpText<"Alias for --Bdynamic">; +def: F<"dn">, Alias<Bstatic>, HelpText<"Alias for --Bstatic">; +def: F<"non_shared">, Alias<Bstatic>, HelpText<"Alias for --Bstatic">; +def: F<"static">, Alias<Bstatic>, HelpText<"Alias for --Bstatic">; +def: Flag<["-"], "d">, Alias<define_common>, HelpText<"Alias for --define-common">; +def: F<"dc">, Alias<define_common>, HelpText<"Alias for --define-common">; +def: F<"dp">, Alias<define_common>, HelpText<"Alias for --define-common">; +def: Flag<["-"], "x">, Alias<discard_all>, HelpText<"Alias for --discard-all">; +def: Flag<["-"], "X">, Alias<discard_locals>, HelpText<"Alias for --discard-locals">; +def: Flag<["-"], "q">, Alias<emit_relocs>, HelpText<"Alias for --emit-relocs">; +def: Flag<["-"], ")">, Alias<end_group>, HelpText<"Alias for --end-group">; +def: JoinedOrSeparate<["-"], "e">, Alias<entry>, HelpText<"Alias for --entry">; +def: Flag<["-"], "E">, Alias<export_dynamic>, HelpText<"Alias for --export-dynamic">; +def: Separate<["-"], "F">, Alias<filter>, HelpText<"Alias for --filter">; +def: Separate<["-"], "b">, Alias<format>, HelpText<"Alias for --format">; +def: JoinedOrSeparate<["-"], "l">, Alias<library>, HelpText<"Alias for --library">; +def: JoinedOrSeparate<["-"], "L">, Alias<library_path>, HelpText<"Alias for --library-path">; +def: F<"no-pic-executable">, Alias<no_pie>, HelpText<"Alias for --no-pie">; +def: Flag<["-"], "N">, Alias<omagic>, HelpText<"Alias for --omagic">; +def: Joined<["--"], "output=">, Alias<o>, HelpText<"Alias for -o">; +def: Separate<["--"], "output">, Alias<o>, HelpText<"Alias for -o">; +def: F<"pic-executable">, Alias<pie>, HelpText<"Alias for --pie">; +def: Flag<["-"], "M">, Alias<print_map>, HelpText<"Alias for --print-map">; +def: Flag<["-"], "r">, Alias<relocatable>, HelpText<"Alias for --relocatable">; +def: JoinedOrSeparate<["-"], "R">, Alias<rpath>, HelpText<"Alias for --rpath">; +def: JoinedOrSeparate<["-"], "T">, Alias<script>, HelpText<"Alias for --script">; +def: F<"Bshareable">, Alias<shared>, HelpText<"Alias for --shared">; +def: JoinedOrSeparate<["-"], "h">, Alias<soname>, HelpText<"Alias for --soname">; +def: Flag<["-"], "(">, Alias<start_group>, HelpText<"Alias for --start-group">; +def: Flag<["-"], "s">, Alias<strip_all>, HelpText<"Alias for --strip-all">; +def: Flag<["-"], "S">, Alias<strip_debug>, HelpText<"Alias for --strip-debug">; +def: Flag<["-"], "t">, Alias<trace>, HelpText<"Alias for --trace">; +def: JoinedOrSeparate<["-"], "y">, Alias<trace_symbol>, HelpText<"Alias for --trace-symbol">; +def: Separate<["-", "--"], "Ttext-segment">, Alias<Ttext>, HelpText<"Alias for --Ttext">; +def: Joined<["-", "--"], "Ttext-segment=">, Alias<Ttext>, HelpText<"Alias for --Ttext">; +def: JoinedOrSeparate<["-"], "u">, Alias<undefined>, HelpText<"Alias for --undefined">; +def: Flag<["-"], "V">, Alias<version>, HelpText<"Alias for --version">; // LTO-related options. def lto_aa_pipeline: J<"lto-aa-pipeline=">, HelpText<"AA pipeline to run during LTO. Used in conjunction with -lto-newpm-passes">; +def lto_debug_pass_manager: F<"lto-debug-pass-manager">, + HelpText<"Debug new pass manager">; +def lto_new_pass_manager: F<"lto-new-pass-manager">, + HelpText<"Use new pass manager">; def lto_newpm_passes: J<"lto-newpm-passes=">, HelpText<"Passes to run during LTO">; +def lto_O: J<"lto-O">, MetaVarName<"<opt-level>">, + HelpText<"Optimization level for LTO">; def lto_partitions: J<"lto-partitions=">, HelpText<"Number of LTO codegen partitions">; +def lto_sample_profile: J<"lto-sample-profile=">, + HelpText<"Sample profile file path">; def disable_verify: F<"disable-verify">; -def mllvm: S<"mllvm">; +defm mllvm: Eq<"mllvm", "Additional arguments to forward to LLVM's option processing">; def opt_remarks_filename: Separate<["--"], "opt-remarks-filename">, HelpText<"YAML output file for optimization remarks">; def opt_remarks_with_hotness: Flag<["--"], "opt-remarks-with-hotness">, - HelpText<"Include hotness informations in the optimization remarks file">; -defm plugin_opt: Eq<"plugin-opt">, - HelpText<"specifies LTO options for compatibility with GNU linkers">; + HelpText<"Include hotness information in the optimization remarks file">; +defm plugin_opt: Eq<"plugin-opt", "specifies LTO options for compatibility with GNU linkers">; def save_temps: F<"save-temps">; def thinlto_cache_dir: J<"thinlto-cache-dir=">, HelpText<"Path to ThinLTO cached object file directory">; -def thinlto_cache_policy: S<"thinlto-cache-policy">, - HelpText<"Pruning policy for the ThinLTO cache">; +defm thinlto_cache_policy: Eq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">; def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">; +def: J<"plugin-opt=O">, Alias<lto_O>, HelpText<"Alias for -lto-O">; +def: F<"plugin-opt=debug-pass-manager">, + Alias<lto_debug_pass_manager>, HelpText<"Alias for -lto-debug-pass-manager">; +def: F<"plugin-opt=disable-verify">, Alias<disable_verify>, HelpText<"Alias for -disable-verify">; +def plugin_opt_dwo_dir_eq: J<"plugin-opt=dwo_dir=">, + HelpText<"Directory to store .dwo files when LTO and debug fission are used">; +def: J<"plugin-opt=jobs=">, Alias<thinlto_jobs>, HelpText<"Alias for -thinlto-jobs">; +def: J<"plugin-opt=lto-partitions=">, Alias<lto_partitions>, HelpText<"Alias for -lto-partitions">; +def plugin_opt_mcpu_eq: J<"plugin-opt=mcpu=">; +def: F<"plugin-opt=new-pass-manager">, + Alias<lto_new_pass_manager>, HelpText<"Alias for -lto-new-pass-manager">; +def plugin_opt_obj_path_eq: J<"plugin-opt=obj-path=">; +def: J<"plugin-opt=sample-profile=">, + Alias<lto_sample_profile>, HelpText<"Alias for -lto-sample-profile">; +def: F<"plugin-opt=save-temps">, Alias<save_temps>, HelpText<"Alias for -save-temps">; +def plugin_opt_thinlto_emit_imports_files: F<"plugin-opt=thinlto-emit-imports-files">; +def plugin_opt_thinlto_index_only: F<"plugin-opt=thinlto-index-only">; +def plugin_opt_thinlto_index_only_eq: J<"plugin-opt=thinlto-index-only=">; +def plugin_opt_thinlto_object_suffix_replace_eq: J<"plugin-opt=thinlto-object-suffix-replace=">; +def plugin_opt_thinlto_prefix_replace_eq: J<"plugin-opt=thinlto-prefix-replace=">; + // Ignore LTO plugin-related options. // clang -flto passes -plugin and -plugin-opt to the linker. This is required // for ld.gold and ld.bfd to get LTO working. But it's not for lld which doesn't @@ -407,31 +462,38 @@ def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">; // just ignore the option on lld side as it's easier. In fact, the linker could // be called 'ld' and understanding which linker is used would require parsing of // --version output. -def plugin: S<"plugin">; -def plugin_eq: J<"plugin=">; +defm plugin: Eq<"plugin", "Ignored for compatibility with GNU linkers">; + +def plugin_opt_fresolution_eq: J<"plugin-opt=-fresolution=">; +def plugin_opt_pass_through_eq: J<"plugin-opt=-pass-through=">; +def plugin_opt_thinlto: J<"plugin-opt=thinlto">; +def plugin_opt_slash: J<"plugin-opt=/">; // Options listed below are silently ignored for now for compatibility. -def allow_shlib_undefined: F<"allow-shlib-undefined">; -def cref: F<"cref">; -def detect_odr_violations: F<"detect-odr-violations">; -def g: Flag<["-"], "g">; -def long_plt: F<"long-plt">; -def no_add_needed: F<"no-add-needed">; -def no_allow_shlib_undefined: F<"no-allow-shlib-undefined">; -def no_copy_dt_needed_entries: F<"no-copy-dt-needed-entries">; -def no_ctors_in_init_array: F<"no-ctors-in-init-array">; -def no_keep_memory: F<"no-keep-memory">; -def no_mmap_output_file: F<"no-mmap-output-file">; -def no_warn_common: F<"no-warn-common">; -def no_warn_mismatch: F<"no-warn-mismatch">; -def rpath_link: S<"rpath-link">; -def rpath_link_eq: J<"rpath-link=">; -def sort_common: F<"sort-common">; -def stats: F<"stats">; -def warn_execstack: F<"warn-execstack">; -def warn_once: F<"warn-once">; -def warn_shared_textrel: F<"warn-shared-textrel">; -def EB : F<"EB">; -def EL : F<"EL">; -def G: JoinedOrSeparate<["-"], "G">; -def Qy : F<"Qy">; +def: F<"allow-shlib-undefined">; +def: F<"detect-odr-violations">; +def: Flag<["-"], "g">; +def: F<"long-plt">; +def: F<"no-add-needed">; +def: F<"no-allow-shlib-undefined">; +def: F<"no-copy-dt-needed-entries">; +def: F<"no-ctors-in-init-array">; +def: F<"no-keep-memory">; +def: F<"no-mmap-output-file">; +def: F<"no-warn-mismatch">; +def: Separate<["--", "-"], "rpath-link">; +def: J<"rpath-link=">; +def: F<"sort-common">; +def: F<"stats">; +def: F<"warn-execstack">; +def: F<"warn-once">; +def: F<"warn-shared-textrel">; +def: F<"EB">; +def: F<"EL">; +def: JoinedOrSeparate<["-"], "G">; +def: F<"Qy">; + +// Hidden option used for testing MIPS multi-GOT implementation. +defm mips_got_size: + Eq<"mips-got-size", "Max size of a single MIPS GOT. 0x10000 by default.">, + Flags<[HelpHidden]>; diff --git a/contrib/llvm/tools/lld/ELF/OutputSections.cpp b/contrib/llvm/tools/lld/ELF/OutputSections.cpp index 94c98284196f..8253b18b486c 100644 --- a/contrib/llvm/tools/lld/ELF/OutputSections.cpp +++ b/contrib/llvm/tools/lld/ELF/OutputSections.cpp @@ -10,11 +10,11 @@ #include "OutputSections.h" #include "Config.h" #include "LinkerScript.h" -#include "Strings.h" #include "SymbolTable.h" #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/Memory.h" +#include "lld/Common/Strings.h" #include "lld/Common/Threads.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Support/Compression.h" @@ -25,15 +25,12 @@ using namespace llvm; using namespace llvm::dwarf; using namespace llvm::object; -using namespace llvm::support::endian; using namespace llvm::ELF; using namespace lld; using namespace lld::elf; uint8_t Out::First; -OutputSection *Out::Opd; -uint8_t *Out::OpdBuf; PhdrEntry *Out::TlsPhdr; OutputSection *Out::DebugInfo; OutputSection *Out::ElfHeader; @@ -45,7 +42,9 @@ OutputSection *Out::FiniArray; std::vector<OutputSection *> elf::OutputSections; uint32_t OutputSection::getPhdrFlags() const { - uint32_t Ret = PF_R; + uint32_t Ret = 0; + if (Config->EMachine != EM_ARM || !(Flags & SHF_ARM_PURECODE)) + Ret |= PF_R; if (Flags & SHF_WRITE) Ret |= PF_W; if (Flags & SHF_EXECINSTR) @@ -70,9 +69,7 @@ void OutputSection::writeHeaderTo(typename ELFT::Shdr *Shdr) { OutputSection::OutputSection(StringRef Name, uint32_t Type, uint64_t Flags) : BaseCommand(OutputSectionKind), SectionBase(Output, Name, Flags, /*Entsize*/ 0, /*Alignment*/ 1, Type, - /*Info*/ 0, - /*Link*/ 0), - SectionIndex(INT_MAX) { + /*Info*/ 0, /*Link*/ 0) { Live = false; } @@ -91,13 +88,15 @@ static bool canMergeToProgbits(unsigned Type) { void OutputSection::addSection(InputSection *IS) { if (!Live) { // If IS is the first section to be added to this section, - // initialize Type and Entsize from IS. + // initialize Type, Entsize and flags from IS. Live = true; Type = IS->Type; Entsize = IS->Entsize; + Flags = IS->Flags; } else { // Otherwise, check if new type or flags are compatible with existing ones. - if ((Flags & (SHF_ALLOC | SHF_TLS)) != (IS->Flags & (SHF_ALLOC | SHF_TLS))) + unsigned Mask = SHF_ALLOC | SHF_TLS | SHF_LINK_ORDER; + if ((Flags & Mask) != (IS->Flags & Mask)) error("incompatible section flags for " + Name + "\n>>> " + toString(IS) + ": 0x" + utohexstr(IS->Flags) + "\n>>> output section " + Name + ": 0x" + utohexstr(Flags)); @@ -114,9 +113,14 @@ void OutputSection::addSection(InputSection *IS) { } IS->Parent = this; - Flags |= IS->Flags; + uint64_t AndMask = + Config->EMachine == EM_ARM ? (uint64_t)SHF_ARM_PURECODE : 0; + uint64_t OrMask = ~AndMask; + uint64_t AndFlags = (Flags & IS->Flags) & AndMask; + uint64_t OrFlags = (Flags | IS->Flags) & OrMask; + Flags = AndFlags | OrFlags; + Alignment = std::max(Alignment, IS->Alignment); - IS->OutSecOff = Size++; // If this section contains a table of fixed-size entries, sh_entsize // holds the element size. If it contains elements of different size we @@ -134,8 +138,8 @@ void OutputSection::addSection(InputSection *IS) { } } -void elf::sortByOrder(MutableArrayRef<InputSection *> In, - std::function<int(InputSectionBase *S)> Order) { +static void sortByOrder(MutableArrayRef<InputSection *> In, + llvm::function_ref<int(InputSectionBase *S)> Order) { typedef std::pair<int, InputSection *> Pair; auto Comp = [](const Pair &A, const Pair &B) { return A.first < B.first; }; @@ -158,11 +162,11 @@ bool OutputSection::classof(const BaseCommand *C) { return C->Kind == OutputSectionKind; } -void OutputSection::sort(std::function<int(InputSectionBase *S)> Order) { +void OutputSection::sort(llvm::function_ref<int(InputSectionBase *S)> Order) { assert(Live); - assert(SectionCommands.size() == 1); - sortByOrder(cast<InputSectionDescription>(SectionCommands[0])->Sections, - Order); + for (BaseCommand *B : SectionCommands) + if (auto *ISD = dyn_cast<InputSectionDescription>(B)) + sortByOrder(ISD->Sections, Order); } // Fill [Buf, Buf + Size) with Filler. @@ -205,11 +209,11 @@ static void writeInt(uint8_t *Buf, uint64_t Data, uint64_t Size) { if (Size == 1) *Buf = Data; else if (Size == 2) - write16(Buf, Data, Config->Endianness); + write16(Buf, Data); else if (Size == 4) - write32(Buf, Data, Config->Endianness); + write32(Buf, Data); else if (Size == 8) - write64(Buf, Data, Config->Endianness); + write64(Buf, Data); else llvm_unreachable("unsupported Size argument"); } @@ -231,12 +235,7 @@ template <class ELFT> void OutputSection::writeTo(uint8_t *Buf) { } // Write leading padding. - std::vector<InputSection *> Sections; - for (BaseCommand *Cmd : SectionCommands) - if (auto *ISD = dyn_cast<InputSectionDescription>(Cmd)) - for (InputSection *IS : ISD->Sections) - if (IS->Live) - Sections.push_back(IS); + std::vector<InputSection *> Sections = getInputSections(this); uint32_t Filler = getFiller(); if (Filler) fill(Buf, Sections.empty() ? Size : Sections[0]->OutSecOff, Filler); @@ -281,17 +280,13 @@ static void finalizeShtGroup(OutputSection *OS, } template <class ELFT> void OutputSection::finalize() { - InputSection *First = nullptr; - for (BaseCommand *Base : SectionCommands) { - if (auto *ISD = dyn_cast<InputSectionDescription>(Base)) { - if (ISD->Sections.empty()) - continue; - if (First == nullptr) - First = ISD->Sections.front(); - } - if (isa<ByteCommand>(Base) && Type == SHT_NOBITS) - Type = SHT_PROGBITS; - } + if (Type == SHT_NOBITS) + for (BaseCommand *Base : SectionCommands) + if (isa<ByteCommand>(Base)) + Type = SHT_PROGBITS; + + std::vector<InputSection *> V = getInputSections(this); + InputSection *First = V.empty() ? nullptr : V[0]; if (Flags & SHF_LINK_ORDER) { // We must preserve the link order dependency of sections with the @@ -367,8 +362,6 @@ static bool compCtors(const InputSection *A, const InputSection *B) { assert(Y.startswith(".ctors") || Y.startswith(".dtors")); X = X.substr(6); Y = Y.substr(6); - if (X.empty() && Y.empty()) - return false; return X < Y; } @@ -394,6 +387,14 @@ int elf::getPriority(StringRef S) { return V; } +std::vector<InputSection *> elf::getInputSections(OutputSection *OS) { + std::vector<InputSection *> Ret; + for (BaseCommand *Base : OS->SectionCommands) + if (auto *ISD = dyn_cast<InputSectionDescription>(Base)) + Ret.insert(Ret.end(), ISD->Sections.begin(), ISD->Sections.end()); + return Ret; +} + // Sorts input sections by section name suffixes, so that .foo.N comes // before .foo.M if N < M. Used to sort .{init,fini}_array.N sections. // We want to keep the original order if the priorities are the same diff --git a/contrib/llvm/tools/lld/ELF/OutputSections.h b/contrib/llvm/tools/lld/ELF/OutputSections.h index c006eae0c04b..efb6aabe9743 100644 --- a/contrib/llvm/tools/lld/ELF/OutputSections.h +++ b/contrib/llvm/tools/lld/ELF/OutputSections.h @@ -14,7 +14,6 @@ #include "InputSection.h" #include "LinkerScript.h" #include "Relocations.h" - #include "lld/Common/LLVM.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Object/ELF.h" @@ -52,7 +51,7 @@ public: uint64_t getLMA() const { return PtLoad ? Addr + PtLoad->LMAOffset : Addr; } template <typename ELFT> void writeHeaderTo(typename ELFT::Shdr *SHdr); - unsigned SectionIndex; + uint32_t SectionIndex = UINT32_MAX; unsigned SortRank; uint32_t getPhdrFlags() const; @@ -100,13 +99,16 @@ public: std::string Location; std::string MemoryRegionName; std::string LMARegionName; + bool NonAlloc = false; bool Noload = false; + bool ExpressionsUseSymbols = false; + bool InOverlay = false; template <class ELFT> void finalize(); template <class ELFT> void writeTo(uint8_t *Buf); template <class ELFT> void maybeCompress(); - void sort(std::function<int(InputSectionBase *S)> Order); + void sort(llvm::function_ref<int(InputSectionBase *S)> Order); void sortInitFini(); void sortCtorsDtors(); @@ -120,13 +122,13 @@ private: int getPriority(StringRef S); +std::vector<InputSection *> getInputSections(OutputSection* OS); + // All output sections that are handled by the linker specially are // globally accessible. Writer initializes them, so don't use them // until Writer is initialized. struct Out { static uint8_t First; - static OutputSection *Opd; - static uint8_t *OpdBuf; static PhdrEntry *TlsPhdr; static OutputSection *DebugInfo; static OutputSection *ElfHeader; @@ -143,8 +145,6 @@ namespace lld { namespace elf { uint64_t getHeaderSize(); -void sortByOrder(llvm::MutableArrayRef<InputSection *> In, - std::function<int(InputSectionBase *S)> Order); extern std::vector<OutputSection *> OutputSections; } // namespace elf diff --git a/contrib/llvm/tools/lld/ELF/Relocations.cpp b/contrib/llvm/tools/lld/ELF/Relocations.cpp index 37bc8bcbd571..467219ad0542 100644 --- a/contrib/llvm/tools/lld/ELF/Relocations.cpp +++ b/contrib/llvm/tools/lld/ELF/Relocations.cpp @@ -45,14 +45,14 @@ #include "Config.h" #include "LinkerScript.h" #include "OutputSections.h" -#include "Strings.h" #include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "Thunks.h" #include "lld/Common/Memory.h" - +#include "lld/Common/Strings.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/Support/Endian.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> @@ -80,55 +80,22 @@ static std::string getLocation(InputSectionBase &S, const Symbol &Sym, return Msg + S.getObjMsg(Off); } -// This is a MIPS-specific rule. -// -// In case of MIPS GP-relative relocations always resolve to a definition -// in a regular input file, ignoring the one-definition rule. So we, -// for example, should not attempt to create a dynamic relocation even -// if the target symbol is preemptible. There are two two MIPS GP-relative -// relocations R_MIPS_GPREL16 and R_MIPS_GPREL32. But only R_MIPS_GPREL16 -// can be against a preemptible symbol. -// -// To get MIPS relocation type we apply 0xff mask. In case of O32 ABI all -// relocation types occupy eight bit. In case of N64 ABI we extract first -// relocation from 3-in-1 packet because only the first relocation can -// be against a real symbol. -static bool isMipsGprel(RelType Type) { - if (Config->EMachine != EM_MIPS) - return false; - Type &= 0xff; - return Type == R_MIPS_GPREL16 || Type == R_MICROMIPS_GPREL16 || - Type == R_MICROMIPS_GPREL7_S2; -} - // This function is similar to the `handleTlsRelocation`. MIPS does not // support any relaxations for TLS relocations so by factoring out MIPS // handling in to the separate function we can simplify the code and do not // pollute other `handleTlsRelocation` by MIPS `ifs` statements. // Mips has a custom MipsGotSection that handles the writing of GOT entries // without dynamic relocations. -template <class ELFT> static unsigned handleMipsTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C, uint64_t Offset, int64_t Addend, RelExpr Expr) { if (Expr == R_MIPS_TLSLD) { - if (InX::MipsGot->addTlsIndex() && Config->Pic) - InX::RelaDyn->addReloc({Target->TlsModuleIndexRel, InX::MipsGot, - InX::MipsGot->getTlsIndexOff(), false, nullptr, - 0}); + InX::MipsGot->addTlsIndex(*C.File); C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return 1; } - if (Expr == R_MIPS_TLSGD) { - if (InX::MipsGot->addDynTlsEntry(Sym) && Sym.IsPreemptible) { - uint64_t Off = InX::MipsGot->getGlobalDynOffset(Sym); - InX::RelaDyn->addReloc( - {Target->TlsModuleIndexRel, InX::MipsGot, Off, false, &Sym, 0}); - if (Sym.IsPreemptible) - InX::RelaDyn->addReloc({Target->TlsOffsetRel, InX::MipsGot, - Off + Config->Wordsize, false, &Sym, 0}); - } + InX::MipsGot->addDynTlsEntry(*C.File, Sym); C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return 1; } @@ -161,7 +128,7 @@ static unsigned handleARMTlsRelocation(RelType Type, Symbol &Sym, auto AddTlsReloc = [&](uint64_t Off, RelType Type, Symbol *Dest, bool Dyn) { if (Dyn) - InX::RelaDyn->addReloc({Type, InX::Got, Off, false, Dest, 0}); + InX::RelaDyn->addReloc(Type, InX::Got, Off, Dest); else InX::Got->Relocations.push_back({R_ABS, Type, Off, 0, Dest}); }; @@ -207,7 +174,7 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C, if (Config->EMachine == EM_ARM) return handleARMTlsRelocation<ELFT>(Type, Sym, C, Offset, Addend, Expr); if (Config->EMachine == EM_MIPS) - return handleMipsTlsRelocation<ELFT>(Type, Sym, C, Offset, Addend, Expr); + return handleMipsTlsRelocation(Type, Sym, C, Offset, Addend, Expr); if (isRelExprOneOf<R_TLSDESC, R_TLSDESC_PAGE, R_TLSDESC_CALL>(Expr) && Config->Shared) { @@ -221,40 +188,62 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C, return 1; } - if (isRelExprOneOf<R_TLSLD_PC, R_TLSLD>(Expr)) { + if (isRelExprOneOf<R_TLSLD_GOT, R_TLSLD_GOT_FROM_END, R_TLSLD_PC, + R_TLSLD_HINT>(Expr)) { // Local-Dynamic relocs can be relaxed to Local-Exec. if (!Config->Shared) { C.Relocations.push_back( - {R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Sym}); - return 2; + {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_LD_TO_LE), Type, + Offset, Addend, &Sym}); + return Target->TlsGdRelaxSkip; } + if (Expr == R_TLSLD_HINT) + return 1; if (InX::Got->addTlsIndex()) - InX::RelaDyn->addReloc({Target->TlsModuleIndexRel, InX::Got, - InX::Got->getTlsIndexOff(), false, nullptr, 0}); + InX::RelaDyn->addReloc(Target->TlsModuleIndexRel, InX::Got, + InX::Got->getTlsIndexOff(), nullptr); C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return 1; } // Local-Dynamic relocs can be relaxed to Local-Exec. - if (isRelExprOneOf<R_ABS, R_TLSLD, R_TLSLD_PC>(Expr) && !Config->Shared) { - C.Relocations.push_back({R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Sym}); + if (Expr == R_ABS && !Config->Shared) { + C.Relocations.push_back( + {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_LD_TO_LE), Type, + Offset, Addend, &Sym}); return 1; } - if (isRelExprOneOf<R_TLSDESC, R_TLSDESC_PAGE, R_TLSDESC_CALL, R_TLSGD, - R_TLSGD_PC>(Expr)) { + // Local-Dynamic sequence where offset of tls variable relative to dynamic + // thread pointer is stored in the got. + if (Expr == R_TLSLD_GOT_OFF) { + // Local-Dynamic relocs can be relaxed to local-exec + if (!Config->Shared) { + C.Relocations.push_back({R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Sym}); + return 1; + } + if (!Sym.isInGot()) { + InX::Got->addEntry(Sym); + uint64_t Off = Sym.getGotOffset(); + InX::Got->Relocations.push_back({R_ABS, Target->TlsOffsetRel, Off, 0, &Sym}); + } + C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); + return 1; + } + + if (isRelExprOneOf<R_TLSDESC, R_TLSDESC_PAGE, R_TLSDESC_CALL, R_TLSGD_GOT, + R_TLSGD_GOT_FROM_END, R_TLSGD_PC>(Expr)) { if (Config->Shared) { if (InX::Got->addDynTlsEntry(Sym)) { uint64_t Off = InX::Got->getGlobalDynOffset(Sym); - InX::RelaDyn->addReloc( - {Target->TlsModuleIndexRel, InX::Got, Off, false, &Sym, 0}); + InX::RelaDyn->addReloc(Target->TlsModuleIndexRel, InX::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) - InX::RelaDyn->addReloc( - {Target->TlsOffsetRel, InX::Got, OffsetOff, false, &Sym, 0}); + InX::RelaDyn->addReloc(Target->TlsOffsetRel, InX::Got, OffsetOff, + &Sym); else InX::Got->Relocations.push_back( {R_ABS, Target->TlsOffsetRel, OffsetOff, 0, &Sym}); @@ -271,8 +260,8 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C, Offset, Addend, &Sym}); if (!Sym.isInGot()) { InX::Got->addEntry(Sym); - InX::RelaDyn->addReloc( - {Target->TlsGotRel, InX::Got, Sym.getGotOffset(), false, &Sym, 0}); + InX::RelaDyn->addReloc(Target->TlsGotRel, InX::Got, Sym.getGotOffset(), + &Sym); } } else { C.Relocations.push_back( @@ -336,7 +325,7 @@ static bool isAbsoluteValue(const Symbol &Sym) { // Returns true if Expr refers a PLT entry. static bool needsPlt(RelExpr Expr) { - return isRelExprOneOf<R_PLT_PC, R_PPC_PLT_OPD, R_PLT, R_PLT_PAGE_PC>(Expr); + return isRelExprOneOf<R_PLT_PC, R_PPC_CALL_PLT, R_PLT, R_PLT_PAGE_PC>(Expr); } // Returns true if Expr refers a GOT entry. Note that this function @@ -352,7 +341,8 @@ static bool needsGot(RelExpr Expr) { // file (PC, or GOT for example). static bool isRelExpr(RelExpr Expr) { return isRelExprOneOf<R_PC, R_GOTREL, R_GOTREL_FROM_END, R_MIPS_GOTREL, - R_PAGE_PC, R_RELAX_GOT_PC>(Expr); + R_PPC_CALL, R_PPC_CALL_PLT, R_PAGE_PC, + R_RELAX_GOT_PC>(Expr); } // Returns true if a given relocation can be computed at link-time. @@ -367,11 +357,13 @@ static bool isRelExpr(RelExpr Expr) { static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym, InputSectionBase &S, uint64_t RelOff) { // These expressions always compute a constant - if (isRelExprOneOf<R_GOT_FROM_END, R_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE, - R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC, - R_MIPS_TLSGD, R_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC, - R_GOTONLY_PC_FROM_END, R_PLT_PC, R_TLSGD_PC, R_TLSGD, - R_PPC_PLT_OPD, R_TLSDESC_CALL, R_TLSDESC_PAGE, R_HINT>(E)) + if (isRelExprOneOf< + R_GOT_FROM_END, R_GOT_OFF, R_TLSLD_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE, + R_MIPS_GOTREL, R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC, + R_MIPS_TLSGD, R_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC, + R_GOTONLY_PC_FROM_END, R_PLT_PC, R_TLSGD_GOT, R_TLSGD_GOT_FROM_END, + R_TLSGD_PC, R_PPC_CALL_PLT, R_TLSDESC_CALL, R_TLSDESC_PAGE, R_HINT, + R_TLSLD_HINT>(E)) return true; // These never do, except if the entire file is position dependent or if @@ -417,39 +409,45 @@ static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym, } static RelExpr toPlt(RelExpr Expr) { - if (Expr == R_PPC_OPD) - return R_PPC_PLT_OPD; - if (Expr == R_PC) + switch (Expr) { + case R_PPC_CALL: + return R_PPC_CALL_PLT; + case R_PC: return R_PLT_PC; - if (Expr == R_PAGE_PC) + case R_PAGE_PC: return R_PLT_PAGE_PC; - if (Expr == R_ABS) + case R_ABS: return R_PLT; - return Expr; + default: + return Expr; + } } static RelExpr fromPlt(RelExpr Expr) { // We decided not to use a plt. Optimize a reference to the plt to a // reference to the symbol itself. - if (Expr == R_PLT_PC) + switch (Expr) { + case R_PLT_PC: return R_PC; - if (Expr == R_PPC_PLT_OPD) - return R_PPC_OPD; - if (Expr == R_PLT) + case R_PPC_CALL_PLT: + return R_PPC_CALL; + case R_PLT: return R_ABS; - return Expr; + default: + return Expr; + } } // Returns true if a given shared symbol is in a read-only segment in a DSO. -template <class ELFT> static bool isReadOnly(SharedSymbol *SS) { +template <class ELFT> static bool isReadOnly(SharedSymbol &SS) { typedef typename ELFT::Phdr Elf_Phdr; // Determine if the symbol is read-only by scanning the DSO's program headers. - const SharedFile<ELFT> &File = SS->getFile<ELFT>(); + const SharedFile<ELFT> &File = SS.getFile<ELFT>(); for (const Elf_Phdr &Phdr : check(File.getObj().program_headers())) if ((Phdr.p_type == ELF::PT_LOAD || Phdr.p_type == ELF::PT_GNU_RELRO) && - !(Phdr.p_flags & ELF::PF_W) && SS->Value >= Phdr.p_vaddr && - SS->Value < Phdr.p_vaddr + Phdr.p_memsz) + !(Phdr.p_flags & ELF::PF_W) && SS.Value >= Phdr.p_vaddr && + SS.Value < Phdr.p_vaddr + Phdr.p_memsz) return true; return false; } @@ -458,26 +456,45 @@ template <class ELFT> static bool isReadOnly(SharedSymbol *SS) { // // If two or more symbols are at the same offset, and at least one of // them are copied by a copy relocation, all of them need to be copied. -// Otherwise, they would refer different places at runtime. +// Otherwise, they would refer to different places at runtime. template <class ELFT> -static std::vector<SharedSymbol *> getSymbolsAt(SharedSymbol *SS) { +static SmallSet<SharedSymbol *, 4> getSymbolsAt(SharedSymbol &SS) { typedef typename ELFT::Sym Elf_Sym; - SharedFile<ELFT> &File = SS->getFile<ELFT>(); + SharedFile<ELFT> &File = SS.getFile<ELFT>(); - std::vector<SharedSymbol *> Ret; + SmallSet<SharedSymbol *, 4> Ret; for (const Elf_Sym &S : File.getGlobalELFSyms()) { if (S.st_shndx == SHN_UNDEF || S.st_shndx == SHN_ABS || - S.st_value != SS->Value) + S.st_value != SS.Value) continue; StringRef Name = check(S.getName(File.getStringTable())); Symbol *Sym = Symtab->find(Name); if (auto *Alias = dyn_cast_or_null<SharedSymbol>(Sym)) - Ret.push_back(Alias); + Ret.insert(Alias); } return Ret; } +// When a symbol is copy relocated or we create a canonical plt entry, it is +// effectively a defined symbol. In the case of copy relocation the symbol is +// in .bss and in the case of a canonical plt entry it is in .plt. This function +// replaces the existing symbol with a Defined pointing to the appropriate +// location. +static void replaceWithDefined(Symbol &Sym, SectionBase *Sec, uint64_t Value, + uint64_t Size) { + Symbol Old = Sym; + replaceSymbol<Defined>(&Sym, Sym.File, Sym.getName(), Sym.Binding, + Sym.StOther, Sym.Type, Value, Size, Sec); + Sym.PltIndex = Old.PltIndex; + Sym.GotIndex = Old.GotIndex; + Sym.VerdefIndex = Old.VerdefIndex; + Sym.IsPreemptible = true; + Sym.ExportDynamic = true; + Sym.IsUsedInRegularObj = true; + Sym.Used = true; +} + // Reserve space in .bss or .bss.rel.ro for copy relocation. // // The copy relocation is pretty much a hack. If you use a copy relocation @@ -520,17 +537,17 @@ static std::vector<SharedSymbol *> getSymbolsAt(SharedSymbol *SS) { // to the variable in .bss. This kind of issue is sometimes very hard to // debug. What's a solution? Instead of exporting a varaible V from a DSO, // define an accessor getV(). -template <class ELFT> static void addCopyRelSymbol(SharedSymbol *SS) { +template <class ELFT> static void addCopyRelSymbol(SharedSymbol &SS) { // Copy relocation against zero-sized symbol doesn't make sense. - uint64_t SymSize = SS->getSize(); - if (SymSize == 0) - fatal("cannot create a copy relocation for symbol " + toString(*SS)); + uint64_t SymSize = SS.getSize(); + if (SymSize == 0 || SS.Alignment == 0) + fatal("cannot create a copy relocation for symbol " + toString(SS)); // See if this symbol is in a read-only segment. If so, preserve the symbol's // memory protection by reserving space in the .bss.rel.ro section. bool IsReadOnly = isReadOnly<ELFT>(SS); BssSection *Sec = make<BssSection>(IsReadOnly ? ".bss.rel.ro" : ".bss", - SymSize, SS->Alignment); + SymSize, SS.Alignment); if (IsReadOnly) InX::BssRelRo->getParent()->addSection(Sec); else @@ -539,125 +556,10 @@ template <class ELFT> static void addCopyRelSymbol(SharedSymbol *SS) { // Look through the DSO's dynamic symbol table for aliases and create a // dynamic symbol for each one. This causes the copy relocation to correctly // interpose any aliases. - for (SharedSymbol *Sym : getSymbolsAt<ELFT>(SS)) { - Sym->CopyRelSec = Sec; - Sym->IsPreemptible = false; - Sym->IsUsedInRegularObj = true; - Sym->Used = true; - } + for (SharedSymbol *Sym : getSymbolsAt<ELFT>(SS)) + replaceWithDefined(*Sym, Sec, 0, Sym->Size); - InX::RelaDyn->addReloc({Target->CopyRel, Sec, 0, false, SS, 0}); -} - -static void errorOrWarn(const Twine &Msg) { - if (!Config->NoinhibitExec) - error(Msg); - else - warn(Msg); -} - -// Returns PLT relocation expression. -// -// This handles a non PIC program call to function in a shared library. In -// an ideal world, we could just report an error saying the relocation can -// overflow at runtime. In the real world with glibc, crt1.o has a -// R_X86_64_PC32 pointing to libc.so. -// -// The general idea on how to handle such cases is to create a PLT entry and -// use that as the function value. -// -// For the static linking part, we just return a plt expr and everything -// else will use the the PLT entry as the address. -// -// The remaining problem is making sure pointer equality still works. We -// need the help of the dynamic linker for that. We let it know that we have -// a direct reference to a so symbol by creating an undefined symbol with a -// non zero st_value. Seeing that, the dynamic linker resolves the symbol to -// the value of the symbol we created. This is true even for got entries, so -// pointer equality is maintained. To avoid an infinite loop, the only entry -// that points to the real function is a dedicated got entry used by the -// plt. That is identified by special relocation types (R_X86_64_JUMP_SLOT, -// R_386_JMP_SLOT, etc). -static RelExpr getPltExpr(Symbol &Sym, RelExpr Expr, bool &IsConstant) { - Sym.NeedsPltAddr = true; - Sym.IsPreemptible = false; - IsConstant = true; - return toPlt(Expr); -} - -// This modifies the expression if we can use a copy relocation or point the -// symbol to the PLT. -template <class ELFT> -static RelExpr adjustExpr(Symbol &Sym, RelExpr Expr, RelType Type, - InputSectionBase &S, uint64_t RelOff, - bool &IsConstant) { - // If a relocation can be applied at link-time, we don't need to - // create a dynamic relocation in the first place. - if (IsConstant) - return Expr; - - // We can create any dynamic relocation supported by the dynamic linker if a - // section is writable or we are passed -z notext. - bool CanWrite = (S.Flags & SHF_WRITE) || !Config->ZText; - if (CanWrite && Target->isPicRel(Type)) - return Expr; - - // If the relocation is to a weak undef, and we are producing - // executable, give up on it and produce a non preemptible 0. - if (!Config->Shared && Sym.isUndefWeak()) { - Sym.IsPreemptible = false; - IsConstant = true; - return Expr; - } - - // If we got here we know that this relocation would require the dynamic - // linker to write a value to read only memory or use an unsupported - // relocation. - - // We can hack around it if we are producing an executable and - // the refered symbol can be preemepted to refer to the executable. - if (!CanWrite && (Config->Shared || (Config->Pic && !isRelExpr(Expr)))) { - error( - "can't create dynamic relocation " + toString(Type) + " against " + - (Sym.getName().empty() ? "local symbol" : "symbol: " + toString(Sym)) + - " in readonly segment; recompile object files with -fPIC" + - getLocation(S, Sym, RelOff)); - return Expr; - } - - // Copy relocations are only possible if we are creating an executable and the - // symbol is shared. - if (!Sym.isShared() || Config->Shared) - return Expr; - - if (Sym.getVisibility() != STV_DEFAULT) { - error("cannot preempt symbol: " + toString(Sym) + - getLocation(S, Sym, RelOff)); - return Expr; - } - - if (Sym.isObject()) { - // Produce a copy relocation. - auto *B = dyn_cast<SharedSymbol>(&Sym); - if (B && !B->CopyRelSec) { - if (Config->ZNocopyreloc) - error("unresolvable relocation " + toString(Type) + - " against symbol '" + toString(*B) + - "'; recompile with -fPIC or remove '-z nocopyreloc'" + - getLocation(S, Sym, RelOff)); - - addCopyRelSymbol<ELFT>(B); - } - IsConstant = true; - return Expr; - } - - if (Sym.isFunc()) - return getPltExpr(Sym, Expr, IsConstant); - - errorOrWarn("symbol '" + toString(Sym) + "' defined in " + - toString(Sym.File) + " has no type"); - return Expr; + InX::RelaDyn->addReloc(Target->CopyRel, Sec, 0, &SS); } // MIPS has an odd notion of "paired" relocations to calculate addends. @@ -732,7 +634,7 @@ static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec, return false; bool CanBeExternal = - Sym.computeBinding() != STB_LOCAL && Sym.getVisibility() == STV_DEFAULT; + Sym.computeBinding() != STB_LOCAL && Sym.Visibility == STV_DEFAULT; if (Config->UnresolvedSymbols == UnresolvedPolicy::Ignore && CanBeExternal) return false; @@ -760,12 +662,12 @@ static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec, // this for the N32 ABI. Iterate over relocation with the same offset and put // theirs types into the single bit-set. template <class RelTy> static RelType getMipsN32RelType(RelTy *&Rel, RelTy *End) { - RelType Type = Rel->getType(Config->IsMips64EL); + RelType Type = 0; uint64_t Offset = Rel->r_offset; int N = 0; - while (Rel + 1 != End && (Rel + 1)->r_offset == Offset) - Type |= (++Rel)->getType(Config->IsMips64EL) << (8 * ++N); + while (Rel != End && Rel->r_offset == Offset) + Type |= (Rel++)->getType(Config->IsMips64EL) << (8 * N++); return Type; } @@ -815,16 +717,34 @@ private: }; } // namespace +static void addRelativeReloc(InputSectionBase *IS, uint64_t OffsetInSec, + Symbol *Sym, int64_t Addend, RelExpr Expr, + RelType Type) { + // Add a relative relocation. If RelrDyn section is enabled, and the + // relocation offset is guaranteed to be even, add the relocation to + // the RelrDyn section, otherwise add it to the RelaDyn section. + // RelrDyn sections don't support odd offsets. Also, RelrDyn sections + // don't store the addend values, so we must write it to the relocated + // address. + if (InX::RelrDyn && IS->Alignment >= 2 && OffsetInSec % 2 == 0) { + IS->Relocations.push_back({Expr, Type, OffsetInSec, Addend, Sym}); + InX::RelrDyn->Relocs.push_back({IS, OffsetInSec}); + return; + } + InX::RelaDyn->addReloc(Target->RelativeRel, IS, OffsetInSec, Sym, Addend, + Expr, Type); +} + template <class ELFT, class GotPltSection> static void addPltEntry(PltSection *Plt, GotPltSection *GotPlt, - RelocationBaseSection *Rel, RelType Type, Symbol &Sym, - bool UseSymVA) { + RelocationBaseSection *Rel, RelType Type, Symbol &Sym) { Plt->addEntry<ELFT>(Sym); GotPlt->addEntry(Sym); - Rel->addReloc({Type, GotPlt, Sym.getGotPltOffset(), UseSymVA, &Sym, 0}); + Rel->addReloc( + {Type, GotPlt, Sym.getGotPltOffset(), !Sym.IsPreemptible, &Sym, 0}); } -template <class ELFT> static void addGotEntry(Symbol &Sym, bool Preemptible) { +template <class ELFT> static void addGotEntry(Symbol &Sym) { InX::Got->addEntry(Sym); RelExpr Expr = Sym.isTls() ? R_TLS : R_ABS; @@ -837,7 +757,8 @@ template <class ELFT> static void addGotEntry(Symbol &Sym, bool Preemptible) { // add a static relocation to a Relocations vector so that // InputSection::relocate will do the work for us. We may be able // to just write a value now, but it is a TODO.) - bool IsLinkTimeConstant = !Preemptible && (!Config->Pic || isAbsolute(Sym)); + bool IsLinkTimeConstant = + !Sym.IsPreemptible && (!Config->Pic || isAbsolute(Sym)); if (IsLinkTimeConstant) { InX::Got->Relocations.push_back({Expr, Target->GotRel, Off, 0, &Sym}); return; @@ -845,23 +766,33 @@ template <class ELFT> static void addGotEntry(Symbol &Sym, bool Preemptible) { // Otherwise, we emit a dynamic relocation to .rel[a].dyn so that // the GOT slot will be fixed at load-time. - RelType Type; - if (Sym.isTls()) - Type = Target->TlsGotRel; - else if (!Preemptible && Config->Pic && !isAbsolute(Sym)) - Type = Target->RelativeRel; - else - Type = Target->GotRel; - InX::RelaDyn->addReloc({Type, InX::Got, Off, !Preemptible, &Sym, 0}); + if (!Sym.isTls() && !Sym.IsPreemptible && Config->Pic && !isAbsolute(Sym)) { + addRelativeReloc(InX::Got, Off, &Sym, 0, R_ABS, Target->GotRel); + return; + } + InX::RelaDyn->addReloc(Sym.isTls() ? Target->TlsGotRel : Target->GotRel, + InX::Got, Off, &Sym, 0, + Sym.IsPreemptible ? R_ADDEND : R_ABS, Target->GotRel); +} - // REL type relocations don't have addend fields unlike RELAs, and - // their addends are stored to the section to which they are applied. - // So, store addends if we need to. - // - // This is ugly -- the difference between REL and RELA should be - // handled in a better way. It's a TODO. - if (!Config->IsRela && !Preemptible) - InX::Got->Relocations.push_back({R_ABS, Target->GotRel, Off, 0, &Sym}); +// Return true if we can define a symbol in the executable that +// contains the value/function of a symbol defined in a shared +// library. +static bool canDefineSymbolInExecutable(Symbol &Sym) { + // If the symbol has default visibility the symbol defined in the + // executable will preempt it. + // Note that we want the visibility of the shared symbol itself, not + // the visibility of the symbol in the output file we are producing. That is + // why we use Sym.StOther. + if ((Sym.StOther & 0x3) == STV_DEFAULT) + return true; + + // If we are allowed to break address equality of functions, defining + // a plt entry will allow the program to call the function in the + // .so, but the .so and the executable will no agree on the address + // of the function. Similar logic for objects. + return ((Sym.isFunc() && Config->IgnoreFunctionAddressEquality) || + (Sym.isObject() && Config->IgnoreDataAddressEquality)); } // The reason we have to do this early scan is as follows @@ -878,129 +809,23 @@ template <class ELFT> static void addGotEntry(Symbol &Sym, bool Preemptible) { // complicates things for the dynamic linker and means we would have to reserve // space for the extra PT_LOAD even if we end up not using it. template <class ELFT, class RelTy> -static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) { - OffsetGetter GetOffset(Sec); - - // Not all relocations end up in Sec.Relocations, but a lot do. - Sec.Relocations.reserve(Rels.size()); - - for (auto I = Rels.begin(), End = Rels.end(); I != End; ++I) { - const RelTy &Rel = *I; - Symbol &Sym = Sec.getFile<ELFT>()->getRelocTargetSym(Rel); - RelType Type = Rel.getType(Config->IsMips64EL); - - // Deal with MIPS oddity. - if (Config->MipsN32Abi) - Type = getMipsN32RelType(I, End); - - // Get an offset in an output section this relocation is applied to. - uint64_t Offset = GetOffset.get(Rel.r_offset); - if (Offset == uint64_t(-1)) - continue; - - // Skip if the target symbol is an erroneous undefined symbol. - if (maybeReportUndefined(Sym, Sec, Rel.r_offset)) - continue; - - RelExpr Expr = - Target->getRelExpr(Type, Sym, Sec.Data.begin() + Rel.r_offset); - - // Ignore "hint" relocations because they are only markers for relaxation. - if (isRelExprOneOf<R_HINT, R_NONE>(Expr)) - continue; - - // Handle yet another MIPS-ness. - if (isMipsGprel(Type)) { - int64_t Addend = computeAddend<ELFT>(Rel, End, Sec, Expr, Sym.isLocal()); - Sec.Relocations.push_back({R_MIPS_GOTREL, Type, Offset, Addend, &Sym}); - continue; - } - - bool Preemptible = Sym.IsPreemptible; - - // Strenghten or relax a PLT access. - // - // GNU ifunc symbols must be accessed via PLT because their addresses - // are determined by runtime. - // - // On the other hand, if we know that a PLT entry will be resolved within - // the same ELF module, we can skip PLT access and directly jump to the - // destination function. For example, if we are linking a main exectuable, - // all dynamic symbols that can be resolved within the executable will - // actually be resolved that way at runtime, because the main exectuable - // is always at the beginning of a search list. We can leverage that fact. - if (Sym.isGnuIFunc()) - Expr = toPlt(Expr); - else if (!Preemptible && Expr == R_GOT_PC && !isAbsoluteValue(Sym)) - Expr = - Target->adjustRelaxExpr(Type, Sec.Data.data() + Rel.r_offset, Expr); - else if (!Preemptible) - Expr = fromPlt(Expr); - - bool IsConstant = - isStaticLinkTimeConstant(Expr, Type, Sym, Sec, Rel.r_offset); - - Expr = adjustExpr<ELFT>(Sym, Expr, Type, Sec, Rel.r_offset, IsConstant); - if (errorCount()) - continue; - - // This relocation does not require got entry, but it is relative to got and - // needs it to be created. Here we request for that. - if (isRelExprOneOf<R_GOTONLY_PC, R_GOTONLY_PC_FROM_END, R_GOTREL, - R_GOTREL_FROM_END, R_PPC_TOC>(Expr)) - InX::Got->HasGotOffRel = true; - - // Read an addend. - int64_t Addend = computeAddend<ELFT>(Rel, End, Sec, Expr, Sym.isLocal()); - - // Process some TLS relocations, including relaxing TLS relocations. - // Note that this function does not handle all TLS relocations. - if (unsigned Processed = - handleTlsRelocation<ELFT>(Type, Sym, Sec, Offset, Addend, Expr)) { - I += (Processed - 1); - continue; - } - - // If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol. - if (needsPlt(Expr) && !Sym.isInPlt()) { - if (Sym.isGnuIFunc() && !Preemptible) - addPltEntry<ELFT>(InX::Iplt, InX::IgotPlt, InX::RelaIplt, - Target->IRelativeRel, Sym, true); - else - addPltEntry<ELFT>(InX::Plt, InX::GotPlt, InX::RelaPlt, Target->PltRel, - Sym, !Preemptible); - } - - // Create a GOT slot if a relocation needs GOT. - if (needsGot(Expr)) { - if (Config->EMachine == EM_MIPS) { - // MIPS ABI has special rules to process GOT entries and doesn't - // require relocation entries for them. A special case is TLS - // relocations. In that case dynamic loader applies dynamic - // relocations to initialize TLS GOT entries. - // See "Global Offset Table" in Chapter 5 in the following document - // for detailed description: - // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf - InX::MipsGot->addEntry(Sym, Addend, Expr); - if (Sym.isTls() && Sym.IsPreemptible) - InX::RelaDyn->addReloc({Target->TlsGotRel, InX::MipsGot, - Sym.getGotOffset(), false, &Sym, 0}); - } else if (!Sym.isInGot()) { - addGotEntry<ELFT>(Sym, Preemptible); - } - } - - if (!needsPlt(Expr) && !needsGot(Expr) && Sym.IsPreemptible) { - // We don't know anything about the finaly symbol. Just ask the dynamic - // linker to handle the relocation for us. - if (!Target->isPicRel(Type)) - errorOrWarn( - "relocation " + toString(Type) + - " cannot be used against shared object; recompile with -fPIC" + - getLocation(Sec, Sym, Offset)); +static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type, + uint64_t Offset, Symbol &Sym, const RelTy &Rel, + int64_t Addend) { + if (isStaticLinkTimeConstant(Expr, Type, Sym, Sec, Offset)) { + Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); + return; + } + bool CanWrite = (Sec.Flags & SHF_WRITE) || !Config->ZText; + if (CanWrite) { + // R_GOT refers to a position in the got, even if the symbol is preemptible. + bool IsPreemptibleValue = Sym.IsPreemptible && Expr != R_GOT; - InX::RelaDyn->addReloc( - {Target->getDynRel(Type), &Sec, Offset, false, &Sym, Addend}); + if (!IsPreemptibleValue) { + addRelativeReloc(&Sec, Offset, &Sym, Addend, Expr, Type); + return; + } else if (RelType Rel = Target->getDynRel(Type)) { + InX::RelaDyn->addReloc(Rel, &Sec, Offset, &Sym, Addend, R_ADDEND, Type); // MIPS ABI turns using of GOT and dynamic relocations inside out. // While regular ABI uses dynamic relocations to fill up GOT entries @@ -1018,37 +843,210 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) { // a dynamic relocation. // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf p.4-19 if (Config->EMachine == EM_MIPS) - InX::MipsGot->addEntry(Sym, Addend, Expr); - continue; + InX::MipsGot->addEntry(*Sec.File, Sym, Addend, Expr); + return; } + } - // The size is not going to change, so we fold it in here. - if (Expr == R_SIZE) - Addend += Sym.getSize(); + // If the relocation is to a weak undef, and we are producing + // executable, give up on it and produce a non preemptible 0. + if (!Config->Shared && Sym.isUndefWeak()) { + Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); + return; + } - // If the produced value is a constant, we just remember to write it - // when outputting this section. We also have to do it if the format - // uses Elf_Rel, since in that case the written value is the addend. - if (IsConstant) { - Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); - continue; + if (!CanWrite && (Config->Pic && !isRelExpr(Expr))) { + error( + "can't create dynamic relocation " + toString(Type) + " against " + + (Sym.getName().empty() ? "local symbol" : "symbol: " + toString(Sym)) + + " in readonly segment; recompile object files with -fPIC " + "or pass '-Wl,-z,notext' to allow text relocations in the output" + + getLocation(Sec, Sym, Offset)); + return; + } + + // Copy relocations are only possible if we are creating an executable. + if (Config->Shared) { + errorOrWarn("relocation " + toString(Type) + + " cannot be used against symbol " + toString(Sym) + + "; recompile with -fPIC" + getLocation(Sec, Sym, Offset)); + return; + } + + // If the symbol is undefined we already reported any relevant errors. + if (Sym.isUndefined()) + return; + + if (!canDefineSymbolInExecutable(Sym)) { + error("cannot preempt symbol: " + toString(Sym) + + getLocation(Sec, Sym, Offset)); + return; + } + + if (Sym.isObject()) { + // Produce a copy relocation. + if (auto *SS = dyn_cast<SharedSymbol>(&Sym)) { + if (!Config->ZCopyreloc) + error("unresolvable relocation " + toString(Type) + + " against symbol '" + toString(*SS) + + "'; recompile with -fPIC or remove '-z nocopyreloc'" + + getLocation(Sec, Sym, Offset)); + addCopyRelSymbol<ELFT>(*SS); } + Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); + return; + } - // If the output being produced is position independent, the final value - // is still not known. In that case we still need some help from the - // dynamic linker. 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->IsRela) { - InX::RelaDyn->addReloc( - {Target->RelativeRel, &Sec, Offset, true, &Sym, Addend}); - } else { - // In REL, addends are stored to the target section. - InX::RelaDyn->addReloc( - {Target->RelativeRel, &Sec, Offset, true, &Sym, 0}); - Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); + if (Sym.isFunc()) { + // This handles a non PIC program call to function in a shared library. In + // an ideal world, we could just report an error saying the relocation can + // overflow at runtime. In the real world with glibc, crt1.o has a + // R_X86_64_PC32 pointing to libc.so. + // + // The general idea on how to handle such cases is to create a PLT entry and + // use that as the function value. + // + // For the static linking part, we just return a plt expr and everything + // else will use the PLT entry as the address. + // + // The remaining problem is making sure pointer equality still works. We + // need the help of the dynamic linker for that. We let it know that we have + // a direct reference to a so symbol by creating an undefined symbol with a + // non zero st_value. Seeing that, the dynamic linker resolves the symbol to + // the value of the symbol we created. This is true even for got entries, so + // pointer equality is maintained. To avoid an infinite loop, the only entry + // that points to the real function is a dedicated got entry used by the + // plt. That is identified by special relocation types (R_X86_64_JUMP_SLOT, + // R_386_JMP_SLOT, etc). + + // For position independent executable on i386, the plt entry requires ebx + // to be set. This causes two problems: + // * If some code has a direct reference to a function, it was probably + // compiled without -fPIE/-fPIC and doesn't maintain ebx. + // * If a library definition gets preempted to the executable, it will have + // the wrong ebx value. + if (Config->Pie && Config->EMachine == EM_386) + errorOrWarn("symbol '" + toString(Sym) + + "' cannot be preempted; recompile with -fPIE" + + getLocation(Sec, Sym, Offset)); + if (!Sym.isInPlt()) + addPltEntry<ELFT>(InX::Plt, InX::GotPlt, InX::RelaPlt, Target->PltRel, + Sym); + if (!Sym.isDefined()) + replaceWithDefined(Sym, InX::Plt, Sym.getPltOffset(), 0); + Sym.NeedsPltAddr = true; + Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); + return; + } + + errorOrWarn("symbol '" + toString(Sym) + "' has no type" + + getLocation(Sec, Sym, Offset)); +} + +template <class ELFT, class RelTy> +static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I, + RelTy *End) { + const RelTy &Rel = *I; + Symbol &Sym = Sec.getFile<ELFT>()->getRelocTargetSym(Rel); + RelType Type; + + // Deal with MIPS oddity. + if (Config->MipsN32Abi) { + Type = getMipsN32RelType(I, End); + } else { + Type = Rel.getType(Config->IsMips64EL); + ++I; + } + + // Get an offset in an output section this relocation is applied to. + uint64_t Offset = GetOffset.get(Rel.r_offset); + if (Offset == uint64_t(-1)) + return; + + // Skip if the target symbol is an erroneous undefined symbol. + if (maybeReportUndefined(Sym, Sec, Rel.r_offset)) + return; + + const uint8_t *RelocatedAddr = Sec.Data.begin() + Rel.r_offset; + RelExpr Expr = Target->getRelExpr(Type, Sym, RelocatedAddr); + + // Ignore "hint" relocations because they are only markers for relaxation. + if (isRelExprOneOf<R_HINT, R_NONE>(Expr)) + return; + + // Strenghten or relax relocations. + // + // GNU ifunc symbols must be accessed via PLT because their addresses + // are determined by runtime. + // + // On the other hand, if we know that a PLT entry will be resolved within + // the same ELF module, we can skip PLT access and directly jump to the + // destination function. For example, if we are linking a main exectuable, + // all dynamic symbols that can be resolved within the executable will + // actually be resolved that way at runtime, because the main exectuable + // is always at the beginning of a search list. We can leverage that fact. + if (Sym.isGnuIFunc()) + Expr = toPlt(Expr); + else if (!Sym.IsPreemptible && Expr == R_GOT_PC && !isAbsoluteValue(Sym)) + Expr = Target->adjustRelaxExpr(Type, RelocatedAddr, Expr); + else if (!Sym.IsPreemptible) + Expr = fromPlt(Expr); + + // This relocation does not require got entry, but it is relative to got and + // needs it to be created. Here we request for that. + if (isRelExprOneOf<R_GOTONLY_PC, R_GOTONLY_PC_FROM_END, R_GOTREL, + R_GOTREL_FROM_END, R_PPC_TOC>(Expr)) + InX::Got->HasGotOffRel = true; + + // Read an addend. + int64_t Addend = computeAddend<ELFT>(Rel, End, Sec, Expr, Sym.isLocal()); + + // Process some TLS relocations, including relaxing TLS relocations. + // Note that this function does not handle all TLS relocations. + if (unsigned Processed = + handleTlsRelocation<ELFT>(Type, Sym, Sec, Offset, Addend, Expr)) { + I += (Processed - 1); + return; + } + + // If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol. + if (needsPlt(Expr) && !Sym.isInPlt()) { + if (Sym.isGnuIFunc() && !Sym.IsPreemptible) + addPltEntry<ELFT>(InX::Iplt, InX::IgotPlt, InX::RelaIplt, + Target->IRelativeRel, Sym); + else + addPltEntry<ELFT>(InX::Plt, InX::GotPlt, InX::RelaPlt, Target->PltRel, + Sym); + } + + // Create a GOT slot if a relocation needs GOT. + if (needsGot(Expr)) { + if (Config->EMachine == EM_MIPS) { + // MIPS ABI has special rules to process GOT entries and doesn't + // require relocation entries for them. A special case is TLS + // relocations. In that case dynamic loader applies dynamic + // relocations to initialize TLS GOT entries. + // See "Global Offset Table" in Chapter 5 in the following document + // for detailed description: + // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf + InX::MipsGot->addEntry(*Sec.File, Sym, Addend, Expr); + } else if (!Sym.isInGot()) { + addGotEntry<ELFT>(Sym); } } + + processRelocAux<ELFT>(Sec, Expr, Type, Offset, Sym, Rel, Addend); +} + +template <class ELFT, class RelTy> +static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) { + OffsetGetter GetOffset(Sec); + + // Not all relocations end up in Sec.Relocations, but a lot do. + Sec.Relocations.reserve(Rels.size()); + + for (auto I = Rels.begin(), End = Rels.end(); I != End;) + scanReloc<ELFT>(Sec, GetOffset, I, End); } template <class ELFT> void elf::scanRelocations(InputSectionBase &S) { @@ -1263,17 +1261,30 @@ ThunkSection *ThunkCreator::getISThunkSec(InputSection *IS) { // // We follow a simple but conservative heuristic to place ThunkSections at // offsets that are multiples of a Target specific branch range. -// For an InputSectionRange that is smaller than the range, a single +// For an InputSectionDescription that is smaller than the range, a single // ThunkSection at the end of the range will do. +// +// For an InputSectionDescription that is more than twice the size of the range, +// we place the last ThunkSection at range bytes from the end of the +// InputSectionDescription in order to increase the likelihood that the +// distance from a thunk to its target will be sufficiently small to +// allow for the creation of a short thunk. void ThunkCreator::createInitialThunkSections( ArrayRef<OutputSection *> OutputSections) { forEachInputSectionDescription( OutputSections, [&](OutputSection *OS, InputSectionDescription *ISD) { if (ISD->Sections.empty()) return; + uint32_t ISDBegin = ISD->Sections.front()->OutSecOff; + uint32_t ISDEnd = + ISD->Sections.back()->OutSecOff + ISD->Sections.back()->getSize(); + uint32_t LastThunkLowerBound = -1; + if (ISDEnd - ISDBegin > Target->ThunkSectionSpacing * 2) + LastThunkLowerBound = ISDEnd - Target->ThunkSectionSpacing; + uint32_t ISLimit; - uint32_t PrevISLimit = ISD->Sections.front()->OutSecOff; - uint32_t ThunkUpperBound = PrevISLimit + Target->ThunkSectionSpacing; + uint32_t PrevISLimit = ISDBegin; + uint32_t ThunkUpperBound = ISDBegin + Target->ThunkSectionSpacing; for (const InputSection *IS : ISD->Sections) { ISLimit = IS->OutSecOff + IS->getSize(); @@ -1281,6 +1292,8 @@ void ThunkCreator::createInitialThunkSections( addThunkSection(OS, ISD, PrevISLimit); ThunkUpperBound = PrevISLimit + Target->ThunkSectionSpacing; } + if (ISLimit > LastThunkLowerBound) + break; PrevISLimit = ISLimit; } addThunkSection(OS, ISD, ISLimit); @@ -1297,17 +1310,22 @@ ThunkSection *ThunkCreator::addThunkSection(OutputSection *OS, std::pair<Thunk *, bool> ThunkCreator::getThunk(Symbol &Sym, RelType Type, uint64_t Src) { - auto Res = ThunkedSymbols.insert({&Sym, std::vector<Thunk *>()}); - if (!Res.second) { - // Check existing Thunks for Sym to see if they can be reused - for (Thunk *ET : Res.first->second) - if (ET->isCompatibleWith(Type) && - Target->inBranchRange(Type, Src, ET->ThunkSym->getVA())) - return std::make_pair(ET, false); - } + std::vector<Thunk *> *ThunkVec = nullptr; + // We use (section, offset) pair to find the thunk position if possible so + // that we create only one thunk for aliased symbols or ICFed sections. + if (auto *D = dyn_cast<Defined>(&Sym)) + if (!D->isInPlt() && D->Section) + ThunkVec = &ThunkedSymbolsBySection[{D->Section->Repl, D->Value}]; + if (!ThunkVec) + ThunkVec = &ThunkedSymbols[&Sym]; + // Check existing Thunks for Sym to see if they can be reused + for (Thunk *ET : *ThunkVec) + if (ET->isCompatibleWith(Type) && + Target->inBranchRange(Type, Src, ET->getThunkTargetSym()->getVA())) + return std::make_pair(ET, false); // No existing compatible Thunk in range, create a new one Thunk *T = addThunk(Type, Sym); - Res.first->second.push_back(T); + ThunkVec->push_back(T); return std::make_pair(T, true); } @@ -1315,7 +1333,7 @@ std::pair<Thunk *, bool> ThunkCreator::getThunk(Symbol &Sym, RelType Type, // InputSectionDescription::Sections. void ThunkCreator::forEachInputSectionDescription( ArrayRef<OutputSection *> OutputSections, - std::function<void(OutputSection *, InputSectionDescription *)> Fn) { + llvm::function_ref<void(OutputSection *, InputSectionDescription *)> Fn) { for (OutputSection *OS : OutputSections) { if (!(OS->Flags & SHF_ALLOC) || !(OS->Flags & SHF_EXECINSTR)) continue; @@ -1383,7 +1401,7 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) { OutputSections, [&](OutputSection *OS, InputSectionDescription *ISD) { for (InputSection *IS : ISD->Sections) for (Relocation &Rel : IS->Relocations) { - uint64_t Src = OS->Addr + IS->OutSecOff + Rel.Offset; + uint64_t Src = IS->getVA(Rel.Offset); // If we are a relocation to an existing Thunk, check if it is // still in range. If not then Rel will be altered to point to its @@ -1398,7 +1416,6 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) { bool IsNew; std::tie(T, IsNew) = getThunk(*Rel.Sym, Rel.Type, Src); if (IsNew) { - AddressesChanged = true; // Find or create a ThunkSection for the new Thunk ThunkSection *TS; if (auto *TIS = T->getTargetInputSection()) @@ -1406,13 +1423,18 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) { else TS = getISDThunkSec(OS, IS, ISD, Rel.Type, Src); TS->addThunk(T); - Thunks[T->ThunkSym] = T; + Thunks[T->getThunkTargetSym()] = T; } // Redirect relocation to Thunk, we never go via the PLT to a Thunk - Rel.Sym = T->ThunkSym; + Rel.Sym = T->getThunkTargetSym(); Rel.Expr = fromPlt(Rel.Expr); } + for (auto &P : ISD->ThunkSections) + AddressesChanged |= P.first->assignOffsets(); }); + for (auto &P : ThunkedSections) + AddressesChanged |= P.second->assignOffsets(); + // Merge all created synthetic ThunkSections back into OutputSection mergeThunks(OutputSections); ++Pass; diff --git a/contrib/llvm/tools/lld/ELF/Relocations.h b/contrib/llvm/tools/lld/ELF/Relocations.h index 2cc8adfa5985..a4125111c4fe 100644 --- a/contrib/llvm/tools/lld/ELF/Relocations.h +++ b/contrib/llvm/tools/lld/ELF/Relocations.h @@ -21,7 +21,7 @@ class Symbol; class InputSection; class InputSectionBase; class OutputSection; -class OutputSection; +class SectionBase; // Represents a relocation type, such as R_X86_64_PC32 or R_ARM_THM_CALL. typedef uint32_t RelType; @@ -32,6 +32,7 @@ typedef uint32_t RelType; enum RelExpr { R_INVALID, R_ABS, + R_ADDEND, R_ARM_SBREL, R_GOT, R_GOTONLY_PC, @@ -58,27 +59,33 @@ enum RelExpr { R_PLT, R_PLT_PAGE_PC, R_PLT_PC, - R_PPC_OPD, - R_PPC_PLT_OPD, + R_PPC_CALL, + R_PPC_CALL_PLT, R_PPC_TOC, R_RELAX_GOT_PC, R_RELAX_GOT_PC_NOPIC, R_RELAX_TLS_GD_TO_IE, R_RELAX_TLS_GD_TO_IE_ABS, R_RELAX_TLS_GD_TO_IE_END, + R_RELAX_TLS_GD_TO_IE_GOT_OFF, R_RELAX_TLS_GD_TO_IE_PAGE_PC, R_RELAX_TLS_GD_TO_LE, R_RELAX_TLS_GD_TO_LE_NEG, R_RELAX_TLS_IE_TO_LE, R_RELAX_TLS_LD_TO_LE, + R_RELAX_TLS_LD_TO_LE_ABS, R_SIZE, R_TLS, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PAGE, - R_TLSGD, + R_TLSGD_GOT, + R_TLSGD_GOT_FROM_END, R_TLSGD_PC, - R_TLSLD, + R_TLSLD_GOT, + R_TLSLD_GOT_FROM_END, + R_TLSLD_GOT_OFF, + R_TLSLD_HINT, R_TLSLD_PC, }; @@ -150,7 +157,7 @@ private: void forEachInputSectionDescription( ArrayRef<OutputSection *> OutputSections, - std::function<void(OutputSection *, InputSectionDescription *)> Fn); + llvm::function_ref<void(OutputSection *, InputSectionDescription *)> Fn); std::pair<Thunk *, bool> getThunk(Symbol &Sym, RelType Type, uint64_t Src); @@ -160,6 +167,8 @@ private: bool normalizeExistingThunk(Relocation &Rel, uint64_t Src); // Record all the available Thunks for a Symbol + llvm::DenseMap<std::pair<SectionBase *, uint64_t>, std::vector<Thunk *>> + ThunkedSymbolsBySection; llvm::DenseMap<Symbol *, std::vector<Thunk *>> ThunkedSymbols; // Find a Thunk from the Thunks symbol definition, we can use this to find diff --git a/contrib/llvm/tools/lld/ELF/ScriptLexer.cpp b/contrib/llvm/tools/lld/ELF/ScriptLexer.cpp index ef5a1cff7590..d4b1f6d99cc1 100644 --- a/contrib/llvm/tools/lld/ELF/ScriptLexer.cpp +++ b/contrib/llvm/tools/lld/ELF/ScriptLexer.cpp @@ -66,8 +66,6 @@ size_t ScriptLexer::getColumnNumber() { std::string ScriptLexer::getCurrentLocation() { std::string Filename = getCurrentMB().getBufferIdentifier(); - if (!Pos) - return Filename; return (Filename + ":" + Twine(getLineNumber())).str(); } @@ -116,8 +114,9 @@ void ScriptLexer::tokenize(MemoryBufferRef MB) { } // ">foo" is parsed to ">" and "foo", but ">>" is parsed to ">>". + // "|", "||", "&" and "&&" are different operators. if (S.startswith("<<") || S.startswith("<=") || S.startswith(">>") || - S.startswith(">=")) { + S.startswith(">=") || S.startswith("||") || S.startswith("&&")) { Vec.push_back(S.substr(0, 2)); S = S.substr(2); continue; @@ -282,10 +281,7 @@ static bool encloses(StringRef S, StringRef T) { MemoryBufferRef ScriptLexer::getCurrentMB() { // Find input buffer containing the current token. - assert(!MBs.empty()); - if (!Pos) - return MBs[0]; - + assert(!MBs.empty() && Pos > 0); for (MemoryBufferRef MB : MBs) if (encloses(MB.getBuffer(), Tokens[Pos - 1])) return MB; diff --git a/contrib/llvm/tools/lld/ELF/ScriptParser.cpp b/contrib/llvm/tools/lld/ELF/ScriptParser.cpp index 3d2606db8d06..ddb4a49a3e5e 100644 --- a/contrib/llvm/tools/lld/ELF/ScriptParser.cpp +++ b/contrib/llvm/tools/lld/ELF/ScriptParser.cpp @@ -63,6 +63,7 @@ private: void readExtern(); void readGroup(); void readInclude(); + void readInput(); void readMemory(); void readOutput(); void readOutputArch(); @@ -74,12 +75,14 @@ private: void readVersion(); void readVersionScriptCommand(); - SymbolAssignment *readAssignment(StringRef Name); + SymbolAssignment *readSymbolAssignment(StringRef Name); ByteCommand *readByteCommand(StringRef Tok); uint32_t readFill(); uint32_t parseFill(StringRef Tok); void readSectionAddressType(OutputSection *Cmd); + OutputSection *readOverlaySectionDescription(); OutputSection *readOutputSectionDescription(StringRef OutSec); + std::vector<BaseCommand *> readOverlay(); std::vector<StringRef> readOutputSectionPhdrs(); InputSectionDescription *readInputSectionDescription(StringRef Tok); StringMatcher readFilePatterns(); @@ -88,16 +91,16 @@ private: unsigned readPhdrType(); SortSectionPolicy readSortKind(); SymbolAssignment *readProvideHidden(bool Provide, bool Hidden); - SymbolAssignment *readProvideOrAssignment(StringRef Tok); + SymbolAssignment *readAssignment(StringRef Tok); void readSort(); - AssertCommand *readAssert(); - Expr readAssertExpr(); + Expr readAssert(); Expr readConstant(); Expr getPageSize(); uint64_t readMemoryAssignment(StringRef, StringRef, StringRef); std::pair<uint32_t, uint32_t> readMemoryAttributes(); + Expr combine(StringRef Op, Expr L, Expr R); Expr readExpr(); Expr readExpr1(Expr Lhs, int MinPrec); StringRef readParenLiteral(); @@ -157,17 +160,6 @@ static ExprValue sub(ExprValue A, ExprValue B) { return {A.Sec, false, A.getSectionOffset() - B.getValue(), A.Loc}; } -static ExprValue mul(ExprValue A, ExprValue B) { - return A.getValue() * B.getValue(); -} - -static ExprValue div(ExprValue A, ExprValue B) { - if (uint64_t BV = B.getValue()) - return A.getValue() / BV; - error("division by zero"); - return 0; -} - static ExprValue bitAnd(ExprValue A, ExprValue B) { moveAbsRight(A, B); return {A.Sec, A.ForceAbsolute, @@ -237,16 +229,16 @@ void ScriptParser::readLinkerScript() { if (Tok == ";") continue; - if (Tok == "ASSERT") { - Script->SectionCommands.push_back(readAssert()); - } else if (Tok == "ENTRY") { + if (Tok == "ENTRY") { readEntry(); } else if (Tok == "EXTERN") { readExtern(); - } else if (Tok == "GROUP" || Tok == "INPUT") { + } else if (Tok == "GROUP") { readGroup(); } else if (Tok == "INCLUDE") { readInclude(); + } else if (Tok == "INPUT") { + readInput(); } else if (Tok == "MEMORY") { readMemory(); } else if (Tok == "OUTPUT") { @@ -265,7 +257,7 @@ void ScriptParser::readLinkerScript() { readSections(); } else if (Tok == "VERSION") { readVersion(); - } else if (SymbolAssignment *Cmd = readProvideOrAssignment(Tok)) { + } else if (SymbolAssignment *Cmd = readAssignment(Tok)) { Script->SectionCommands.push_back(Cmd); } else { setError("unknown directive: " + Tok); @@ -336,13 +328,12 @@ void ScriptParser::readExtern() { } void ScriptParser::readGroup() { - expect("("); - while (!errorCount() && !consume(")")) { - if (consume("AS_NEEDED")) - readAsNeeded(); - else - addFile(unquote(next())); - } + bool Orig = InputFile::IsInGroup; + InputFile::IsInGroup = true; + readInput(); + InputFile::IsInGroup = Orig; + if (!Orig) + ++InputFile::NextGroupId; } void ScriptParser::readInclude() { @@ -353,7 +344,7 @@ void ScriptParser::readInclude() { return; } - if (Optional<std::string> Path = searchLinkerScript(Tok)) { + if (Optional<std::string> Path = searchScript(Tok)) { if (Optional<MemoryBufferRef> MB = readFile(*Path)) tokenize(*MB); return; @@ -361,6 +352,16 @@ void ScriptParser::readInclude() { setError("cannot find linker script " + Tok); } +void ScriptParser::readInput() { + expect("("); + while (!errorCount() && !consume(")")) { + if (consume("AS_NEEDED")) + readAsNeeded(); + else + addFile(unquote(next())); + } +} + void ScriptParser::readOutput() { // -o <file> takes predecence over OUTPUT(<file>). expect("("); @@ -437,6 +438,49 @@ void ScriptParser::readSearchDir() { expect(")"); } +// This reads an overlay description. Overlays are used to describe output +// sections that use the same virtual memory range and normally would trigger +// linker's sections sanity check failures. +// https://sourceware.org/binutils/docs/ld/Overlay-Description.html#Overlay-Description +std::vector<BaseCommand *> ScriptParser::readOverlay() { + // VA and LMA expressions are optional, though for simplicity of + // implementation we assume they are not. That is what OVERLAY was designed + // for first of all: to allow sections with overlapping VAs at different LMAs. + Expr AddrExpr = readExpr(); + expect(":"); + expect("AT"); + Expr LMAExpr = readParenExpr(); + expect("{"); + + std::vector<BaseCommand *> V; + OutputSection *Prev = nullptr; + while (!errorCount() && !consume("}")) { + // VA is the same for all sections. The LMAs are consecutive in memory + // starting from the base load address specified. + OutputSection *OS = readOverlaySectionDescription(); + OS->AddrExpr = AddrExpr; + if (Prev) + OS->LMAExpr = [=] { return Prev->getLMA() + Prev->Size; }; + else + OS->LMAExpr = LMAExpr; + V.push_back(OS); + Prev = OS; + } + + // According to the specification, at the end of the overlay, the location + // counter should be equal to the overlay base address plus size of the + // largest section seen in the overlay. + // Here we want to create the Dot assignment command to achieve that. + Expr MoveDot = [=] { + uint64_t Max = 0; + for (BaseCommand *Cmd : V) + Max = std::max(Max, cast<OutputSection>(Cmd)->Size); + return AddrExpr().getValue() + Max; + }; + V.push_back(make<SymbolAssignment>(".", MoveDot, getCurrentLocation())); + return V; +} + void ScriptParser::readSections() { Script->HasSectionsCommand = true; @@ -446,26 +490,48 @@ void ScriptParser::readSections() { Config->SingleRoRx = true; expect("{"); + std::vector<BaseCommand *> V; while (!errorCount() && !consume("}")) { StringRef Tok = next(); - BaseCommand *Cmd = readProvideOrAssignment(Tok); - if (!Cmd) { - if (Tok == "ASSERT") - Cmd = readAssert(); - else - Cmd = readOutputSectionDescription(Tok); + if (Tok == "OVERLAY") { + for (BaseCommand *Cmd : readOverlay()) + V.push_back(Cmd); + continue; } - Script->SectionCommands.push_back(Cmd); + + if (BaseCommand *Cmd = readAssignment(Tok)) + V.push_back(Cmd); + else + V.push_back(readOutputSectionDescription(Tok)); + } + + if (!atEOF() && consume("INSERT")) { + std::vector<BaseCommand *> *Dest = nullptr; + if (consume("AFTER")) + Dest = &Script->InsertAfterCommands[next()]; + else if (consume("BEFORE")) + Dest = &Script->InsertBeforeCommands[next()]; + else + setError("expected AFTER/BEFORE, but got '" + next() + "'"); + if (Dest) + Dest->insert(Dest->end(), V.begin(), V.end()); + return; } + + Script->SectionCommands.insert(Script->SectionCommands.end(), V.begin(), + V.end()); } static int precedence(StringRef Op) { return StringSwitch<int>(Op) - .Cases("*", "/", 5) - .Cases("+", "-", 4) - .Cases("<<", ">>", 3) - .Cases("<", "<=", ">", ">=", "==", "!=", 2) - .Cases("&", "|", 1) + .Cases("*", "/", "%", 8) + .Cases("+", "-", 7) + .Cases("<<", ">>", 6) + .Cases("<", "<=", ">", ">=", "==", "!=", 5) + .Case("&", 4) + .Case("|", 3) + .Case("&&", 2) + .Case("||", 1) .Default(-1); } @@ -588,11 +654,7 @@ void ScriptParser::readSort() { expect(")"); } -AssertCommand *ScriptParser::readAssert() { - return make<AssertCommand>(readAssertExpr()); -} - -Expr ScriptParser::readAssertExpr() { +Expr ScriptParser::readAssert() { expect("("); Expr E = readExpr(); expect(","); @@ -617,12 +679,14 @@ uint32_t ScriptParser::readFill() { return V; } -// Reads an expression and/or the special directive "(NOLOAD)" for an -// output section definition. +// Reads an expression and/or the special directive for an output +// section definition. Directive is one of following: "(NOLOAD)", +// "(COPY)", "(INFO)" or "(OVERLAY)". // // An output section name can be followed by an address expression -// and/or by "(NOLOAD)". This grammar is not LL(1) because "(" can be -// interpreted as either the beginning of some expression or "(NOLOAD)". +// and/or directive. This grammar is not LL(1) because "(" can be +// interpreted as either the beginning of some expression or beginning +// of directive. // // https://sourceware.org/binutils/docs/ld/Output-Section-Address.html // https://sourceware.org/binutils/docs/ld/Output-Section-Type.html @@ -633,6 +697,11 @@ void ScriptParser::readSectionAddressType(OutputSection *Cmd) { Cmd->Noload = true; return; } + if (consume("COPY") || consume("INFO") || consume("OVERLAY")) { + expect(")"); + Cmd->NonAlloc = true; + return; + } Cmd->AddrExpr = readExpr(); expect(")"); } else { @@ -657,10 +726,23 @@ static Expr checkAlignment(Expr E, std::string &Loc) { }; } +OutputSection *ScriptParser::readOverlaySectionDescription() { + OutputSection *Cmd = + Script->createOutputSection(next(), getCurrentLocation()); + Cmd->InOverlay = true; + expect("{"); + while (!errorCount() && !consume("}")) + Cmd->SectionCommands.push_back(readInputSectionRules(next())); + Cmd->Phdrs = readOutputSectionPhdrs(); + return Cmd; +} + OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) { OutputSection *Cmd = Script->createOutputSection(OutSec, getCurrentLocation()); + size_t SymbolsReferenced = Script->ReferencedSymbols.size(); + if (peek() != ":") readSectionAddressType(Cmd); expect(":"); @@ -684,13 +766,10 @@ OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) { StringRef Tok = next(); if (Tok == ";") { // Empty commands are allowed. Do nothing here. - } else if (SymbolAssignment *Assign = readProvideOrAssignment(Tok)) { + } else if (SymbolAssignment *Assign = readAssignment(Tok)) { Cmd->SectionCommands.push_back(Assign); } else if (ByteCommand *Data = readByteCommand(Tok)) { Cmd->SectionCommands.push_back(Data); - } else if (Tok == "ASSERT") { - Cmd->SectionCommands.push_back(readAssert()); - expect(";"); } else if (Tok == "CONSTRUCTORS") { // CONSTRUCTORS is a keyword to make the linker recognize C++ ctors/dtors // by name. This is for very old file formats such as ECOFF/XCOFF. @@ -727,6 +806,8 @@ OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) { // Consume optional comma following output section command. consume(","); + if (Script->ReferencedSymbols.size() > SymbolsReferenced) + Cmd->ExpressionsUseSymbols = true; return Cmd; } @@ -749,30 +830,39 @@ uint32_t ScriptParser::parseFill(StringRef Tok) { SymbolAssignment *ScriptParser::readProvideHidden(bool Provide, bool Hidden) { expect("("); - SymbolAssignment *Cmd = readAssignment(next()); + SymbolAssignment *Cmd = readSymbolAssignment(next()); Cmd->Provide = Provide; Cmd->Hidden = Hidden; expect(")"); - expect(";"); return Cmd; } -SymbolAssignment *ScriptParser::readProvideOrAssignment(StringRef Tok) { +SymbolAssignment *ScriptParser::readAssignment(StringRef Tok) { + // Assert expression returns Dot, so this is equal to ".=." + if (Tok == "ASSERT") + return make<SymbolAssignment>(".", readAssert(), getCurrentLocation()); + + size_t OldPos = Pos; SymbolAssignment *Cmd = nullptr; - if (peek() == "=" || peek() == "+=") { - Cmd = readAssignment(Tok); - expect(";"); - } else if (Tok == "PROVIDE") { + if (peek() == "=" || peek() == "+=") + Cmd = readSymbolAssignment(Tok); + else if (Tok == "PROVIDE") Cmd = readProvideHidden(true, false); - } else if (Tok == "HIDDEN") { + else if (Tok == "HIDDEN") Cmd = readProvideHidden(false, true); - } else if (Tok == "PROVIDE_HIDDEN") { + else if (Tok == "PROVIDE_HIDDEN") Cmd = readProvideHidden(true, true); + + if (Cmd) { + Cmd->CommandString = + Tok.str() + " " + + llvm::join(Tokens.begin() + OldPos, Tokens.begin() + Pos, " "); + expect(";"); } return Cmd; } -SymbolAssignment *ScriptParser::readAssignment(StringRef Name) { +SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef Name) { StringRef Op = next(); assert(Op == "=" || Op == "+="); Expr E = readExpr(); @@ -795,15 +885,31 @@ Expr ScriptParser::readExpr() { return E; } -static Expr combine(StringRef Op, Expr L, Expr R) { +Expr ScriptParser::combine(StringRef Op, Expr L, Expr R) { if (Op == "+") return [=] { return add(L(), R()); }; if (Op == "-") return [=] { return sub(L(), R()); }; if (Op == "*") - return [=] { return mul(L(), R()); }; - if (Op == "/") - return [=] { return div(L(), R()); }; + return [=] { return L().getValue() * R().getValue(); }; + if (Op == "/") { + std::string Loc = getCurrentLocation(); + return [=]() -> uint64_t { + if (uint64_t RV = R().getValue()) + return L().getValue() / RV; + error(Loc + ": division by zero"); + return 0; + }; + } + if (Op == "%") { + std::string Loc = getCurrentLocation(); + return [=]() -> uint64_t { + if (uint64_t RV = R().getValue()) + return L().getValue() % RV; + error(Loc + ": modulo by zero"); + return 0; + }; + } if (Op == "<<") return [=] { return L().getValue() << R().getValue(); }; if (Op == ">>") @@ -820,6 +926,10 @@ static Expr combine(StringRef Op, Expr L, Expr R) { return [=] { return L().getValue() == R().getValue(); }; if (Op == "!=") return [=] { return L().getValue() != R().getValue(); }; + if (Op == "||") + return [=] { return L().getValue() || R().getValue(); }; + if (Op == "&&") + return [=] { return L().getValue() && R().getValue(); }; if (Op == "&") return [=] { return bitAnd(L(), R()); }; if (Op == "|") @@ -873,20 +983,13 @@ Expr ScriptParser::readConstant() { if (S == "MAXPAGESIZE") return [] { return Config->MaxPageSize; }; setError("unknown constant: " + S); - return {}; + return [] { return 0; }; } // Parses Tok as an integer. It recognizes hexadecimal (prefixed with // "0x" or suffixed with "H") and decimal numbers. Decimal numbers may // have "K" (Ki) or "M" (Mi) suffixes. static Optional<uint64_t> parseInt(StringRef Tok) { - // Negative number - if (Tok.startswith("-")) { - if (Optional<uint64_t> Val = parseInt(Tok.substr(1))) - return -*Val; - return None; - } - // Hexadecimal uint64_t Val; if (Tok.startswith_lower("0x")) { @@ -925,7 +1028,13 @@ ByteCommand *ScriptParser::readByteCommand(StringRef Tok) { .Default(-1); if (Size == -1) return nullptr; - return make<ByteCommand>(readParenExpr(), Size); + + size_t OldPos = Pos; + Expr E = readParenExpr(); + std::string CommandString = + Tok.str() + " " + + llvm::join(Tokens.begin() + OldPos, Tokens.begin() + Pos, " "); + return make<ByteCommand>(E, Size, CommandString); } StringRef ScriptParser::readParenLiteral() { @@ -1006,7 +1115,7 @@ Expr ScriptParser::readPrimary() { }; } if (Tok == "ASSERT") - return readAssertExpr(); + return readAssert(); if (Tok == "CONSTANT") return readConstant(); if (Tok == "DATA_SEGMENT_ALIGN") { @@ -1043,8 +1152,10 @@ Expr ScriptParser::readPrimary() { } if (Tok == "LENGTH") { StringRef Name = readParenLiteral(); - if (Script->MemoryRegions.count(Name) == 0) + if (Script->MemoryRegions.count(Name) == 0) { setError("memory region not defined: " + Name); + return [] { return 0; }; + } return [=] { return Script->MemoryRegions[Name]->Length; }; } if (Tok == "LOADADDR") { @@ -1055,10 +1166,22 @@ Expr ScriptParser::readPrimary() { return Cmd->getLMA(); }; } + if (Tok == "MAX" || Tok == "MIN") { + expect("("); + Expr A = readExpr(); + expect(","); + Expr B = readExpr(); + expect(")"); + if (Tok == "MIN") + return [=] { return std::min(A().getValue(), B().getValue()); }; + return [=] { return std::max(A().getValue(), B().getValue()); }; + } if (Tok == "ORIGIN") { StringRef Name = readParenLiteral(); - if (Script->MemoryRegions.count(Name) == 0) + if (Script->MemoryRegions.count(Name) == 0) { setError("memory region not defined: " + Name); + return [] { return 0; }; + } return [=] { return Script->MemoryRegions[Name]->Origin; }; } if (Tok == "SEGMENT_START") { @@ -1240,6 +1363,9 @@ ScriptParser::readSymbols() { // Reads an "extern C++" directive, e.g., // "extern "C++" { ns::*; "f(int, double)"; };" +// +// The last semicolon is optional. E.g. this is OK: +// "extern "C++" { ns::*; "f(int, double)" };" std::vector<SymbolVersion> ScriptParser::readVersionExtern() { StringRef Tok = next(); bool IsCXX = Tok == "\"C++\""; @@ -1252,6 +1378,8 @@ std::vector<SymbolVersion> ScriptParser::readVersionExtern() { StringRef Tok = next(); bool HasWildcard = !Tok.startswith("\"") && hasWildcard(Tok); Ret.push_back({unquote(Tok), IsCXX, HasWildcard}); + if (consume("}")) + return Ret; expect(";"); } @@ -1291,11 +1419,10 @@ void ScriptParser::readMemory() { uint64_t Length = readMemoryAssignment("LENGTH", "len", "l"); // Add the memory region to the region map. - if (Script->MemoryRegions.count(Name)) - setError("region '" + Name + "' already defined"); MemoryRegion *MR = make<MemoryRegion>(Name, Origin, Length, Flags, NegFlags); - Script->MemoryRegions[Name] = MR; + if (!Script->MemoryRegions.insert({Name, MR}).second) + setError("region '" + Name + "' already defined"); } } diff --git a/contrib/llvm/tools/lld/ELF/Strings.cpp b/contrib/llvm/tools/lld/ELF/Strings.cpp deleted file mode 100644 index 0ef33a14bc3d..000000000000 --- a/contrib/llvm/tools/lld/ELF/Strings.cpp +++ /dev/null @@ -1,62 +0,0 @@ -//===- Strings.cpp -------------------------------------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "Strings.h" -#include "Config.h" -#include "lld/Common/ErrorHandler.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/Twine.h" -#include "llvm/Demangle/Demangle.h" -#include <algorithm> -#include <cstring> - -using namespace llvm; -using namespace lld; -using namespace lld::elf; - -StringMatcher::StringMatcher(ArrayRef<StringRef> Pat) { - for (StringRef S : Pat) { - Expected<GlobPattern> Pat = GlobPattern::create(S); - if (!Pat) - error(toString(Pat.takeError())); - else - Patterns.push_back(*Pat); - } -} - -bool StringMatcher::match(StringRef S) const { - for (const GlobPattern &Pat : Patterns) - if (Pat.match(S)) - return true; - return false; -} - -// Converts a hex string (e.g. "deadbeef") to a vector. -std::vector<uint8_t> elf::parseHex(StringRef S) { - std::vector<uint8_t> Hex; - while (!S.empty()) { - StringRef B = S.substr(0, 2); - S = S.substr(2); - uint8_t H; - if (!to_integer(B, H, 16)) { - error("not a hexadecimal value: " + B); - return {}; - } - Hex.push_back(H); - } - return Hex; -} - -// Returns true if S is valid as a C language identifier. -bool elf::isValidCIdentifier(StringRef S) { - return !S.empty() && (isAlpha(S[0]) || S[0] == '_') && - std::all_of(S.begin() + 1, S.end(), - [](char C) { return C == '_' || isAlnum(C); }); -} diff --git a/contrib/llvm/tools/lld/ELF/Strings.h b/contrib/llvm/tools/lld/ELF/Strings.h deleted file mode 100644 index 5009df65f4c1..000000000000 --- a/contrib/llvm/tools/lld/ELF/Strings.h +++ /dev/null @@ -1,75 +0,0 @@ -//===- Strings.h ------------------------------------------------*- C++ -*-===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLD_ELF_STRINGS_H -#define LLD_ELF_STRINGS_H - -#include "lld/Common/LLVM.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/BitVector.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/GlobPattern.h" -#include <vector> - -namespace lld { -namespace elf { - -std::vector<uint8_t> parseHex(StringRef S); -bool isValidCIdentifier(StringRef S); - -// This is a lazy version of StringRef. String size is computed lazily -// when it is needed. It is more efficient than StringRef to instantiate -// if you have a string whose size is unknown. -// -// ELF string tables contain a lot of null-terminated strings. -// Most of them are not necessary for the linker because they are names -// of local symbols and the linker doesn't use local symbol names for -// name resolution. So, we use this class to represents strings read -// from string tables. -class StringRefZ { -public: - StringRefZ() : Start(nullptr), Size(0) {} - StringRefZ(const char *S, size_t Size) : Start(S), Size(Size) {} - - /*implicit*/ StringRefZ(const char *S) : Start(S), Size(-1) {} - - /*implicit*/ StringRefZ(llvm::StringRef S) - : Start(S.data()), Size(S.size()) {} - - operator llvm::StringRef() const { - if (Size == (size_t)-1) - Size = strlen(Start); - return {Start, Size}; - } - -private: - const char *Start; - mutable size_t Size; -}; - -// This class represents multiple glob patterns. -class StringMatcher { -public: - StringMatcher() = default; - explicit StringMatcher(ArrayRef<StringRef> Pat); - - bool match(StringRef S) const; - -private: - std::vector<llvm::GlobPattern> Patterns; -}; - -inline ArrayRef<uint8_t> toArrayRef(StringRef S) { - return {(const uint8_t *)S.data(), S.size()}; -} -} // namespace elf -} // namespace lld - -#endif diff --git a/contrib/llvm/tools/lld/ELF/SymbolTable.cpp b/contrib/llvm/tools/lld/ELF/SymbolTable.cpp index c41e2b2de748..1f5a84ec2c7d 100644 --- a/contrib/llvm/tools/lld/ELF/SymbolTable.cpp +++ b/contrib/llvm/tools/lld/ELF/SymbolTable.cpp @@ -38,7 +38,7 @@ static InputFile *getFirstElf() { return ObjectFiles[0]; if (!SharedFiles.empty()) return SharedFiles[0]; - return nullptr; + return BitcodeFiles[0]; } // All input object files must be for the same architecture @@ -82,6 +82,7 @@ template <class ELFT> void SymbolTable::addFile(InputFile *File) { // Lazy object file if (auto *F = dyn_cast<LazyObjFile>(File)) { + LazyObjFiles.push_back(F); F->parse<ELFT>(); return; } @@ -117,7 +118,7 @@ template <class ELFT> void SymbolTable::addFile(InputFile *File) { // not in native object file format but in the LLVM bitcode format. // This function compiles bitcode files into a few big native files // using LLVM functions and replaces bitcode symbols with the results. -// Because all bitcode files that consist of a program are passed +// Because all bitcode files that the program consists of are passed // to the compiler at once, it can do whole-program optimization. template <class ELFT> void SymbolTable::addCombinedLTOObject() { if (BitcodeFiles.empty()) @@ -157,6 +158,12 @@ template <class ELFT> void SymbolTable::addSymbolWrap(StringRef Name) { Symbol *Sym = find(Name); if (!Sym) return; + + // Do not wrap the same symbol twice. + for (const WrappedSymbol &S : WrappedSymbols) + if (S.Sym == Sym) + return; + Symbol *Real = addUndefined<ELFT>(Saver.save("__real_" + Name)); Symbol *Wrap = addUndefined<ELFT>(Saver.save("__wrap_" + Name)); WrappedSymbols.push_back({Sym, Real, Wrap}); @@ -189,7 +196,7 @@ void SymbolTable::applySymbolWrap() { // First, make a copy of __real_sym. Symbol *Real = nullptr; if (W.Real->isDefined()) { - Real = (Symbol *)make<SymbolUnion>(); + Real = reinterpret_cast<Symbol *>(make<SymbolUnion>()); memcpy(Real, W.Real, sizeof(SymbolUnion)); } @@ -237,8 +244,7 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) { Symbol *Sym; if (IsNew) { - Sym = (Symbol *)make<SymbolUnion>(); - Sym->InVersionScript = false; + Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>()); Sym->Visibility = STV_DEFAULT; Sym->IsUsedInRegularObj = false; Sym->ExportDynamic = false; @@ -297,26 +303,88 @@ Symbol *SymbolTable::addUndefined(StringRef Name, uint8_t Binding, uint8_t Visibility = getVisibility(StOther); std::tie(S, WasInserted) = insert(Name, Type, Visibility, CanOmitFromDynSym, File); + // An undefined symbol with non default visibility must be satisfied // in the same DSO. if (WasInserted || (isa<SharedSymbol>(S) && Visibility != STV_DEFAULT)) { replaceSymbol<Undefined>(S, File, Name, Binding, StOther, Type); return S; } + if (S->isShared() || S->isLazy() || (S->isUndefined() && Binding != STB_WEAK)) S->Binding = Binding; - if (Binding != STB_WEAK) { + + if (!Config->GcSections && Binding != STB_WEAK) if (auto *SS = dyn_cast<SharedSymbol>(S)) - if (!Config->GcSections) - SS->getFile<ELFT>().IsNeeded = true; - } - if (auto *L = dyn_cast<Lazy>(S)) { + SS->getFile<ELFT>().IsNeeded = true; + + if (S->isLazy()) { // An undefined weak will not fetch archive members. See comment on Lazy in // Symbols.h for the details. - if (Binding == STB_WEAK) - L->Type = Type; - else if (InputFile *F = L->fetch()) - addFile<ELFT>(F); + if (Binding == STB_WEAK) { + S->Type = Type; + return S; + } + + // Do extra check for --warn-backrefs. + // + // --warn-backrefs is an option to prevent an undefined reference from + // fetching an archive member written earlier in the command line. It can be + // used to keep compatibility with GNU linkers to some degree. + // I'll explain the feature and why you may find it useful in this comment. + // + // lld's symbol resolution semantics is more relaxed than traditional Unix + // linkers. For example, + // + // ld.lld foo.a bar.o + // + // succeeds even if bar.o contains an undefined symbol that has to be + // resolved by some object file in foo.a. Traditional Unix linkers don't + // allow this kind of backward reference, as they visit each file only once + // from left to right in the command line while resolving all undefined + // symbols at the moment of visiting. + // + // In the above case, since there's no undefined symbol when a linker visits + // foo.a, no files are pulled out from foo.a, and because the linker forgets + // about foo.a after visiting, it can't resolve undefined symbols in bar.o + // that could have been resolved otherwise. + // + // That lld accepts more relaxed form means that (besides it'd make more + // sense) you can accidentally write a command line or a build file that + // works only with lld, even if you have a plan to distribute it to wider + // users who may be using GNU linkers. With --warn-backrefs, you can detect + // a library order that doesn't work with other Unix linkers. + // + // The option is also useful to detect cyclic dependencies between static + // archives. Again, lld accepts + // + // ld.lld foo.a bar.a + // + // even if foo.a and bar.a depend on each other. With --warn-backrefs, it is + // handled as an error. + // + // Here is how the option works. We assign a group ID to each file. A file + // with a smaller group ID can pull out object files from an archive file + // with an equal or greater group ID. Otherwise, it is a reverse dependency + // and an error. + // + // A file outside --{start,end}-group gets a fresh ID when instantiated. All + // files within the same --{start,end}-group get the same group ID. E.g. + // + // ld.lld A B --start-group C D --end-group E + // + // A forms group 0. B form group 1. C and D (including their member object + // files) form group 2. E forms group 3. I think that you can see how this + // group assignment rule simulates the traditional linker's semantics. + bool Backref = + Config->WarnBackrefs && File && S->File->GroupId < File->GroupId; + fetchLazy<ELFT>(S); + + // We don't report backward references to weak symbols as they can be + // overridden later. + if (Backref && S->Binding != STB_WEAK) + warn("backward reference detected: " + Name + " in " + toString(File) + + " refers to " + toString(S->File)); } return S; } @@ -384,7 +452,11 @@ Symbol *SymbolTable::addCommon(StringRef N, uint64_t Size, uint32_t Alignment, bool WasInserted; std::tie(S, WasInserted) = insert(N, Type, getVisibility(StOther), /*CanOmitFromDynSym*/ false, &File); + int Cmp = compareDefined(S, WasInserted, Binding, N); + if (Cmp < 0) + return S; + if (Cmp > 0) { auto *Bss = make<BssSection>("COMMON", Size, Alignment); Bss->File = &File; @@ -392,45 +464,43 @@ Symbol *SymbolTable::addCommon(StringRef N, uint64_t Size, uint32_t Alignment, InputSections.push_back(Bss); replaceSymbol<Defined>(S, &File, N, Binding, StOther, Type, 0, Size, Bss); - } else if (Cmp == 0) { - auto *D = cast<Defined>(S); - auto *Bss = dyn_cast_or_null<BssSection>(D->Section); - if (!Bss) { - // Non-common symbols take precedence over common symbols. - if (Config->WarnCommon) - warn("common " + S->getName() + " is overridden"); - return S; - } + return S; + } + auto *D = cast<Defined>(S); + auto *Bss = dyn_cast_or_null<BssSection>(D->Section); + if (!Bss) { + // Non-common symbols take precedence over common symbols. if (Config->WarnCommon) - warn("multiple common of " + D->getName()); + warn("common " + S->getName() + " is overridden"); + return S; + } - Bss->Alignment = std::max(Bss->Alignment, Alignment); - if (Size > Bss->Size) { - D->File = Bss->File = &File; - D->Size = Bss->Size = Size; - } + if (Config->WarnCommon) + warn("multiple common of " + D->getName()); + + Bss->Alignment = std::max(Bss->Alignment, Alignment); + if (Size > Bss->Size) { + D->File = Bss->File = &File; + D->Size = Bss->Size = Size; } return S; } -static void warnOrError(const Twine &Msg) { - if (Config->AllowMultipleDefinition) - warn(Msg); - else - error(Msg); -} - static void reportDuplicate(Symbol *Sym, InputFile *NewFile) { - warnOrError("duplicate symbol: " + toString(*Sym) + "\n>>> defined in " + - toString(Sym->File) + "\n>>> defined in " + toString(NewFile)); + if (!Config->AllowMultipleDefinition) + error("duplicate symbol: " + toString(*Sym) + "\n>>> defined in " + + toString(Sym->File) + "\n>>> defined in " + toString(NewFile)); } -static void reportDuplicate(Symbol *Sym, InputSectionBase *ErrSec, - uint64_t ErrOffset) { +static void reportDuplicate(Symbol *Sym, InputFile *NewFile, + InputSectionBase *ErrSec, uint64_t ErrOffset) { + if (Config->AllowMultipleDefinition) + return; + Defined *D = cast<Defined>(Sym); if (!D->Section || !ErrSec) { - reportDuplicate(Sym, ErrSec ? ErrSec->File : nullptr); + reportDuplicate(Sym, NewFile); return; } @@ -454,7 +524,7 @@ static void reportDuplicate(Symbol *Sym, InputSectionBase *ErrSec, if (!Src2.empty()) Msg += Src2 + "\n>>> "; Msg += Obj2; - warnOrError(Msg); + error(Msg); } Symbol *SymbolTable::addRegular(StringRef Name, uint8_t StOther, uint8_t Type, @@ -470,7 +540,8 @@ Symbol *SymbolTable::addRegular(StringRef Name, uint8_t StOther, uint8_t Type, replaceSymbol<Defined>(S, File, Name, Binding, StOther, Type, Value, Size, Section); else if (Cmp == 0) - reportDuplicate(S, dyn_cast_or_null<InputSectionBase>(Section), Value); + reportDuplicate(S, File, dyn_cast_or_null<InputSectionBase>(Section), + Value); return S; } @@ -491,8 +562,8 @@ void SymbolTable::addShared(StringRef Name, SharedFile<ELFT> &File, // An undefined symbol with non default visibility must be satisfied // in the same DSO. - if (WasInserted || ((S->isUndefined() || S->isLazy()) && - S->getVisibility() == STV_DEFAULT)) { + if (WasInserted || + ((S->isUndefined() || S->isLazy()) && S->Visibility == STV_DEFAULT)) { uint8_t Binding = S->Binding; bool WasUndefined = S->isUndefined(); replaceSymbol<SharedSymbol>(S, File, Name, Sym.getBinding(), Sym.st_other, @@ -531,85 +602,58 @@ Symbol *SymbolTable::find(StringRef Name) { return SymVector[It->second]; } -template <class ELFT> -Symbol *SymbolTable::addLazyArchive(StringRef Name, ArchiveFile &F, - const object::Archive::Symbol Sym) { +// This is used to handle lazy symbols. May replace existent +// symbol with lazy version or request to Fetch it. +template <class ELFT, typename LazyT, typename... ArgT> +static void replaceOrFetchLazy(StringRef Name, InputFile &File, + llvm::function_ref<InputFile *()> Fetch, + ArgT &&... Arg) { Symbol *S; bool WasInserted; - std::tie(S, WasInserted) = insert(Name); + std::tie(S, WasInserted) = Symtab->insert(Name); if (WasInserted) { - replaceSymbol<LazyArchive>(S, F, Sym, Symbol::UnknownType); - return S; + replaceSymbol<LazyT>(S, File, Symbol::UnknownType, + std::forward<ArgT>(Arg)...); + return; } if (!S->isUndefined()) - return S; + return; // An undefined weak will not fetch archive members. See comment on Lazy in // Symbols.h for the details. if (S->isWeak()) { - replaceSymbol<LazyArchive>(S, F, Sym, S->Type); + replaceSymbol<LazyT>(S, File, S->Type, std::forward<ArgT>(Arg)...); S->Binding = STB_WEAK; - return S; + return; } - std::pair<MemoryBufferRef, uint64_t> MBInfo = F.getMember(&Sym); - if (!MBInfo.first.getBuffer().empty()) - addFile<ELFT>(createObjectFile(MBInfo.first, F.getName(), MBInfo.second)); - return S; + + if (InputFile *F = Fetch()) + Symtab->addFile<ELFT>(F); } template <class ELFT> -void SymbolTable::addLazyObject(StringRef Name, LazyObjFile &Obj) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(Name); - if (WasInserted) { - replaceSymbol<LazyObject>(S, Obj, Name, Symbol::UnknownType); - return; - } - if (!S->isUndefined()) - return; - - // See comment for addLazyArchive above. - if (S->isWeak()) - replaceSymbol<LazyObject>(S, Obj, Name, S->Type); - else if (InputFile *F = Obj.fetch()) - addFile<ELFT>(F); -} - -// If we already saw this symbol, force loading its file. -template <class ELFT> void SymbolTable::fetchIfLazy(StringRef Name) { - if (Symbol *B = find(Name)) { - // Mark the symbol not to be eliminated by LTO - // even if it is a bitcode symbol. - B->IsUsedInRegularObj = true; - if (auto *L = dyn_cast<Lazy>(B)) - if (InputFile *File = L->fetch()) - addFile<ELFT>(File); - } +void SymbolTable::addLazyArchive(StringRef Name, ArchiveFile &F, + const object::Archive::Symbol Sym) { + replaceOrFetchLazy<ELFT, LazyArchive>(Name, F, [&]() { return F.fetch(Sym); }, + Sym); } -// This function takes care of the case in which shared libraries depend on -// the user program (not the other way, which is usual). Shared libraries -// may have undefined symbols, expecting that the user program provides -// the definitions for them. An example is BSD's __progname symbol. -// We need to put such symbols to the main program's .dynsym so that -// shared libraries can find them. -// Except this, we ignore undefined symbols in DSOs. -template <class ELFT> void SymbolTable::scanShlibUndefined() { - for (InputFile *F : SharedFiles) { - for (StringRef U : cast<SharedFile<ELFT>>(F)->getUndefinedSymbols()) { - Symbol *Sym = find(U); - if (!Sym || !Sym->isDefined()) - continue; - Sym->ExportDynamic = true; +template <class ELFT> +void SymbolTable::addLazyObject(StringRef Name, LazyObjFile &Obj) { + replaceOrFetchLazy<ELFT, LazyObject>(Name, Obj, [&]() { return Obj.fetch(); }, + Name); +} - // If -dynamic-list is given, the default version is set to - // VER_NDX_LOCAL, which prevents a symbol to be exported via .dynsym. - // Set to VER_NDX_GLOBAL so the symbol will be handled as if it were - // specified by -dynamic-list. - Sym->VersionId = VER_NDX_GLOBAL; - } +template <class ELFT> void SymbolTable::fetchLazy(Symbol *Sym) { + if (auto *S = dyn_cast<LazyArchive>(Sym)) { + if (InputFile *File = S->fetch()) + addFile<ELFT>(File); + return; } + + auto *S = cast<LazyObject>(Sym); + if (InputFile *File = cast<LazyObjFile>(S->File)->fetch()) + addFile<ELFT>(File); } // Initialize DemangledSyms with a map from demangled symbols to symbol @@ -708,7 +752,7 @@ void SymbolTable::assignExactVersion(SymbolVersion Ver, uint16_t VersionId, // Get a list of symbols which we need to assign the version to. std::vector<Symbol *> Syms = findByVersion(Ver); if (Syms.empty()) { - if (Config->NoUndefinedVersion) + if (!Config->UndefinedVersion) error("version script assignment of '" + VersionName + "' to symbol '" + Ver.Name + "' failed: symbol not defined"); return; @@ -722,10 +766,10 @@ void SymbolTable::assignExactVersion(SymbolVersion Ver, uint16_t VersionId, if (Sym->getName().contains('@')) continue; - if (Sym->InVersionScript) - warn("duplicate symbol '" + Ver.Name + "' in version script"); + if (Sym->VersionId != Config->DefaultSymbolVersion && + Sym->VersionId != VersionId) + error("duplicate symbol '" + Ver.Name + "' in version script"); Sym->VersionId = VersionId; - Sym->InVersionScript = true; } } @@ -773,6 +817,11 @@ void SymbolTable::scanVersionScript() { Sym->parseSymbolVersion(); } +template void SymbolTable::addFile<ELF32LE>(InputFile *); +template void SymbolTable::addFile<ELF32BE>(InputFile *); +template void SymbolTable::addFile<ELF64LE>(InputFile *); +template void SymbolTable::addFile<ELF64BE>(InputFile *); + template void SymbolTable::addSymbolWrap<ELF32LE>(StringRef); template void SymbolTable::addSymbolWrap<ELF32BE>(StringRef); template void SymbolTable::addSymbolWrap<ELF64LE>(StringRef); @@ -797,16 +846,16 @@ template void SymbolTable::addCombinedLTOObject<ELF32BE>(); template void SymbolTable::addCombinedLTOObject<ELF64LE>(); template void SymbolTable::addCombinedLTOObject<ELF64BE>(); -template Symbol * +template void SymbolTable::addLazyArchive<ELF32LE>(StringRef, ArchiveFile &, const object::Archive::Symbol); -template Symbol * +template void SymbolTable::addLazyArchive<ELF32BE>(StringRef, ArchiveFile &, const object::Archive::Symbol); -template Symbol * +template void SymbolTable::addLazyArchive<ELF64LE>(StringRef, ArchiveFile &, const object::Archive::Symbol); -template Symbol * +template void SymbolTable::addLazyArchive<ELF64BE>(StringRef, ArchiveFile &, const object::Archive::Symbol); @@ -815,6 +864,11 @@ template void SymbolTable::addLazyObject<ELF32BE>(StringRef, LazyObjFile &); template void SymbolTable::addLazyObject<ELF64LE>(StringRef, LazyObjFile &); template void SymbolTable::addLazyObject<ELF64BE>(StringRef, LazyObjFile &); +template void SymbolTable::fetchLazy<ELF32LE>(Symbol *); +template void SymbolTable::fetchLazy<ELF32BE>(Symbol *); +template void SymbolTable::fetchLazy<ELF64LE>(Symbol *); +template void SymbolTable::fetchLazy<ELF64BE>(Symbol *); + template void SymbolTable::addShared<ELF32LE>(StringRef, SharedFile<ELF32LE> &, const typename ELF32LE::Sym &, uint32_t Alignment, uint32_t); @@ -827,13 +881,3 @@ template void SymbolTable::addShared<ELF64LE>(StringRef, SharedFile<ELF64LE> &, template void SymbolTable::addShared<ELF64BE>(StringRef, SharedFile<ELF64BE> &, const typename ELF64BE::Sym &, uint32_t Alignment, uint32_t); - -template void SymbolTable::fetchIfLazy<ELF32LE>(StringRef); -template void SymbolTable::fetchIfLazy<ELF32BE>(StringRef); -template void SymbolTable::fetchIfLazy<ELF64LE>(StringRef); -template void SymbolTable::fetchIfLazy<ELF64BE>(StringRef); - -template void SymbolTable::scanShlibUndefined<ELF32LE>(); -template void SymbolTable::scanShlibUndefined<ELF32BE>(); -template void SymbolTable::scanShlibUndefined<ELF64LE>(); -template void SymbolTable::scanShlibUndefined<ELF64BE>(); diff --git a/contrib/llvm/tools/lld/ELF/SymbolTable.h b/contrib/llvm/tools/lld/ELF/SymbolTable.h index e7341b05baf5..5e6d44dfe4f9 100644 --- a/contrib/llvm/tools/lld/ELF/SymbolTable.h +++ b/contrib/llvm/tools/lld/ELF/SymbolTable.h @@ -12,7 +12,7 @@ #include "InputFiles.h" #include "LTO.h" -#include "Strings.h" +#include "lld/Common/Strings.h" #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseMap.h" @@ -60,8 +60,8 @@ public: uint32_t VerdefIndex); template <class ELFT> - Symbol *addLazyArchive(StringRef Name, ArchiveFile &F, - const llvm::object::Archive::Symbol S); + void addLazyArchive(StringRef Name, ArchiveFile &F, + const llvm::object::Archive::Symbol S); template <class ELFT> void addLazyObject(StringRef Name, LazyObjFile &Obj); @@ -77,8 +77,8 @@ public: uint8_t Visibility, bool CanOmitFromDynSym, InputFile *File); - template <class ELFT> void fetchIfLazy(StringRef Name); - template <class ELFT> void scanShlibUndefined(); + template <class ELFT> void fetchLazy(Symbol *Sym); + void scanVersionScript(); Symbol *find(StringRef Name); @@ -90,7 +90,6 @@ public: private: std::vector<Symbol *> findByVersion(SymbolVersion Ver); std::vector<Symbol *> findAllByVersion(SymbolVersion Ver); - void defsym(Symbol *Dst, Symbol *Src); llvm::StringMap<std::vector<Symbol *>> &getDemangledSyms(); void handleAnonymousVersion(); diff --git a/contrib/llvm/tools/lld/ELF/Symbols.cpp b/contrib/llvm/tools/lld/ELF/Symbols.cpp index 13a91aab80bb..4243cb1e80ef 100644 --- a/contrib/llvm/tools/lld/ELF/Symbols.cpp +++ b/contrib/llvm/tools/lld/ELF/Symbols.cpp @@ -14,7 +14,6 @@ #include "SyntheticSections.h" #include "Target.h" #include "Writer.h" - #include "lld/Common/ErrorHandler.h" #include "lld/Common/Strings.h" #include "llvm/ADT/STLExtras.h" @@ -39,6 +38,7 @@ Defined *ElfSym::GlobalOffsetTable; Defined *ElfSym::MipsGp; Defined *ElfSym::MipsGpDisp; Defined *ElfSym::MipsLocalGp; +Defined *ElfSym::RelaIpltEnd; static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) { switch (Sym.kind()) { @@ -58,6 +58,7 @@ static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) { return D.Value; IS = IS->Repl; + uint64_t Offset = D.Value; // An object in an SHF_MERGE section might be referenced via a @@ -76,8 +77,6 @@ static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) { Addend = 0; } - const OutputSection *OutSec = IS->getOutputSection(); - // In the typical case, this is actually very simple and boils // down to adding together 3 numbers: // 1. The address of the output section. @@ -88,7 +87,7 @@ static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) { // If you understand the data structures involved with this next // line (and how they get built), then you have a pretty good // understanding of the linker. - uint64_t VA = (OutSec ? OutSec->Addr : 0) + IS->getOffset(Offset); + uint64_t VA = IS->getVA(Offset); if (D.isTls() && !Config->Relocatable) { if (!Out::TlsPhdr) @@ -98,20 +97,12 @@ static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) { } return VA; } - case Symbol::SharedKind: { - auto &SS = cast<SharedSymbol>(Sym); - if (SS.CopyRelSec) - return SS.CopyRelSec->getParent()->Addr + SS.CopyRelSec->OutSecOff; - if (SS.NeedsPltAddr) - return Sym.getPltVA(); - return 0; - } + case Symbol::SharedKind: case Symbol::UndefinedKind: return 0; case Symbol::LazyArchiveKind: case Symbol::LazyObjectKind: - assert(Sym.IsUsedInRegularObj && "lazy symbol reached writer"); - return 0; + llvm_unreachable("lazy symbol reached writer"); } llvm_unreachable("invalid symbol kind"); } @@ -134,22 +125,26 @@ uint64_t Symbol::getGotPltVA() const { } uint64_t Symbol::getGotPltOffset() const { - return GotPltIndex * Target->GotPltEntrySize; + if (IsInIgot) + return PltIndex * Target->GotPltEntrySize; + return (PltIndex + Target->GotPltHeaderEntriesNum) * Target->GotPltEntrySize; } uint64_t Symbol::getPltVA() const { if (this->IsInIplt) return InX::Iplt->getVA() + PltIndex * Target->PltEntrySize; - return InX::Plt->getVA() + Target->PltHeaderSize + - PltIndex * Target->PltEntrySize; + return InX::Plt->getVA() + Target->getPltEntryOffset(PltIndex); +} + +uint64_t Symbol::getPltOffset() const { + assert(!this->IsInIplt); + return Target->getPltEntryOffset(PltIndex); } uint64_t Symbol::getSize() const { if (const auto *DR = dyn_cast<Defined>(this)) return DR->Size; - if (const auto *S = dyn_cast<SharedSymbol>(this)) - return S->Size; - return 0; + return cast<SharedSymbol>(this)->Size; } OutputSection *Symbol::getOutputSection() const { @@ -158,13 +153,6 @@ OutputSection *Symbol::getOutputSection() const { return Sec->Repl->getOutputSection(); return nullptr; } - - if (auto *S = dyn_cast<SharedSymbol>(this)) { - if (S->CopyRelSec) - return S->CopyRelSec->getParent(); - return nullptr; - } - return nullptr; } @@ -180,7 +168,7 @@ void Symbol::parseSymbolVersion() { return; // Truncate the symbol name so that it doesn't include the version string. - Name = {S.data(), Pos}; + NameSize = Pos; // If this is not in this DSO, it is not a definition. if (!isDefined()) @@ -206,33 +194,15 @@ void Symbol::parseSymbolVersion() { // It is an error if the specified version is not defined. // Usually version script is not provided when linking executable, // but we may still want to override a versioned symbol from DSO, - // so we do not report error in this case. - if (Config->Shared) + // so we do not report error in this case. We also do not error + // if the symbol has a local version as it won't be in the dynamic + // symbol table. + if (Config->Shared && VersionId != VER_NDX_LOCAL) error(toString(File) + ": symbol " + S + " has undefined version " + Verstr); } -InputFile *Lazy::fetch() { - if (auto *S = dyn_cast<LazyArchive>(this)) - return S->fetch(); - return cast<LazyObject>(this)->fetch(); -} - -ArchiveFile &LazyArchive::getFile() { return *cast<ArchiveFile>(File); } - -InputFile *LazyArchive::fetch() { - std::pair<MemoryBufferRef, uint64_t> MBInfo = getFile().getMember(&Sym); - - // getMember returns an empty buffer if the member was already - // read from the library. - if (MBInfo.first.getBuffer().empty()) - return nullptr; - return createObjectFile(MBInfo.first, getFile().getName(), MBInfo.second); -} - -LazyObjFile &LazyObject::getFile() { return *cast<LazyObjFile>(File); } - -InputFile *LazyObject::fetch() { return getFile().fetch(); } +InputFile *LazyArchive::fetch() { return cast<ArchiveFile>(File)->fetch(Sym); } uint8_t Symbol::computeBinding() const { if (Config->Relocatable) @@ -241,7 +211,7 @@ uint8_t Symbol::computeBinding() const { return STB_LOCAL; if (VersionId == VER_NDX_LOCAL && isDefined()) return STB_LOCAL; - if (Config->NoGnuUnique && Binding == STB_GNU_UNIQUE) + if (!Config->GnuUnique && Binding == STB_GNU_UNIQUE) return STB_GLOBAL; return Binding; } @@ -273,6 +243,27 @@ void elf::printTraceSymbol(Symbol *Sym) { message(toString(Sym->File) + S + Sym->getName()); } +void elf::warnUnorderableSymbol(const Symbol *Sym) { + if (!Config->WarnSymbolOrdering) + return; + + const InputFile *File = Sym->File; + auto *D = dyn_cast<Defined>(Sym); + + auto Warn = [&](StringRef S) { warn(toString(File) + S + Sym->getName()); }; + + if (Sym->isUndefined()) + Warn(": unable to order undefined symbol: "); + else if (Sym->isShared()) + Warn(": unable to order shared symbol: "); + else if (D && !D->Section) + Warn(": unable to order absolute symbol: "); + else if (D && isa<OutputSection>(D->Section)) + Warn(": unable to order synthetic symbol: "); + else if (D && !D->Section->Repl->Live) + Warn(": unable to order discarded symbol: "); +} + // Returns a symbol for an error message. std::string lld::toString(const Symbol &B) { if (Config->Demangle) diff --git a/contrib/llvm/tools/lld/ELF/Symbols.h b/contrib/llvm/tools/lld/ELF/Symbols.h index 9b7207383345..8c9513b9368b 100644 --- a/contrib/llvm/tools/lld/ELF/Symbols.h +++ b/contrib/llvm/tools/lld/ELF/Symbols.h @@ -7,8 +7,7 @@ // //===----------------------------------------------------------------------===// // -// All symbols are handled as SymbolBodies regardless of their types. -// This file defines various types of SymbolBodies. +// This file defines various types of Symbols. // //===----------------------------------------------------------------------===// @@ -16,9 +15,8 @@ #define LLD_ELF_SYMBOLS_H #include "InputSection.h" -#include "Strings.h" - #include "lld/Common/LLVM.h" +#include "lld/Common/Strings.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ELF.h" @@ -34,6 +32,20 @@ template <class ELFT> class ObjFile; class OutputSection; template <class ELFT> class SharedFile; +// This is a StringRef-like container that doesn't run strlen(). +// +// ELF string tables contain a lot of null-terminated strings. Most of them +// are not necessary for the linker because they are names of local symbols, +// and the linker doesn't use local symbol names for name resolution. So, we +// use this class to represents strings read from string tables. +struct StringRefZ { + StringRefZ(const char *S) : Data(S), Size(-1) {} + StringRefZ(StringRef S) : Data(S.data()), Size(S.size()) {} + + const char *Data; + const uint32_t Size; +}; + // The base class for real symbol classes. class Symbol { public: @@ -47,6 +59,25 @@ public: Kind kind() const { return static_cast<Kind>(SymbolKind); } + // The file from which this symbol was created. + InputFile *File; + +protected: + const char *NameData; + mutable uint32_t NameSize; + +public: + uint32_t DynsymIndex = 0; + uint32_t GotIndex = -1; + uint32_t PltIndex = -1; + uint32_t GlobalDynIndex = -1; + + // This field is a index to the symbol's version definition. + uint32_t VerdefIndex = -1; + + // Version definition index. + uint16_t VersionId; + // Symbol binding. This is not overwritten by replaceSymbol to track // changes during resolution. In particular: // - An undefined weak is still weak when it resolves to a shared library. @@ -54,8 +85,11 @@ public: // remember it is weak. uint8_t Binding; - // Version definition index. - uint16_t VersionId; + // The following fields have the same meaning as the ELF symbol attributes. + uint8_t Type; // symbol type + uint8_t StOther; // st_other field value + + const uint8_t SymbolKind; // Symbol visibility. This is the computed minimum visibility of all // observed non-DSO symbols. @@ -81,12 +115,6 @@ public: // True if this symbol is specified by --trace-symbol option. unsigned Traced : 1; - // This symbol version was found in a version script. - unsigned InVersionScript : 1; - - // The file from which this symbol was created. - InputFile *File; - bool includeInDynsym() const; uint8_t computeBinding() const; bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; } @@ -100,15 +128,15 @@ public: return SymbolKind == LazyArchiveKind || SymbolKind == LazyObjectKind; } - // True is this is an undefined weak symbol. This only works once - // all input files have been added. - bool isUndefWeak() const { - // See comment on Lazy the details. - return isWeak() && (isUndefined() || isLazy()); + // True if this is an undefined weak symbol. + bool isUndefWeak() const { return isWeak() && isUndefined(); } + + StringRef getName() const { + if (NameSize == (uint32_t)-1) + NameSize = strlen(NameData); + return {NameData, NameSize}; } - StringRef getName() const { return Name; } - uint8_t getVisibility() const { return StOther & 0x3; } void parseSymbolVersion(); bool isInGot() const { return GotIndex != -1U; } @@ -121,34 +149,22 @@ public: uint64_t getGotPltOffset() const; uint64_t getGotPltVA() const; uint64_t getPltVA() const; + uint64_t getPltOffset() const; uint64_t getSize() const; OutputSection *getOutputSection() const; - uint32_t DynsymIndex = 0; - uint32_t GotIndex = -1; - uint32_t GotPltIndex = -1; - uint32_t PltIndex = -1; - uint32_t GlobalDynIndex = -1; - protected: Symbol(Kind K, InputFile *File, StringRefZ Name, uint8_t Binding, uint8_t StOther, uint8_t Type) - : Binding(Binding), File(File), SymbolKind(K), NeedsPltAddr(false), - IsInGlobalMipsGot(false), Is32BitMipsGot(false), IsInIplt(false), - IsInIgot(false), IsPreemptible(false), Used(!Config->GcSections), - Type(Type), StOther(StOther), Name(Name) {} - - const unsigned SymbolKind : 8; + : File(File), NameData(Name.Data), NameSize(Name.Size), Binding(Binding), + Type(Type), StOther(StOther), SymbolKind(K), NeedsPltAddr(false), + IsInIplt(false), IsInIgot(false), IsPreemptible(false), + Used(!Config->GcSections), NeedsTocRestore(false) {} public: // True the symbol should point to its PLT entry. // For SharedSymbol only. unsigned NeedsPltAddr : 1; - // True if this symbol has an entry in the global part of MIPS GOT. - unsigned IsInGlobalMipsGot : 1; - - // True if this symbol is referenced by 32-bit GOT relocations. - unsigned Is32BitMipsGot : 1; // True if this symbol is in the Iplt sub-section of the Plt. unsigned IsInIplt : 1; @@ -156,14 +172,15 @@ public: // True if this symbol is in the Igot sub-section of the .got.plt or .got. unsigned IsInIgot : 1; + // True if this symbol is preemptible at load time. unsigned IsPreemptible : 1; // True if an undefined or shared symbol is used from a live section. unsigned Used : 1; - // The following fields have the same meaning as the ELF symbol attributes. - uint8_t Type; // symbol type - uint8_t StOther; // st_other field value + // True if a call to this symbol needs to be followed by a restore of the + // PPC64 toc pointer. + unsigned NeedsTocRestore : 1; // The Type field may also have this value. It means that we have not yet seen // a non-Lazy symbol with this name, so we don't know what its type is. The @@ -178,9 +195,6 @@ public: bool isGnuIFunc() const { return Type == llvm::ELF::STT_GNU_IFUNC; } bool isObject() const { return Type == llvm::ELF::STT_OBJECT; } bool isFile() const { return Type == llvm::ELF::STT_FILE; } - -protected: - StringRefZ Name; }; // Represents a symbol that is defined in the current output file. @@ -214,8 +228,9 @@ public: SharedSymbol(InputFile &File, StringRef Name, uint8_t Binding, uint8_t StOther, uint8_t Type, uint64_t Value, uint64_t Size, uint32_t Alignment, uint32_t VerdefIndex) - : Symbol(SharedKind, &File, Name, Binding, StOther, Type), Value(Value), - Size(Size), VerdefIndex(VerdefIndex), Alignment(Alignment) { + : Symbol(SharedKind, &File, Name, Binding, StOther, Type), + Alignment(Alignment), Value(Value), Size(Size) { + this->VerdefIndex = VerdefIndex; // GNU ifunc is a mechanism to allow user-supplied functions to // resolve PLT slot values at load-time. This is contrary to the // regular symbol resolution scheme in which symbols are resolved just @@ -240,54 +255,36 @@ public: return *cast<SharedFile<ELFT>>(File); } - // If not null, there is a copy relocation to this section. - InputSection *CopyRelSec = nullptr; + uint32_t Alignment; uint64_t Value; // st_value uint64_t Size; // st_size - - // This field is a index to the symbol's version definition. - uint32_t VerdefIndex; - - uint32_t Alignment; }; -// This represents a symbol that is not yet in the link, but we know where to -// find it if needed. If the resolver finds both Undefined and Lazy for the same -// name, it will ask the Lazy to load a file. +// LazyArchive and LazyObject represent a symbols that is not yet in the link, +// but we know where to find it if needed. If the resolver finds both Undefined +// and Lazy for the same name, it will ask the Lazy to load a file. // // A special complication is the handling of weak undefined symbols. They should // not load a file, but we have to remember we have seen both the weak undefined // and the lazy. We represent that with a lazy symbol with a weak binding. This // means that code looking for undefined symbols normally also has to take lazy // symbols into consideration. -class Lazy : public Symbol { -public: - static bool classof(const Symbol *S) { return S->isLazy(); } - - // Returns an object file for this symbol, or a nullptr if the file - // was already returned. - InputFile *fetch(); - -protected: - Lazy(Kind K, InputFile &File, StringRef Name, uint8_t Type) - : Symbol(K, &File, Name, llvm::ELF::STB_GLOBAL, llvm::ELF::STV_DEFAULT, - Type) {} -}; // This class represents a symbol defined in an archive file. It is // created from an archive file header, and it knows how to load an // object file from an archive to replace itself with a defined // symbol. -class LazyArchive : public Lazy { +class LazyArchive : public Symbol { public: - LazyArchive(InputFile &File, const llvm::object::Archive::Symbol S, - uint8_t Type) - : Lazy(LazyArchiveKind, File, S.getName(), Type), Sym(S) {} + LazyArchive(InputFile &File, uint8_t Type, + const llvm::object::Archive::Symbol S) + : Symbol(LazyArchiveKind, &File, S.getName(), llvm::ELF::STB_GLOBAL, + llvm::ELF::STV_DEFAULT, Type), + Sym(S) {} static bool classof(const Symbol *S) { return S->kind() == LazyArchiveKind; } - ArchiveFile &getFile(); InputFile *fetch(); private: @@ -296,15 +293,13 @@ private: // LazyObject symbols represents symbols in object files between // --start-lib and --end-lib options. -class LazyObject : public Lazy { +class LazyObject : public Symbol { public: - LazyObject(InputFile &File, StringRef Name, uint8_t Type) - : Lazy(LazyObjectKind, File, Name, Type) {} + LazyObject(InputFile &File, uint8_t Type, StringRef Name) + : Symbol(LazyObjectKind, &File, Name, llvm::ELF::STB_GLOBAL, + llvm::ELF::STV_DEFAULT, Type) {} static bool classof(const Symbol *S) { return S->kind() == LazyObjectKind; } - - LazyObjFile &getFile(); - InputFile *fetch(); }; // Some linker-generated symbols need to be created as @@ -334,6 +329,9 @@ struct ElfSym { static Defined *MipsGp; static Defined *MipsGpDisp; static Defined *MipsLocalGp; + + // __rela_iplt_end or __rel_iplt_end + static Defined *RelaIpltEnd; }; // A buffer class that is large enough to hold any Symbol-derived @@ -351,6 +349,8 @@ void printTraceSymbol(Symbol *Sym); template <typename T, typename... ArgT> void replaceSymbol(Symbol *S, ArgT &&... Arg) { + static_assert(std::is_trivially_destructible<T>(), + "Symbol types must be trivially destructible"); static_assert(sizeof(T) <= sizeof(SymbolUnion), "SymbolUnion too small"); static_assert(alignof(T) <= alignof(SymbolUnion), "SymbolUnion not aligned enough"); @@ -367,13 +367,14 @@ void replaceSymbol(Symbol *S, ArgT &&... Arg) { S->ExportDynamic = Sym.ExportDynamic; S->CanInline = Sym.CanInline; S->Traced = Sym.Traced; - S->InVersionScript = Sym.InVersionScript; // Print out a log message if --trace-symbol was specified. // This is for debugging. if (S->Traced) printTraceSymbol(S); } + +void warnUnorderableSymbol(const Symbol *Sym); } // namespace elf std::string toString(const elf::Symbol &B); diff --git a/contrib/llvm/tools/lld/ELF/SyntheticSections.cpp b/contrib/llvm/tools/lld/ELF/SyntheticSections.cpp index a5a851f95400..ae02434572c2 100644 --- a/contrib/llvm/tools/lld/ELF/SyntheticSections.cpp +++ b/contrib/llvm/tools/lld/ELF/SyntheticSections.cpp @@ -20,15 +20,16 @@ #include "InputFiles.h" #include "LinkerScript.h" #include "OutputSections.h" -#include "Strings.h" #include "SymbolTable.h" #include "Symbols.h" #include "Target.h" #include "Writer.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" +#include "lld/Common/Strings.h" #include "lld/Common/Threads.h" #include "lld/Common/Version.h" +#include "llvm/ADT/SetOperations.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" #include "llvm/Object/Decompressor.h" @@ -47,27 +48,20 @@ using namespace llvm::dwarf; using namespace llvm::ELF; using namespace llvm::object; using namespace llvm::support; -using namespace llvm::support::endian; using namespace lld; using namespace lld::elf; -constexpr size_t MergeNoTailSection::NumShards; - -static void write32(void *Buf, uint32_t Val) { - endian::write32(Buf, Val, Config->Endianness); -} +using llvm::support::endian::read32le; +using llvm::support::endian::write32le; +using llvm::support::endian::write64le; -uint64_t SyntheticSection::getVA() const { - if (OutputSection *Sec = getParent()) - return Sec->Addr + OutSecOff; - return 0; -} +constexpr size_t MergeNoTailSection::NumShards; // Returns an LLD version string. static ArrayRef<uint8_t> getVersion() { // Check LLD_VERSION first for ease of testing. - // You can get consitent output by using the environment variable. + // You can get consistent output by using the environment variable. // This is only for testing. StringRef S = getenv("LLD_VERSION"); if (S.empty()) @@ -192,8 +186,6 @@ MipsOptionsSection<ELFT> *MipsOptionsSection<ELFT>::create() { auto *Opt = reinterpret_cast<const Elf_Mips_Options *>(D.data()); if (Opt->kind == ODK_REGINFO) { - if (Config->Relocatable && Opt->getRegInfo().ri_gp_value) - error(Filename + ": unsupported non-zero ri_gp_value"); Reginfo.ri_gprmask |= Opt->getRegInfo().ri_gprmask; Sec->getFile<ELFT>()->MipsGp0 = Opt->getRegInfo().ri_gp_value; break; @@ -244,10 +236,8 @@ MipsReginfoSection<ELFT> *MipsReginfoSection<ELFT>::create() { error(toString(Sec->File) + ": invalid size of .reginfo section"); return nullptr; } - auto *R = reinterpret_cast<const Elf_Mips_RegInfo *>(Sec->Data.data()); - if (Config->Relocatable && R->ri_gp_value) - error(toString(Sec->File) + ": unsupported non-zero ri_gp_value"); + auto *R = reinterpret_cast<const Elf_Mips_RegInfo *>(Sec->Data.data()); Reginfo.ri_gprmask |= R->ri_gprmask; Sec->getFile<ELFT>()->MipsGp0 = R->ri_gp_value; }; @@ -266,8 +256,8 @@ InputSection *elf::createInterpSection() { return Sec; } -Symbol *elf::addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value, - uint64_t Size, InputSectionBase &Section) { +Defined *elf::addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value, + uint64_t Size, InputSectionBase &Section) { auto *S = make<Defined>(Section.File, Name, STB_LOCAL, STV_DEFAULT, Type, Value, Size, &Section); if (InX::SymTab) @@ -338,8 +328,6 @@ void BuildIdSection::computeHash( BssSection::BssSection(StringRef Name, uint64_t Size, uint32_t Alignment) : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_NOBITS, Alignment, Name) { this->Bss = true; - if (OutputSection *Sec = getParent()) - Sec->Alignment = std::max(Sec->Alignment, Alignment); this->Size = Size; } @@ -347,7 +335,7 @@ void BuildIdSection::writeBuildId(ArrayRef<uint8_t> Buf) { switch (Config->BuildId) { case BuildIdKind::Fast: computeHash(Buf, [](uint8_t *Dest, ArrayRef<uint8_t> Arr) { - write64le(Dest, xxHash64(toStringRef(Arr))); + write64le(Dest, xxHash64(Arr)); }); break; case BuildIdKind::Md5: @@ -380,15 +368,11 @@ EhFrameSection::EhFrameSection() // and where their relocations point to. template <class ELFT, class RelTy> CieRecord *EhFrameSection::addCie(EhSectionPiece &Cie, ArrayRef<RelTy> Rels) { - auto *Sec = cast<EhInputSection>(Cie.Sec); - if (read32(Cie.data().data() + 4, Config->Endianness) != 0) - fatal(toString(Sec) + ": CIE expected at beginning of .eh_frame"); - Symbol *Personality = nullptr; unsigned FirstRelI = Cie.FirstRelocation; if (FirstRelI != (unsigned)-1) Personality = - &Sec->template getFile<ELFT>()->getRelocTargetSym(Rels[FirstRelI]); + &Cie.Sec->template getFile<ELFT>()->getRelocTargetSym(Rels[FirstRelI]); // Search for an existing CIE by CIE contents/relocation target pair. CieRecord *&Rec = CieMap[{Cie.data(), Personality}]; @@ -433,14 +417,14 @@ bool EhFrameSection::isFdeLive(EhSectionPiece &Fde, ArrayRef<RelTy> Rels) { // one and associates FDEs to the CIE. template <class ELFT, class RelTy> void EhFrameSection::addSectionAux(EhInputSection *Sec, ArrayRef<RelTy> Rels) { - DenseMap<size_t, CieRecord *> OffsetToCie; + OffsetToCie.clear(); for (EhSectionPiece &Piece : Sec->Pieces) { // The empty record is the end marker. if (Piece.Size == 4) return; size_t Offset = Piece.InputOff; - uint32_t ID = read32(Piece.data().data() + 4, Config->Endianness); + uint32_t ID = read32(Piece.data().data() + 4); if (ID == 0) { OffsetToCie[Offset] = addCie<ELFT>(Piece, Rels); continue; @@ -468,10 +452,6 @@ template <class ELFT> void EhFrameSection::addSection(InputSectionBase *C) { for (auto *DS : Sec->DependentSections) DependentSections.push_back(DS); - // .eh_frame is a sequence of CIE or FDE records. This function - // splits it into pieces so that we can call - // SplitInputSection::getSectionPiece on the section. - Sec->split<ELFT>(); if (Sec->Pieces.empty()) return; @@ -494,9 +474,7 @@ static void writeCieFde(uint8_t *Buf, ArrayRef<uint8_t> D) { } void EhFrameSection::finalizeContents() { - if (this->Size) - return; // Already finalized. - + assert(!this->Size); // Not finalized. size_t Off = 0; for (CieRecord *Rec : CieRecords) { Rec->Cie->OutputOff = Off; @@ -509,10 +487,10 @@ void EhFrameSection::finalizeContents() { } // The LSB standard does not allow a .eh_frame section with zero - // Call Frame Information records. Therefore add a CIE record length - // 0 as a terminator if this .eh_frame section is empty. - if (Off == 0) - Off = 4; + // Call Frame Information records. glibc unwind-dw2-fde.c + // classify_object_over_fdes expects there is a CIE record length 0 as a + // terminator. Thus we add one unconditionally. + Off += 4; this->Size = Off; } @@ -524,25 +502,47 @@ std::vector<EhFrameSection::FdeData> EhFrameSection::getFdeData() const { uint8_t *Buf = getParent()->Loc + OutSecOff; std::vector<FdeData> Ret; + uint64_t VA = InX::EhFrameHdr->getVA(); for (CieRecord *Rec : CieRecords) { uint8_t Enc = getFdeEncoding(Rec->Cie); for (EhSectionPiece *Fde : Rec->Fdes) { - uint32_t Pc = getFdePc(Buf, Fde->OutputOff, Enc); - uint32_t FdeVA = getParent()->Addr + Fde->OutputOff; - Ret.push_back({Pc, FdeVA}); + uint64_t Pc = getFdePc(Buf, Fde->OutputOff, Enc); + uint64_t FdeVA = getParent()->Addr + Fde->OutputOff; + if (!isInt<32>(Pc - VA)) + fatal(toString(Fde->Sec) + ": PC offset is too large: 0x" + + Twine::utohexstr(Pc - VA)); + Ret.push_back({uint32_t(Pc - VA), uint32_t(FdeVA - VA)}); } } + + // Sort the FDE list by their PC and uniqueify. Usually there is only + // one FDE for a PC (i.e. function), but if ICF merges two functions + // into one, there can be more than one FDEs pointing to the address. + auto Less = [](const FdeData &A, const FdeData &B) { + return A.PcRel < B.PcRel; + }; + std::stable_sort(Ret.begin(), Ret.end(), Less); + auto Eq = [](const FdeData &A, const FdeData &B) { + return A.PcRel == B.PcRel; + }; + Ret.erase(std::unique(Ret.begin(), Ret.end(), Eq), Ret.end()); + return Ret; } static uint64_t readFdeAddr(uint8_t *Buf, int Size) { switch (Size) { case DW_EH_PE_udata2: - return read16(Buf, Config->Endianness); + return read16(Buf); + case DW_EH_PE_sdata2: + return (int16_t)read16(Buf); case DW_EH_PE_udata4: - return read32(Buf, Config->Endianness); + return read32(Buf); + case DW_EH_PE_sdata4: + return (int32_t)read32(Buf); case DW_EH_PE_udata8: - return read64(Buf, Config->Endianness); + case DW_EH_PE_sdata8: + return read64(Buf); case DW_EH_PE_absptr: return readUint(Buf); } @@ -556,7 +556,7 @@ uint64_t EhFrameSection::getFdePc(uint8_t *Buf, size_t FdeOff, // The starting address to which this FDE applies is // stored at FDE + 8 byte. size_t Off = FdeOff + 8; - uint64_t Addr = readFdeAddr(Buf + Off, Enc & 0x7); + uint64_t Addr = readFdeAddr(Buf + Off, Enc & 0xf); if ((Enc & 0x70) == DW_EH_PE_absptr) return Addr; if ((Enc & 0x70) == DW_EH_PE_pcrel) @@ -589,7 +589,15 @@ void EhFrameSection::writeTo(uint8_t *Buf) { GotSection::GotSection() : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, - Target->GotEntrySize, ".got") {} + Target->GotEntrySize, ".got") { + // PPC64 saves the ElfSym::GlobalOffsetTable .TOC. as the first entry in the + // .got. If there are no references to .TOC. in the symbol table, + // ElfSym::GlobalOffsetTable will not be defined and we won't need to save + // .TOC. in the .got. When it is defined, we increase NumEntries by the number + // of entries used to emit ElfSym::GlobalOffsetTable. + if (ElfSym::GlobalOffsetTable && !Target->GotBaseSymInGotPlt) + NumEntries += Target->GotHeaderEntriesNum; +} void GotSection::addEntry(Symbol &Sym) { Sym.GotIndex = NumEntries; @@ -623,196 +631,383 @@ uint64_t GotSection::getGlobalDynOffset(const Symbol &B) const { return B.GlobalDynIndex * Config->Wordsize; } -void GotSection::finalizeContents() { Size = NumEntries * Config->Wordsize; } +void GotSection::finalizeContents() { + Size = NumEntries * Config->Wordsize; +} bool GotSection::empty() const { // We need to emit a GOT even if it's empty if there's a relocation that is // relative to GOT(such as GOTOFFREL) or there's a symbol that points to a GOT - // (i.e. _GLOBAL_OFFSET_TABLE_). - return NumEntries == 0 && !HasGotOffRel && !ElfSym::GlobalOffsetTable; + // (i.e. _GLOBAL_OFFSET_TABLE_) that the target defines relative to the .got. + return NumEntries == 0 && !HasGotOffRel && + !(ElfSym::GlobalOffsetTable && !Target->GotBaseSymInGotPlt); } void GotSection::writeTo(uint8_t *Buf) { // Buf points to the start of this section's buffer, // whereas InputSectionBase::relocateAlloc() expects its argument // to point to the start of the output section. + Target->writeGotHeader(Buf); relocateAlloc(Buf - OutSecOff, Buf - OutSecOff + Size); } +static uint64_t getMipsPageAddr(uint64_t Addr) { + return (Addr + 0x8000) & ~0xffff; +} + +static uint64_t getMipsPageCount(uint64_t Size) { + return (Size + 0xfffe) / 0xffff + 1; +} + MipsGotSection::MipsGotSection() : SyntheticSection(SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL, SHT_PROGBITS, 16, ".got") {} -void MipsGotSection::addEntry(Symbol &Sym, int64_t Addend, RelExpr Expr) { - // For "true" local symbols which can be referenced from the same module - // only compiler creates two instructions for address loading: - // - // lw $8, 0($gp) # R_MIPS_GOT16 - // addi $8, $8, 0 # R_MIPS_LO16 - // - // The first instruction loads high 16 bits of the symbol address while - // the second adds an offset. That allows to reduce number of required - // GOT entries because only one global offset table entry is necessary - // for every 64 KBytes of local data. So for local symbols we need to - // allocate number of GOT entries to hold all required "page" addresses. - // - // All global symbols (hidden and regular) considered by compiler uniformly. - // It always generates a single `lw` instruction and R_MIPS_GOT16 relocation - // to load address of the symbol. So for each such symbol we need to - // allocate dedicated GOT entry to store its address. - // - // If a symbol is preemptible we need help of dynamic linker to get its - // final address. The corresponding GOT entries are allocated in the - // "global" part of GOT. Entries for non preemptible global symbol allocated - // in the "local" part of GOT. - // - // See "Global Offset Table" in Chapter 5: - // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf +void MipsGotSection::addEntry(InputFile &File, Symbol &Sym, int64_t Addend, + RelExpr Expr) { + FileGot &G = getGot(File); if (Expr == R_MIPS_GOT_LOCAL_PAGE) { - // At this point we do not know final symbol value so to reduce number - // of allocated GOT entries do the following trick. Save all output - // sections referenced by GOT relocations. Then later in the `finalize` - // method calculate number of "pages" required to cover all saved output - // section and allocate appropriate number of GOT entries. - PageIndexMap.insert({Sym.getOutputSection(), 0}); - return; - } - if (Sym.isTls()) { - // GOT entries created for MIPS TLS relocations behave like - // almost GOT entries from other ABIs. They go to the end - // of the global offset table. - Sym.GotIndex = TlsEntries.size(); - TlsEntries.push_back(&Sym); - return; - } - auto AddEntry = [&](Symbol &S, uint64_t A, GotEntries &Items) { - if (S.isInGot() && !A) - return; - size_t NewIndex = Items.size(); - if (!EntryIndexMap.insert({{&S, A}, NewIndex}).second) - return; - Items.emplace_back(&S, A); - if (!A) - S.GotIndex = NewIndex; - }; - if (Sym.IsPreemptible) { - // Ignore addends for preemptible symbols. They got single GOT entry anyway. - AddEntry(Sym, 0, GlobalEntries); - Sym.IsInGlobalMipsGot = true; - } else if (Expr == R_MIPS_GOT_OFF32) { - AddEntry(Sym, Addend, LocalEntries32); - Sym.Is32BitMipsGot = true; - } else { - // Hold local GOT entries accessed via a 16-bit index separately. - // That allows to write them in the beginning of the GOT and keep - // their indexes as less as possible to escape relocation's overflow. - AddEntry(Sym, Addend, LocalEntries); - } + if (const OutputSection *OS = Sym.getOutputSection()) + G.PagesMap.insert({OS, {}}); + else + G.Local16.insert({{nullptr, getMipsPageAddr(Sym.getVA(Addend))}, 0}); + } else if (Sym.isTls()) + G.Tls.insert({&Sym, 0}); + else if (Sym.IsPreemptible && Expr == R_ABS) + G.Relocs.insert({&Sym, 0}); + else if (Sym.IsPreemptible) + G.Global.insert({&Sym, 0}); + else if (Expr == R_MIPS_GOT_OFF32) + G.Local32.insert({{&Sym, Addend}, 0}); + else + G.Local16.insert({{&Sym, Addend}, 0}); } -bool MipsGotSection::addDynTlsEntry(Symbol &Sym) { - if (Sym.GlobalDynIndex != -1U) - return false; - Sym.GlobalDynIndex = TlsEntries.size(); - // Global Dynamic TLS entries take two GOT slots. - TlsEntries.push_back(nullptr); - TlsEntries.push_back(&Sym); - return true; +void MipsGotSection::addDynTlsEntry(InputFile &File, Symbol &Sym) { + getGot(File).DynTlsSymbols.insert({&Sym, 0}); } -// Reserves TLS entries for a TLS module ID and a TLS block offset. -// In total it takes two GOT slots. -bool MipsGotSection::addTlsIndex() { - if (TlsIndexOff != uint32_t(-1)) - return false; - TlsIndexOff = TlsEntries.size() * Config->Wordsize; - TlsEntries.push_back(nullptr); - TlsEntries.push_back(nullptr); - return true; +void MipsGotSection::addTlsIndex(InputFile &File) { + getGot(File).DynTlsSymbols.insert({nullptr, 0}); } -static uint64_t getMipsPageAddr(uint64_t Addr) { - return (Addr + 0x8000) & ~0xffff; +size_t MipsGotSection::FileGot::getEntriesNum() const { + return getPageEntriesNum() + Local16.size() + Global.size() + Relocs.size() + + Tls.size() + DynTlsSymbols.size() * 2; } -static uint64_t getMipsPageCount(uint64_t Size) { - return (Size + 0xfffe) / 0xffff + 1; +size_t MipsGotSection::FileGot::getPageEntriesNum() const { + size_t Num = 0; + for (const std::pair<const OutputSection *, FileGot::PageBlock> &P : PagesMap) + Num += P.second.Count; + return Num; } -uint64_t MipsGotSection::getPageEntryOffset(const Symbol &B, - int64_t Addend) const { - const OutputSection *OutSec = B.getOutputSection(); - uint64_t SecAddr = getMipsPageAddr(OutSec->Addr); - uint64_t SymAddr = getMipsPageAddr(B.getVA(Addend)); - uint64_t Index = PageIndexMap.lookup(OutSec) + (SymAddr - SecAddr) / 0xffff; - assert(Index < PageEntriesNum); - return (HeaderEntriesNum + Index) * Config->Wordsize; +size_t MipsGotSection::FileGot::getIndexedEntriesNum() const { + size_t Count = getPageEntriesNum() + Local16.size() + Global.size(); + // If there are relocation-only entries in the GOT, TLS entries + // are allocated after them. TLS entries should be addressable + // by 16-bit index so count both reloc-only and TLS entries. + if (!Tls.empty() || !DynTlsSymbols.empty()) + Count += Relocs.size() + Tls.size() + DynTlsSymbols.size() * 2; + return Count; } -uint64_t MipsGotSection::getSymEntryOffset(const Symbol &B, - int64_t Addend) const { - // Calculate offset of the GOT entries block: TLS, global, local. - uint64_t Index = HeaderEntriesNum + PageEntriesNum; - if (B.isTls()) - Index += LocalEntries.size() + LocalEntries32.size() + GlobalEntries.size(); - else if (B.IsInGlobalMipsGot) - Index += LocalEntries.size() + LocalEntries32.size(); - else if (B.Is32BitMipsGot) - Index += LocalEntries.size(); - // Calculate offset of the GOT entry in the block. - if (B.isInGot()) - Index += B.GotIndex; - else { - auto It = EntryIndexMap.find({&B, Addend}); - assert(It != EntryIndexMap.end()); - Index += It->second; +MipsGotSection::FileGot &MipsGotSection::getGot(InputFile &F) { + if (!F.MipsGotIndex.hasValue()) { + Gots.emplace_back(); + Gots.back().File = &F; + F.MipsGotIndex = Gots.size() - 1; + } + return Gots[*F.MipsGotIndex]; +} + +uint64_t MipsGotSection::getPageEntryOffset(const InputFile *F, + const Symbol &Sym, + int64_t Addend) const { + const FileGot &G = Gots[*F->MipsGotIndex]; + uint64_t Index = 0; + if (const OutputSection *OutSec = Sym.getOutputSection()) { + uint64_t SecAddr = getMipsPageAddr(OutSec->Addr); + uint64_t SymAddr = getMipsPageAddr(Sym.getVA(Addend)); + Index = G.PagesMap.lookup(OutSec).FirstIndex + (SymAddr - SecAddr) / 0xffff; + } else { + Index = G.Local16.lookup({nullptr, getMipsPageAddr(Sym.getVA(Addend))}); } return Index * Config->Wordsize; } -uint64_t MipsGotSection::getTlsOffset() const { - return (getLocalEntriesNum() + GlobalEntries.size()) * Config->Wordsize; +uint64_t MipsGotSection::getSymEntryOffset(const InputFile *F, const Symbol &S, + int64_t Addend) const { + const FileGot &G = Gots[*F->MipsGotIndex]; + Symbol *Sym = const_cast<Symbol *>(&S); + if (Sym->isTls()) + return G.Tls.lookup(Sym) * Config->Wordsize; + if (Sym->IsPreemptible) + return G.Global.lookup(Sym) * Config->Wordsize; + return G.Local16.lookup({Sym, Addend}) * Config->Wordsize; } -uint64_t MipsGotSection::getGlobalDynOffset(const Symbol &B) const { - return B.GlobalDynIndex * Config->Wordsize; +uint64_t MipsGotSection::getTlsIndexOffset(const InputFile *F) const { + const FileGot &G = Gots[*F->MipsGotIndex]; + return G.DynTlsSymbols.lookup(nullptr) * Config->Wordsize; +} + +uint64_t MipsGotSection::getGlobalDynOffset(const InputFile *F, + const Symbol &S) const { + const FileGot &G = Gots[*F->MipsGotIndex]; + Symbol *Sym = const_cast<Symbol *>(&S); + return G.DynTlsSymbols.lookup(Sym) * Config->Wordsize; } const Symbol *MipsGotSection::getFirstGlobalEntry() const { - return GlobalEntries.empty() ? nullptr : GlobalEntries.front().first; + if (Gots.empty()) + return nullptr; + const FileGot &PrimGot = Gots.front(); + if (!PrimGot.Global.empty()) + return PrimGot.Global.front().first; + if (!PrimGot.Relocs.empty()) + return PrimGot.Relocs.front().first; + return nullptr; } unsigned MipsGotSection::getLocalEntriesNum() const { - return HeaderEntriesNum + PageEntriesNum + LocalEntries.size() + - LocalEntries32.size(); + if (Gots.empty()) + return HeaderEntriesNum; + return HeaderEntriesNum + Gots.front().getPageEntriesNum() + + Gots.front().Local16.size(); +} + +bool MipsGotSection::tryMergeGots(FileGot &Dst, FileGot &Src, bool IsPrimary) { + FileGot Tmp = Dst; + set_union(Tmp.PagesMap, Src.PagesMap); + set_union(Tmp.Local16, Src.Local16); + set_union(Tmp.Global, Src.Global); + set_union(Tmp.Relocs, Src.Relocs); + set_union(Tmp.Tls, Src.Tls); + set_union(Tmp.DynTlsSymbols, Src.DynTlsSymbols); + + size_t Count = IsPrimary ? HeaderEntriesNum : 0; + Count += Tmp.getIndexedEntriesNum(); + + if (Count * Config->Wordsize > Config->MipsGotSize) + return false; + + std::swap(Tmp, Dst); + return true; } void MipsGotSection::finalizeContents() { updateAllocSize(); } bool MipsGotSection::updateAllocSize() { - PageEntriesNum = 0; - for (std::pair<const OutputSection *, size_t> &P : PageIndexMap) { - // For each output section referenced by GOT page relocations calculate - // and save into PageIndexMap an upper bound of MIPS GOT entries required - // to store page addresses of local symbols. We assume the worst case - - // each 64kb page of the output section has at least one GOT relocation - // against it. And take in account the case when the section intersects - // page boundaries. - P.second = PageEntriesNum; - PageEntriesNum += getMipsPageCount(P.first->Size); - } - Size = (getLocalEntriesNum() + GlobalEntries.size() + TlsEntries.size()) * - Config->Wordsize; + Size = HeaderEntriesNum * Config->Wordsize; + for (const FileGot &G : Gots) + Size += G.getEntriesNum() * Config->Wordsize; return false; } +template <class ELFT> void MipsGotSection::build() { + if (Gots.empty()) + return; + + std::vector<FileGot> MergedGots(1); + + // For each GOT move non-preemptible symbols from the `Global` + // to `Local16` list. Preemptible symbol might become non-preemptible + // one if, for example, it gets a related copy relocation. + for (FileGot &Got : Gots) { + for (auto &P: Got.Global) + if (!P.first->IsPreemptible) + Got.Local16.insert({{P.first, 0}, 0}); + Got.Global.remove_if([&](const std::pair<Symbol *, size_t> &P) { + return !P.first->IsPreemptible; + }); + } + + // For each GOT remove "reloc-only" entry if there is "global" + // entry for the same symbol. And add local entries which indexed + // using 32-bit value at the end of 16-bit entries. + for (FileGot &Got : Gots) { + Got.Relocs.remove_if([&](const std::pair<Symbol *, size_t> &P) { + return Got.Global.count(P.first); + }); + set_union(Got.Local16, Got.Local32); + Got.Local32.clear(); + } + + // Evaluate number of "reloc-only" entries in the resulting GOT. + // To do that put all unique "reloc-only" and "global" entries + // from all GOTs to the future primary GOT. + FileGot *PrimGot = &MergedGots.front(); + for (FileGot &Got : Gots) { + set_union(PrimGot->Relocs, Got.Global); + set_union(PrimGot->Relocs, Got.Relocs); + Got.Relocs.clear(); + } + + // Evaluate number of "page" entries in each GOT. + for (FileGot &Got : Gots) { + for (std::pair<const OutputSection *, FileGot::PageBlock> &P : + Got.PagesMap) { + const OutputSection *OS = P.first; + uint64_t SecSize = 0; + for (BaseCommand *Cmd : OS->SectionCommands) { + if (auto *ISD = dyn_cast<InputSectionDescription>(Cmd)) + for (InputSection *IS : ISD->Sections) { + uint64_t Off = alignTo(SecSize, IS->Alignment); + SecSize = Off + IS->getSize(); + } + } + P.second.Count = getMipsPageCount(SecSize); + } + } + + // Merge GOTs. Try to join as much as possible GOTs but do not exceed + // maximum GOT size. At first, try to fill the primary GOT because + // the primary GOT can be accessed in the most effective way. If it + // is not possible, try to fill the last GOT in the list, and finally + // create a new GOT if both attempts failed. + for (FileGot &SrcGot : Gots) { + InputFile *File = SrcGot.File; + if (tryMergeGots(MergedGots.front(), SrcGot, true)) { + File->MipsGotIndex = 0; + } else { + // If this is the first time we failed to merge with the primary GOT, + // MergedGots.back() will also be the primary GOT. We must make sure not + // to try to merge again with IsPrimary=false, as otherwise, if the + // inputs are just right, we could allow the primary GOT to become 1 or 2 + // words too big due to ignoring the header size. + if (MergedGots.size() == 1 || + !tryMergeGots(MergedGots.back(), SrcGot, false)) { + MergedGots.emplace_back(); + std::swap(MergedGots.back(), SrcGot); + } + File->MipsGotIndex = MergedGots.size() - 1; + } + } + std::swap(Gots, MergedGots); + + // Reduce number of "reloc-only" entries in the primary GOT + // by substracting "global" entries exist in the primary GOT. + PrimGot = &Gots.front(); + PrimGot->Relocs.remove_if([&](const std::pair<Symbol *, size_t> &P) { + return PrimGot->Global.count(P.first); + }); + + // Calculate indexes for each GOT entry. + size_t Index = HeaderEntriesNum; + for (FileGot &Got : Gots) { + Got.StartIndex = &Got == PrimGot ? 0 : Index; + for (std::pair<const OutputSection *, FileGot::PageBlock> &P : + Got.PagesMap) { + // For each output section referenced by GOT page relocations calculate + // and save into PagesMap an upper bound of MIPS GOT entries required + // to store page addresses of local symbols. We assume the worst case - + // each 64kb page of the output section has at least one GOT relocation + // against it. And take in account the case when the section intersects + // page boundaries. + P.second.FirstIndex = Index; + Index += P.second.Count; + } + for (auto &P: Got.Local16) + P.second = Index++; + for (auto &P: Got.Global) + P.second = Index++; + for (auto &P: Got.Relocs) + P.second = Index++; + for (auto &P: Got.Tls) + P.second = Index++; + for (auto &P: Got.DynTlsSymbols) { + P.second = Index; + Index += 2; + } + } + + // Update Symbol::GotIndex field to use this + // value later in the `sortMipsSymbols` function. + for (auto &P : PrimGot->Global) + P.first->GotIndex = P.second; + for (auto &P : PrimGot->Relocs) + P.first->GotIndex = P.second; + + // Create dynamic relocations. + for (FileGot &Got : Gots) { + // Create dynamic relocations for TLS entries. + for (std::pair<Symbol *, size_t> &P : Got.Tls) { + Symbol *S = P.first; + uint64_t Offset = P.second * Config->Wordsize; + if (S->IsPreemptible) + InX::RelaDyn->addReloc(Target->TlsGotRel, this, Offset, S); + } + 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->Pic) + continue; + InX::RelaDyn->addReloc(Target->TlsModuleIndexRel, this, Offset, S); + } 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->Pic) + continue; + InX::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; + InX::RelaDyn->addReloc(Target->TlsOffsetRel, this, Offset, S); + } + } + + // Do not create dynamic relocations for non-TLS + // entries in the primary GOT. + if (&Got == PrimGot) + continue; + + // Dynamic relocations for "global" entries. + for (const std::pair<Symbol *, size_t> &P : Got.Global) { + uint64_t Offset = P.second * Config->Wordsize; + InX::RelaDyn->addReloc(Target->RelativeRel, this, Offset, P.first); + } + if (!Config->Pic) + continue; + // Dynamic relocations for "local" entries in case of PIC. + for (const std::pair<const OutputSection *, FileGot::PageBlock> &L : + Got.PagesMap) { + size_t PageCount = L.second.Count; + for (size_t PI = 0; PI < PageCount; ++PI) { + uint64_t Offset = (L.second.FirstIndex + PI) * Config->Wordsize; + InX::RelaDyn->addReloc({Target->RelativeRel, this, Offset, L.first, + int64_t(PI * 0x10000)}); + } + } + for (const std::pair<GotEntry, size_t> &P : Got.Local16) { + uint64_t Offset = P.second * Config->Wordsize; + InX::RelaDyn->addReloc({Target->RelativeRel, this, Offset, true, + P.first.first, P.first.second}); + } + } +} + bool MipsGotSection::empty() const { // We add the .got section to the result for dynamic MIPS target because // its address and properties are mentioned in the .dynamic section. return Config->Relocatable; } -uint64_t MipsGotSection::getGp() const { return ElfSym::MipsGp->getVA(0); } +uint64_t MipsGotSection::getGp(const InputFile *F) const { + // For files without related GOT or files refer a primary GOT + // returns "common" _gp value. For secondary GOTs calculate + // individual _gp values. + if (!F || !F->MipsGotIndex.hasValue() || *F->MipsGotIndex == 0) + return ElfSym::MipsGp->getVA(0); + return getVA() + Gots[*F->MipsGotIndex].StartIndex * Config->Wordsize + + 0x7ff0; +} void MipsGotSection::writeTo(uint8_t *Buf) { // Set the MSB of the second GOT slot. This is not required by any @@ -830,59 +1025,67 @@ void MipsGotSection::writeTo(uint8_t *Buf) { // keep doing this for now. We really need to revisit this to see // if we had to do this. writeUint(Buf + Config->Wordsize, (uint64_t)1 << (Config->Wordsize * 8 - 1)); - Buf += HeaderEntriesNum * Config->Wordsize; - // Write 'page address' entries to the local part of the GOT. - for (std::pair<const OutputSection *, size_t> &L : PageIndexMap) { - size_t PageCount = getMipsPageCount(L.first->Size); - uint64_t FirstPageAddr = getMipsPageAddr(L.first->Addr); - for (size_t PI = 0; PI < PageCount; ++PI) { - uint8_t *Entry = Buf + (L.second + PI) * Config->Wordsize; - writeUint(Entry, FirstPageAddr + PI * 0x10000); - } - } - Buf += PageEntriesNum * Config->Wordsize; - auto AddEntry = [&](const GotEntry &SA) { - uint8_t *Entry = Buf; - Buf += Config->Wordsize; - const Symbol *Sym = SA.first; - uint64_t VA = Sym->getVA(SA.second); - if (Sym->StOther & STO_MIPS_MICROMIPS) - VA |= 1; - writeUint(Entry, VA); - }; - std::for_each(std::begin(LocalEntries), std::end(LocalEntries), AddEntry); - std::for_each(std::begin(LocalEntries32), std::end(LocalEntries32), AddEntry); - std::for_each(std::begin(GlobalEntries), std::end(GlobalEntries), AddEntry); - // Initialize TLS-related GOT entries. If the 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 - if (TlsIndexOff != -1U && !Config->Pic) - writeUint(Buf + TlsIndexOff, 1); - for (const Symbol *B : TlsEntries) { - if (!B || B->IsPreemptible) - continue; - uint64_t VA = B->getVA(); - if (B->GotIndex != -1U) { - uint8_t *Entry = Buf + B->GotIndex * Config->Wordsize; - writeUint(Entry, VA - 0x7000); + for (const FileGot &G : Gots) { + auto Write = [&](size_t I, const Symbol *S, int64_t A) { + uint64_t VA = A; + if (S) { + VA = S->getVA(A); + if (S->StOther & STO_MIPS_MICROMIPS) + VA |= 1; + } + writeUint(Buf + I * Config->Wordsize, VA); + }; + // Write 'page address' entries to the local part of the GOT. + for (const std::pair<const OutputSection *, FileGot::PageBlock> &L : + G.PagesMap) { + size_t PageCount = L.second.Count; + uint64_t FirstPageAddr = getMipsPageAddr(L.first->Addr); + for (size_t PI = 0; PI < PageCount; ++PI) + Write(L.second.FirstIndex + PI, nullptr, FirstPageAddr + PI * 0x10000); } - if (B->GlobalDynIndex != -1U) { - uint8_t *Entry = Buf + B->GlobalDynIndex * Config->Wordsize; - writeUint(Entry, 1); - Entry += Config->Wordsize; - writeUint(Entry, VA - 0x8000); + // Local, global, TLS, reloc-only entries. + // 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 + for (const std::pair<GotEntry, size_t> &P : G.Local16) + Write(P.second, P.first.first, P.first.second); + // Write VA to the primary GOT only. For secondary GOTs that + // will be done by REL32 dynamic relocations. + if (&G == &Gots.front()) + for (const std::pair<const Symbol *, size_t> &P : G.Global) + Write(P.second, P.first, 0); + for (const std::pair<Symbol *, size_t> &P : G.Relocs) + Write(P.second, P.first, 0); + for (const std::pair<Symbol *, size_t> &P : G.Tls) + Write(P.second, P.first, P.first->IsPreemptible ? 0 : -0x7000); + for (const std::pair<Symbol *, size_t> &P : G.DynTlsSymbols) { + if (P.first == nullptr && !Config->Pic) + Write(P.second, nullptr, 1); + else if (P.first && !P.first->IsPreemptible) { + // If we are emitting PIC code with relocations we mustn't write + // anything to the GOT here. When using Elf_Rel relocations the value + // one will be treated as an addend and will cause crashes at runtime + if (!Config->Pic) + Write(P.second, nullptr, 1); + Write(P.second + 1, P.first, -0x8000); + } } } } +// On PowerPC the .plt section is used to hold the table of function addresses +// instead of the .got.plt, and the type is SHT_NOBITS similar to a .bss +// section. I don't know why we have a BSS style type for the section but it is +// consitent across both 64-bit PowerPC ABIs as well as the 32-bit PowerPC ABI. GotPltSection::GotPltSection() - : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, - Target->GotPltEntrySize, ".got.plt") {} + : SyntheticSection(SHF_ALLOC | SHF_WRITE, + Config->EMachine == EM_PPC64 ? SHT_NOBITS : SHT_PROGBITS, + Target->GotPltEntrySize, + Config->EMachine == EM_PPC64 ? ".plt" : ".got.plt") {} void GotPltSection::addEntry(Symbol &Sym) { - Sym.GotPltIndex = Target->GotPltHeaderEntriesNum + Entries.size(); + assert(Sym.PltIndex == Entries.size()); Entries.push_back(&Sym); } @@ -900,16 +1103,37 @@ void GotPltSection::writeTo(uint8_t *Buf) { } } -// On ARM the IgotPltSection is part of the GotSection, on other Targets it is -// part of the .got.plt +bool GotPltSection::empty() const { + // We need to emit a GOT.PLT even if it's empty if there's a symbol that + // references the _GLOBAL_OFFSET_TABLE_ and the Target defines the symbol + // relative to the .got.plt section. + return Entries.empty() && + !(ElfSym::GlobalOffsetTable && Target->GotBaseSymInGotPlt); +} + +static StringRef getIgotPltName() { + // On ARM the IgotPltSection is part of the GotSection. + if (Config->EMachine == EM_ARM) + return ".got"; + + // On PowerPC64 the GotPltSection is renamed to '.plt' so the IgotPltSection + // needs to be named the same. + if (Config->EMachine == EM_PPC64) + return ".plt"; + + return ".got.plt"; +} + +// On PowerPC64 the GotPltSection type is SHT_NOBITS so we have to follow suit +// with the IgotPltSection. IgotPltSection::IgotPltSection() - : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, - Target->GotPltEntrySize, - Config->EMachine == EM_ARM ? ".got" : ".got.plt") {} + : SyntheticSection(SHF_ALLOC | SHF_WRITE, + Config->EMachine == EM_PPC64 ? SHT_NOBITS : SHT_PROGBITS, + Target->GotPltEntrySize, getIgotPltName()) {} void IgotPltSection::addEntry(Symbol &Sym) { Sym.IsInIgot = true; - Sym.GotPltIndex = Entries.size(); + assert(Sym.PltIndex == Entries.size()); Entries.push_back(&Sym); } @@ -1005,8 +1229,14 @@ void DynamicSection<ELFT>::addInt(int32_t Tag, uint64_t Val) { template <class ELFT> void DynamicSection<ELFT>::addInSec(int32_t Tag, InputSection *Sec) { + Entries.push_back({Tag, [=] { return Sec->getVA(0); }}); +} + +template <class ELFT> +void DynamicSection<ELFT>::addInSecRelative(int32_t Tag, InputSection *Sec) { + size_t TagOffset = Entries.size() * Entsize; Entries.push_back( - {Tag, [=] { return Sec->getParent()->Addr + Sec->OutSecOff; }}); + {Tag, [=] { return Sec->getVA(0) - (getVA() + TagOffset); }}); } template <class ELFT> @@ -1034,6 +1264,8 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() { uint32_t DtFlags1 = 0; if (Config->Bsymbolic) DtFlags |= DF_SYMBOLIC; + if (Config->ZInitfirst) + DtFlags1 |= DF_1_INITFIRST; if (Config->ZNodelete) DtFlags1 |= DF_1_NODELETE; if (Config->ZNodlopen) @@ -1046,6 +1278,8 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() { DtFlags |= DF_ORIGIN; DtFlags1 |= DF_1_ORIGIN; } + if (!Config->ZText) + DtFlags |= DF_TEXTREL; if (DtFlags) addInt(DT_FLAGS, DtFlags); @@ -1081,6 +1315,14 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() { addInt(IsRela ? DT_RELACOUNT : DT_RELCOUNT, NumRelativeRels); } } + if (InX::RelrDyn && !InX::RelrDyn->Relocs.empty()) { + addInSec(Config->UseAndroidRelrTags ? DT_ANDROID_RELR : DT_RELR, + InX::RelrDyn); + addSize(Config->UseAndroidRelrTags ? DT_ANDROID_RELRSZ : DT_RELRSZ, + InX::RelrDyn->getParent()); + addInt(Config->UseAndroidRelrTags ? DT_ANDROID_RELRENT : DT_RELRENT, + sizeof(Elf_Relr)); + } // .rel[a].plt section usually consists of two parts, containing plt and // iplt relocations. It is possible to have only iplt relocations in the // output. In that case RelaPlt is empty and have zero offset, the same offset @@ -1160,8 +1402,23 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() { else addInt(DT_MIPS_GOTSYM, InX::DynSymTab->getNumSymbols()); addInSec(DT_PLTGOT, InX::MipsGot); - if (InX::MipsRldMap) - addInSec(DT_MIPS_RLD_MAP, InX::MipsRldMap); + if (InX::MipsRldMap) { + if (!Config->Pie) + addInSec(DT_MIPS_RLD_MAP, InX::MipsRldMap); + // Store the offset to the .rld_map section + // relative to the address of the tag. + addInSecRelative(DT_MIPS_RLD_MAP_REL, InX::MipsRldMap); + } + } + + // Glink dynamic tag is required by the V2 abi if the plt section isn't empty. + if (Config->EMachine == EM_PPC64 && !InX::Plt->empty()) { + // The Glink tag points to 32 bytes before the first lazy symbol resolution + // stub, which starts directly after the header. + Entries.push_back({DT_PPC64_GLINK, [=] { + unsigned Offset = Target->PltHeaderSize - 32; + return InX::Plt->getVA(0) + Offset; + }}); } addInt(DT_NULL, 0); @@ -1181,13 +1438,16 @@ template <class ELFT> void DynamicSection<ELFT>::writeTo(uint8_t *Buf) { } uint64_t DynamicReloc::getOffset() const { - return InputSec->getOutputSection()->Addr + InputSec->getOffset(OffsetInSec); + return InputSec->getVA(OffsetInSec); } -int64_t DynamicReloc::getAddend() const { +int64_t DynamicReloc::computeAddend() const { if (UseSymVA) return Sym->getVA(Addend); - return Addend; + if (!OutputSec) + return Addend; + // See the comment in the DynamicReloc ctor. + return getMipsPageAddr(OutputSec->Addr) + Addend; } uint32_t DynamicReloc::getSymIndex() const { @@ -1202,6 +1462,23 @@ RelocationBaseSection::RelocationBaseSection(StringRef Name, uint32_t Type, : SyntheticSection(SHF_ALLOC, Type, Config->Wordsize, Name), DynamicTag(DynamicTag), SizeDynamicTag(SizeDynamicTag) {} +void RelocationBaseSection::addReloc(RelType DynType, InputSectionBase *IS, + uint64_t OffsetInSec, Symbol *Sym) { + addReloc({DynType, IS, OffsetInSec, false, Sym, 0}); +} + +void RelocationBaseSection::addReloc(RelType DynType, + InputSectionBase *InputSec, + uint64_t OffsetInSec, Symbol *Sym, + int64_t Addend, RelExpr Expr, + RelType Type) { + // 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}); +} + void RelocationBaseSection::addReloc(const DynamicReloc &Reloc) { if (Reloc.Type == Target->RelativeRel) ++NumRelativeRelocs; @@ -1218,23 +1495,17 @@ void RelocationBaseSection::finalizeContents() { getParent()->Link = Link; } +RelrBaseSection::RelrBaseSection() + : SyntheticSection(SHF_ALLOC, + Config->UseAndroidRelrTags ? SHT_ANDROID_RELR : SHT_RELR, + Config->Wordsize, ".relr.dyn") {} + template <class ELFT> static void encodeDynamicReloc(typename ELFT::Rela *P, const DynamicReloc &Rel) { if (Config->IsRela) - P->r_addend = Rel.getAddend(); + P->r_addend = Rel.computeAddend(); P->r_offset = Rel.getOffset(); - if (Config->EMachine == EM_MIPS && Rel.getInputSec() == InX::MipsGot) - // The MIPS GOT section contains dynamic relocations that correspond to TLS - // entries. These entries are placed after the global and local sections of - // the GOT. At the point when we create these relocations, the size of the - // global and local sections is unknown, so the offset that we store in the - // TLS entry's DynamicReloc is relative to the start of the TLS section of - // the GOT, rather than being relative to the start of the GOT. This line of - // code adds the size of the global and local sections to the virtual - // address computed by getOffset() in order to adjust it into the TLS - // section. - P->r_offset += InX::MipsGot->getTlsOffset(); P->setSymbolAndType(Rel.getSymIndex(), Rel.Type, Config->IsMips64EL); } @@ -1247,32 +1518,22 @@ RelocationSection<ELFT>::RelocationSection(StringRef Name, bool Sort) this->Entsize = Config->IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); } -template <class ELFT, class RelTy> -static bool compRelocations(const RelTy &A, const RelTy &B) { - bool AIsRel = A.getType(Config->IsMips64EL) == Target->RelativeRel; - bool BIsRel = B.getType(Config->IsMips64EL) == Target->RelativeRel; +static bool compRelocations(const DynamicReloc &A, const DynamicReloc &B) { + bool AIsRel = A.Type == Target->RelativeRel; + bool BIsRel = B.Type == Target->RelativeRel; if (AIsRel != BIsRel) return AIsRel; - - return A.getSymbol(Config->IsMips64EL) < B.getSymbol(Config->IsMips64EL); + return A.getSymIndex() < B.getSymIndex(); } template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *Buf) { - uint8_t *BufBegin = Buf; + if (Sort) + std::stable_sort(Relocs.begin(), Relocs.end(), compRelocations); + for (const DynamicReloc &Rel : Relocs) { encodeDynamicReloc<ELFT>(reinterpret_cast<Elf_Rela *>(Buf), Rel); Buf += Config->IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); } - - if (Sort) { - if (Config->IsRela) - std::stable_sort((Elf_Rela *)BufBegin, - (Elf_Rela *)BufBegin + Relocs.size(), - compRelocations<ELFT, Elf_Rela>); - else - std::stable_sort((Elf_Rel *)BufBegin, (Elf_Rel *)BufBegin + Relocs.size(), - compRelocations<ELFT, Elf_Rel>); - } } template <class ELFT> unsigned RelocationSection<ELFT>::getRelocOffset() { @@ -1360,10 +1621,10 @@ bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() { NonRelatives.push_back(R); } - std::sort(Relatives.begin(), Relatives.end(), - [](const Elf_Rel &A, const Elf_Rel &B) { - return A.r_offset < B.r_offset; - }); + llvm::sort(Relatives.begin(), Relatives.end(), + [](const Elf_Rel &A, const Elf_Rel &B) { + return A.r_offset < B.r_offset; + }); // Try to find groups of relative relocations which are spaced one word // apart from one another. These generally correspond to vtable entries. The @@ -1441,10 +1702,10 @@ bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() { } // Finally the non-relative relocations. - std::sort(NonRelatives.begin(), NonRelatives.end(), - [](const Elf_Rela &A, const Elf_Rela &B) { - return A.r_offset < B.r_offset; - }); + llvm::sort(NonRelatives.begin(), NonRelatives.end(), + [](const Elf_Rela &A, const Elf_Rela &B) { + return A.r_offset < B.r_offset; + }); if (!NonRelatives.empty()) { Add(NonRelatives.size()); Add(HasAddendIfRela); @@ -1467,6 +1728,97 @@ bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() { return RelocData.size() != OldSize; } +template <class ELFT> RelrSection<ELFT>::RelrSection() { + this->Entsize = Config->Wordsize; +} + +template <class ELFT> bool RelrSection<ELFT>::updateAllocSize() { + // This function computes the contents of an SHT_RELR packed relocation + // section. + // + // Proposal for adding SHT_RELR sections to generic-abi is here: + // https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg + // + // The encoded sequence of Elf64_Relr entries in a SHT_RELR section looks + // like [ AAAAAAAA BBBBBBB1 BBBBBBB1 ... AAAAAAAA BBBBBB1 ... ] + // + // i.e. start with an address, followed by any number of bitmaps. The address + // entry encodes 1 relocation. The subsequent bitmap entries encode up to 63 + // relocations each, at subsequent offsets following the last address entry. + // + // The bitmap entries must have 1 in the least significant bit. The assumption + // here is that an address cannot have 1 in lsb. Odd addresses are not + // supported. + // + // Excluding the least significant bit in the bitmap, each non-zero bit in + // the bitmap represents a relocation to be applied to a corresponding machine + // word that follows the base address word. The second least significant bit + // represents the machine word immediately following the initial address, and + // each bit that follows represents the next word, in linear order. As such, + // a single bitmap can encode up to 31 relocations in a 32-bit object, and + // 63 relocations in a 64-bit object. + // + // This encoding has a couple of interesting properties: + // 1. Looking at any entry, it is clear whether it's an address or a bitmap: + // even means address, odd means bitmap. + // 2. Just a simple list of addresses is a valid encoding. + + size_t OldSize = RelrRelocs.size(); + RelrRelocs.clear(); + + // Same as Config->Wordsize but faster because this is a compile-time + // constant. + const size_t Wordsize = sizeof(typename ELFT::uint); + + // Number of bits to use for the relocation offsets bitmap. + // Must be either 63 or 31. + const size_t NBits = Wordsize * 8 - 1; + + // Get offsets for all relative relocations and sort them. + std::vector<uint64_t> Offsets; + for (const RelativeReloc &Rel : Relocs) + Offsets.push_back(Rel.getOffset()); + llvm::sort(Offsets.begin(), Offsets.end()); + + // For each leading relocation, find following ones that can be folded + // as a bitmap and fold them. + for (size_t I = 0, E = Offsets.size(); I < E;) { + // Add a leading relocation. + RelrRelocs.push_back(Elf_Relr(Offsets[I])); + uint64_t Base = Offsets[I] + Wordsize; + ++I; + + // Find foldable relocations to construct bitmaps. + while (I < E) { + uint64_t Bitmap = 0; + + while (I < E) { + uint64_t Delta = Offsets[I] - Base; + + // If it is too far, it cannot be folded. + if (Delta >= NBits * Wordsize) + break; + + // If it is not a multiple of wordsize away, it cannot be folded. + if (Delta % Wordsize) + break; + + // Fold it. + Bitmap |= 1ULL << (Delta / Wordsize); + ++I; + } + + if (!Bitmap) + break; + + RelrRelocs.push_back(Elf_Relr((Bitmap << 1) | 1)); + Base += NBits * Wordsize; + } + } + + return RelrRelocs.size() != OldSize; +} + SymbolTableBaseSection::SymbolTableBaseSection(StringTableSection &StrTabSec) : SyntheticSection(StrTabSec.isDynamic() ? (uint64_t)SHF_ALLOC : 0, StrTabSec.isDynamic() ? SHT_DYNSYM : SHT_SYMTAB, @@ -1482,50 +1834,70 @@ SymbolTableBaseSection::SymbolTableBaseSection(StringTableSection &StrTabSec) static bool sortMipsSymbols(const SymbolTableEntry &L, const SymbolTableEntry &R) { // Sort entries related to non-local preemptible symbols by GOT indexes. - // All other entries go to the first part of GOT in arbitrary order. - bool LIsInLocalGot = !L.Sym->IsInGlobalMipsGot; - bool RIsInLocalGot = !R.Sym->IsInGlobalMipsGot; - if (LIsInLocalGot || RIsInLocalGot) - return !RIsInLocalGot; - return L.Sym->GotIndex < R.Sym->GotIndex; + // All other entries go to the beginning of a dynsym in arbitrary order. + if (L.Sym->isInGot() && R.Sym->isInGot()) + return L.Sym->GotIndex < R.Sym->GotIndex; + if (!L.Sym->isInGot() && !R.Sym->isInGot()) + return false; + return !L.Sym->isInGot(); } void SymbolTableBaseSection::finalizeContents() { getParent()->Link = StrTabSec.getParent()->SectionIndex; + if (this->Type != SHT_DYNSYM) + return; + // If it is a .dynsym, there should be no local symbols, but we need // to do a few things for the dynamic linker. - if (this->Type == SHT_DYNSYM) { - // Section's Info field has the index of the first non-local symbol. - // Because the first symbol entry is a null entry, 1 is the first. - getParent()->Info = 1; - - if (InX::GnuHashTab) { - // NB: It also sorts Symbols to meet the GNU hash table requirements. - InX::GnuHashTab->addSymbols(Symbols); - } else if (Config->EMachine == EM_MIPS) { - std::stable_sort(Symbols.begin(), Symbols.end(), sortMipsSymbols); - } - size_t I = 0; - for (const SymbolTableEntry &S : Symbols) S.Sym->DynsymIndex = ++I; - return; + // Section's Info field has the index of the first non-local symbol. + // Because the first symbol entry is a null entry, 1 is the first. + getParent()->Info = 1; + + if (InX::GnuHashTab) { + // NB: It also sorts Symbols to meet the GNU hash table requirements. + InX::GnuHashTab->addSymbols(Symbols); + } else if (Config->EMachine == EM_MIPS) { + std::stable_sort(Symbols.begin(), Symbols.end(), sortMipsSymbols); } + + size_t I = 0; + for (const SymbolTableEntry &S : Symbols) + S.Sym->DynsymIndex = ++I; } // The ELF spec requires that all local symbols precede global symbols, so we // sort symbol entries in this function. (For .dynsym, we don't do that because // symbols for dynamic linking are inherently all globals.) +// +// Aside from above, we put local symbols in groups starting with the STT_FILE +// symbol. That is convenient for purpose of identifying where are local symbols +// coming from. void SymbolTableBaseSection::postThunkContents() { - if (this->Type == SHT_DYNSYM) - return; - // move all local symbols before global symbols. - auto It = std::stable_partition( + assert(this->Type == SHT_SYMTAB); + + // Move all local symbols before global symbols. + auto E = std::stable_partition( Symbols.begin(), Symbols.end(), [](const SymbolTableEntry &S) { return S.Sym->isLocal() || S.Sym->computeBinding() == STB_LOCAL; }); - size_t NumLocals = It - Symbols.begin(); + size_t NumLocals = E - Symbols.begin(); getParent()->Info = NumLocals + 1; + + // We want to group the local symbols by file. For that we rebuild the local + // part of the symbols vector. We do not need to care about the STT_FILE + // symbols, they are already naturally placed first in each group. That + // happens because STT_FILE is always the first symbol in the object and hence + // precede all other local symbols we add for a file. + MapVector<InputFile *, std::vector<SymbolTableEntry>> Arr; + for (const SymbolTableEntry &S : llvm::make_range(Symbols.begin(), E)) + Arr[S.Sym->File].push_back(S); + + auto I = Symbols.begin(); + for (std::pair<InputFile *, std::vector<SymbolTableEntry>> &P : Arr) + for (SymbolTableEntry &Entry : P.second) + *I++ = Entry; } void SymbolTableBaseSection::addSymbol(Symbol *B) { @@ -1563,6 +1935,23 @@ SymbolTableSection<ELFT>::SymbolTableSection(StringTableSection &StrTabSec) this->Entsize = sizeof(Elf_Sym); } +static BssSection *getCommonSec(Symbol *Sym) { + if (!Config->DefineCommon) + if (auto *D = dyn_cast<Defined>(Sym)) + return dyn_cast_or_null<BssSection>(D->Section); + return nullptr; +} + +static uint32_t getSymSectionIndex(Symbol *Sym) { + if (getCommonSec(Sym)) + return SHN_COMMON; + if (!isa<Defined>(Sym) || Sym->NeedsPltAddr) + return SHN_UNDEF; + if (const OutputSection *OS = Sym->getOutputSection()) + return OS->SectionIndex >= SHN_LORESERVE ? SHN_XINDEX : OS->SectionIndex; + return SHN_ABS; +} + // Write the internal symbol table contents to the output symbol table. template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) { // The first entry is a null entry as per the ELF spec. @@ -1584,20 +1973,7 @@ template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) { } ESym->st_name = Ent.StrTabOffset; - - // Set a section index. - BssSection *CommonSec = nullptr; - if (!Config->DefineCommon) - if (auto *D = dyn_cast<Defined>(Sym)) - CommonSec = dyn_cast_or_null<BssSection>(D->Section); - if (CommonSec) - ESym->st_shndx = SHN_COMMON; - else if (const OutputSection *OutSec = Sym->getOutputSection()) - ESym->st_shndx = OutSec->SectionIndex; - else if (isa<Defined>(Sym)) - ESym->st_shndx = SHN_ABS; - else - ESym->st_shndx = SHN_UNDEF; + ESym->st_shndx = getSymSectionIndex(Ent.Sym); // Copy symbol size if it is a defined symbol. st_size is not significant // for undefined symbols, so whether copying it or not is up to us if that's @@ -1612,7 +1988,7 @@ template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) { // st_value is usually an address of a symbol, but that has a // special meaining for uninstantiated common symbols (this can // occur if -r is given). - if (CommonSec) + if (BssSection *CommonSec = getCommonSec(Ent.Sym)) ESym->st_value = CommonSec->Alignment; else ESym->st_value = Sym->getVA(); @@ -1633,9 +2009,11 @@ template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) { ESym->st_other |= STO_MIPS_PLT; if (isMicroMips()) { // Set STO_MIPS_MICROMIPS flag and less-significant bit for - // defined microMIPS symbols and shared symbols with PLT record. - if ((Sym->isDefined() && (Sym->StOther & STO_MIPS_MICROMIPS)) || - (Sym->isShared() && Sym->NeedsPltAddr)) { + // a defined microMIPS symbol and symbol should point to its + // PLT entry (in case of microMIPS, PLT entries always contain + // microMIPS code). + if (Sym->isDefined() && + ((Sym->StOther & STO_MIPS_MICROMIPS) || Sym->NeedsPltAddr)) { if (StrTabSec.isDynamic()) ESym->st_value |= 1; ESym->st_other |= STO_MIPS_MICROMIPS; @@ -1650,6 +2028,44 @@ template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) { } } +SymtabShndxSection::SymtabShndxSection() + : SyntheticSection(0, SHT_SYMTAB_SHNDX, 4, ".symtab_shndxr") { + this->Entsize = 4; +} + +void SymtabShndxSection::writeTo(uint8_t *Buf) { + // We write an array of 32 bit values, where each value has 1:1 association + // with an entry in .symtab. If the corresponding entry contains SHN_XINDEX, + // we need to write actual index, otherwise, we must write SHN_UNDEF(0). + Buf += 4; // Ignore .symtab[0] entry. + for (const SymbolTableEntry &Entry : InX::SymTab->getSymbols()) { + if (getSymSectionIndex(Entry.Sym) == SHN_XINDEX) + write32(Buf, Entry.Sym->getOutputSection()->SectionIndex); + Buf += 4; + } +} + +bool SymtabShndxSection::empty() const { + // SHT_SYMTAB can hold symbols with section indices values up to + // SHN_LORESERVE. If we need more, we want to use extension SHT_SYMTAB_SHNDX + // section. Problem is that we reveal the final section indices a bit too + // late, and we do not know them here. For simplicity, we just always create + // a .symtab_shndxr section when the amount of output sections is huge. + size_t Size = 0; + for (BaseCommand *Base : Script->SectionCommands) + if (isa<OutputSection>(Base)) + ++Size; + return Size < SHN_LORESERVE; +} + +void SymtabShndxSection::finalizeContents() { + getParent()->Link = InX::SymTab->getParent()->SectionIndex; +} + +size_t SymtabShndxSection::getSize() const { + return InX::SymTab->getNumSymbols() * 4; +} + // .hash and .gnu.hash sections contain on-disk hash tables that map // symbol names to their dynamic symbol table indices. Their purpose // is to help the dynamic linker resolve symbols quickly. If ELF files @@ -1688,12 +2104,14 @@ GnuHashTableSection::GnuHashTableSection() void GnuHashTableSection::finalizeContents() { getParent()->Link = InX::DynSymTab->getParent()->SectionIndex; - // Computes bloom filter size in word size. We want to allocate 8 + // Computes bloom filter size in word size. We want to allocate 12 // bits for each symbol. It must be a power of two. - if (Symbols.empty()) + if (Symbols.empty()) { MaskWords = 1; - else - MaskWords = NextPowerOf2((Symbols.size() - 1) / Config->Wordsize); + } else { + uint64_t NumBits = Symbols.size() * 12; + MaskWords = NextPowerOf2(NumBits / (Config->Wordsize * 8)); + } Size = 16; // Header Size += Config->Wordsize * MaskWords; // Bloom filter @@ -1711,7 +2129,7 @@ void GnuHashTableSection::writeTo(uint8_t *Buf) { write32(Buf, NBuckets); write32(Buf + 4, InX::DynSymTab->getNumSymbols() - Symbols.size()); write32(Buf + 8, MaskWords); - write32(Buf + 12, getShift2()); + write32(Buf + 12, Shift2); Buf += 16; // Write a bloom filter and a hash table. @@ -1728,12 +2146,12 @@ void GnuHashTableSection::writeTo(uint8_t *Buf) { // [1] Ulrich Drepper (2011), "How To Write Shared Libraries" (Ver. 4.1.2), // p.9, https://www.akkadia.org/drepper/dsohowto.pdf void GnuHashTableSection::writeBloomFilter(uint8_t *Buf) { - const unsigned C = Config->Wordsize * 8; + unsigned C = Config->Is64 ? 64 : 32; for (const Entry &Sym : Symbols) { size_t I = (Sym.Hash / C) & (MaskWords - 1); uint64_t Val = readUint(Buf + I * Config->Wordsize); Val |= uint64_t(1) << (Sym.Hash % C); - Val |= uint64_t(1) << ((Sym.Hash >> getShift2()) % C); + Val |= uint64_t(1) << ((Sym.Hash >> Shift2) % C); writeUint(Buf + I * Config->Wordsize, Val); } } @@ -1775,21 +2193,23 @@ void GnuHashTableSection::addSymbols(std::vector<SymbolTableEntry> &V) { // its type correctly. std::vector<SymbolTableEntry>::iterator Mid = std::stable_partition(V.begin(), V.end(), [](const SymbolTableEntry &S) { - // Shared symbols that this executable preempts are special. The dynamic - // linker has to look them up, so they have to be in the hash table. - if (auto *SS = dyn_cast<SharedSymbol>(S.Sym)) - return SS->CopyRelSec == nullptr && !SS->NeedsPltAddr; return !S.Sym->isDefined(); }); - if (Mid == V.end()) - return; // We chose load factor 4 for the on-disk hash table. For each hash // collision, the dynamic linker will compare a uint32_t hash value. - // Since the integer comparison is quite fast, we believe we can make - // the load factor even larger. 4 is just a conservative choice. + // Since the integer comparison is quite fast, we believe we can + // make the load factor even larger. 4 is just a conservative choice. + // + // Note that we don't want to create a zero-sized hash table because + // Android loader as of 2018 doesn't like a .gnu.hash containing such + // table. If that's the case, we create a hash table with one unused + // dummy slot. NBuckets = std::max<size_t>((V.end() - Mid) / 4, 1); + if (Mid == V.end()) + return; + for (SymbolTableEntry &Ent : llvm::make_range(Mid, V.end())) { Symbol *B = Ent.Sym; uint32_t Hash = hashGnu(B->getName()); @@ -1845,9 +2265,12 @@ void HashTableSection::writeTo(uint8_t *Buf) { } } -PltSection::PltSection(size_t S) - : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16, ".plt"), - HeaderSize(S) { +// On PowerPC64 the lazy symbol resolvers go into the `global linkage table` +// in the .glink section, rather then the typical .plt section. +PltSection::PltSection(bool IsIplt) + : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16, + Config->EMachine == EM_PPC64 ? ".glink" : ".plt"), + HeaderSize(IsIplt ? 0 : Target->PltHeaderSize), IsIplt(IsIplt) { // The PLT needs to be writable on SPARC as the dynamic linker will // modify the instructions in the PLT entries. if (Config->EMachine == EM_SPARCV9) @@ -1857,7 +2280,7 @@ PltSection::PltSection(size_t S) void PltSection::writeTo(uint8_t *Buf) { // At beginning of PLT but not the IPLT, we have code to call the dynamic // linker to resolve dynsyms at runtime. Write such code. - if (HeaderSize != 0) + if (!IsIplt) Target->writePltHeader(Buf); size_t Off = HeaderSize; // The IPlt is immediately after the Plt, account for this in RelOff @@ -1876,7 +2299,7 @@ void PltSection::writeTo(uint8_t *Buf) { template <class ELFT> void PltSection::addEntry(Symbol &Sym) { Sym.PltIndex = Entries.size(); RelocationBaseSection *PltRelocSection = InX::RelaPlt; - if (HeaderSize == 0) { + if (IsIplt) { PltRelocSection = InX::RelaIplt; Sym.IsInIplt = true; } @@ -1893,7 +2316,7 @@ size_t PltSection::getSize() const { // example ARM uses mapping symbols to aid disassembly void PltSection::addSymbols() { // The PLT may have symbols defined for the Header, the IPLT has no header - if (HeaderSize != 0) + if (!IsIplt) Target->addPltHeaderSymbols(*this); size_t Off = HeaderSize; for (size_t I = 0; I < Entries.size(); ++I) { @@ -1903,7 +2326,7 @@ void PltSection::addSymbols() { } unsigned PltSection::getPltRelocOff() const { - return (HeaderSize == 0) ? InX::Plt->getSize() : 0; + return IsIplt ? InX::Plt->getSize() : 0; } // The string hash function for .gdb_index. @@ -1914,16 +2337,48 @@ static uint32_t computeGdbHash(StringRef S) { return H; } -static std::vector<GdbIndexChunk::CuEntry> readCuList(DWARFContext &Dwarf) { - std::vector<GdbIndexChunk::CuEntry> Ret; +GdbIndexSection::GdbIndexSection() + : SyntheticSection(0, SHT_PROGBITS, 1, ".gdb_index") {} + +// Returns the desired size of an on-disk hash table for a .gdb_index section. +// There's a tradeoff between size and collision rate. We aim 75% utilization. +size_t GdbIndexSection::computeSymtabSize() const { + return std::max<size_t>(NextPowerOf2(Symbols.size() * 4 / 3), 1024); +} + +// Compute the output section size. +void GdbIndexSection::initOutputSize() { + Size = sizeof(GdbIndexHeader) + computeSymtabSize() * 8; + + for (GdbChunk &Chunk : Chunks) + Size += Chunk.CompilationUnits.size() * 16 + Chunk.AddressAreas.size() * 20; + + // Add the constant pool size if exists. + if (!Symbols.empty()) { + GdbSymbol &Sym = Symbols.back(); + Size += Sym.NameOff + Sym.Name.size() + 1; + } +} + +static std::vector<InputSection *> getDebugInfoSections() { + std::vector<InputSection *> Ret; + for (InputSectionBase *S : InputSections) + if (InputSection *IS = dyn_cast<InputSection>(S)) + if (IS->Name == ".debug_info") + Ret.push_back(IS); + return Ret; +} + +static std::vector<GdbIndexSection::CuEntry> readCuList(DWARFContext &Dwarf) { + std::vector<GdbIndexSection::CuEntry> Ret; for (std::unique_ptr<DWARFCompileUnit> &Cu : Dwarf.compile_units()) Ret.push_back({Cu->getOffset(), Cu->getLength() + 4}); return Ret; } -static std::vector<GdbIndexChunk::AddressEntry> +static std::vector<GdbIndexSection::AddressEntry> readAddressAreas(DWARFContext &Dwarf, InputSection *Sec) { - std::vector<GdbIndexChunk::AddressEntry> Ret; + std::vector<GdbIndexSection::AddressEntry> Ret; uint32_t CuIdx = 0; for (std::unique_ptr<DWARFCompileUnit> &Cu : Dwarf.compile_units()) { @@ -1947,218 +2402,192 @@ readAddressAreas(DWARFContext &Dwarf, InputSection *Sec) { return Ret; } -static std::vector<GdbIndexChunk::NameTypeEntry> -readPubNamesAndTypes(DWARFContext &Dwarf) { +static std::vector<GdbIndexSection::NameTypeEntry> +readPubNamesAndTypes(DWARFContext &Dwarf, uint32_t Idx) { StringRef Sec1 = Dwarf.getDWARFObj().getGnuPubNamesSection(); StringRef Sec2 = Dwarf.getDWARFObj().getGnuPubTypesSection(); - std::vector<GdbIndexChunk::NameTypeEntry> Ret; + std::vector<GdbIndexSection::NameTypeEntry> Ret; for (StringRef Sec : {Sec1, Sec2}) { DWARFDebugPubTable Table(Sec, Config->IsLE, true); - for (const DWARFDebugPubTable::Set &Set : Table.getData()) { - for (const DWARFDebugPubTable::Entry &Ent : Set.Entries) { - CachedHashStringRef S(Ent.Name, computeGdbHash(Ent.Name)); - Ret.push_back({S, Ent.Descriptor.toBits()}); - } - } + for (const DWARFDebugPubTable::Set &Set : Table.getData()) + for (const DWARFDebugPubTable::Entry &Ent : Set.Entries) + Ret.push_back({{Ent.Name, computeGdbHash(Ent.Name)}, + (Ent.Descriptor.toBits() << 24) | Idx}); } return Ret; } -static std::vector<InputSection *> getDebugInfoSections() { - std::vector<InputSection *> Ret; - for (InputSectionBase *S : InputSections) - if (InputSection *IS = dyn_cast<InputSection>(S)) - if (IS->Name == ".debug_info") - Ret.push_back(IS); - return Ret; -} +// Create a list of symbols from a given list of symbol names and types +// by uniquifying them by name. +static std::vector<GdbIndexSection::GdbSymbol> +createSymbols(ArrayRef<std::vector<GdbIndexSection::NameTypeEntry>> NameTypes) { + typedef GdbIndexSection::GdbSymbol GdbSymbol; + typedef GdbIndexSection::NameTypeEntry NameTypeEntry; -void GdbIndexSection::fixCuIndex() { - uint32_t Idx = 0; - for (GdbIndexChunk &Chunk : Chunks) { - for (GdbIndexChunk::AddressEntry &Ent : Chunk.AddressAreas) - Ent.CuIndex += Idx; - Idx += Chunk.CompilationUnits.size(); - } -} + // The number of symbols we will handle in this function is of the order + // of millions for very large executables, so we use multi-threading to + // speed it up. + size_t NumShards = 32; + size_t Concurrency = 1; + if (ThreadsEnabled) + Concurrency = + std::min<size_t>(PowerOf2Floor(hardware_concurrency()), NumShards); -std::vector<std::vector<uint32_t>> GdbIndexSection::createCuVectors() { - std::vector<std::vector<uint32_t>> Ret; - uint32_t Idx = 0; - uint32_t Off = 0; + // A sharded map to uniquify symbols by name. + std::vector<DenseMap<CachedHashStringRef, size_t>> Map(NumShards); + size_t Shift = 32 - countTrailingZeros(NumShards); - for (GdbIndexChunk &Chunk : Chunks) { - for (GdbIndexChunk::NameTypeEntry &Ent : Chunk.NamesAndTypes) { - GdbSymbol *&Sym = Symbols[Ent.Name]; - if (!Sym) { - Sym = make<GdbSymbol>(GdbSymbol{Ent.Name.hash(), Off, Ret.size()}); - Off += Ent.Name.size() + 1; - Ret.push_back({}); - } + // Instantiate GdbSymbols while uniqufying them by name. + std::vector<std::vector<GdbSymbol>> Symbols(NumShards); + parallelForEachN(0, Concurrency, [&](size_t ThreadId) { + for (ArrayRef<NameTypeEntry> Entries : NameTypes) { + for (const NameTypeEntry &Ent : Entries) { + size_t ShardId = Ent.Name.hash() >> Shift; + if ((ShardId & (Concurrency - 1)) != ThreadId) + continue; + + size_t &Idx = Map[ShardId][Ent.Name]; + if (Idx) { + Symbols[ShardId][Idx - 1].CuVector.push_back(Ent.Type); + continue; + } - // gcc 5.4.1 produces a buggy .debug_gnu_pubnames that contains - // duplicate entries, so we want to dedup them. - std::vector<uint32_t> &Vec = Ret[Sym->CuVectorIndex]; - uint32_t Val = (Ent.Type << 24) | Idx; - if (Vec.empty() || Vec.back() != Val) - Vec.push_back(Val); + Idx = Symbols[ShardId].size() + 1; + Symbols[ShardId].push_back({Ent.Name, {Ent.Type}, 0, 0}); + } } - Idx += Chunk.CompilationUnits.size(); + }); + + size_t NumSymbols = 0; + for (ArrayRef<GdbSymbol> V : Symbols) + NumSymbols += V.size(); + + // The return type is a flattened vector, so we'll copy each vector + // contents to Ret. + std::vector<GdbSymbol> Ret; + Ret.reserve(NumSymbols); + for (std::vector<GdbSymbol> &Vec : Symbols) + for (GdbSymbol &Sym : Vec) + Ret.push_back(std::move(Sym)); + + // CU vectors and symbol names are adjacent in the output file. + // We can compute their offsets in the output file now. + size_t Off = 0; + for (GdbSymbol &Sym : Ret) { + Sym.CuVectorOff = Off; + Off += (Sym.CuVector.size() + 1) * 4; + } + for (GdbSymbol &Sym : Ret) { + Sym.NameOff = Off; + Off += Sym.Name.size() + 1; } - StringPoolSize = Off; return Ret; } -template <class ELFT> GdbIndexSection *elf::createGdbIndex() { - // Gather debug info to create a .gdb_index section. +// Returns a newly-created .gdb_index section. +template <class ELFT> GdbIndexSection *GdbIndexSection::create() { std::vector<InputSection *> Sections = getDebugInfoSections(); - std::vector<GdbIndexChunk> Chunks(Sections.size()); - - parallelForEachN(0, Chunks.size(), [&](size_t I) { - ObjFile<ELFT> *File = Sections[I]->getFile<ELFT>(); - DWARFContext Dwarf(make_unique<LLDDwarfObj<ELFT>>(File)); - - Chunks[I].DebugInfoSec = Sections[I]; - Chunks[I].CompilationUnits = readCuList(Dwarf); - Chunks[I].AddressAreas = readAddressAreas(Dwarf, Sections[I]); - Chunks[I].NamesAndTypes = readPubNamesAndTypes(Dwarf); - }); // .debug_gnu_pub{names,types} are useless in executables. // They are present in input object files solely for creating - // a .gdb_index. So we can remove it from the output. + // a .gdb_index. So we can remove them from the output. for (InputSectionBase *S : InputSections) if (S->Name == ".debug_gnu_pubnames" || S->Name == ".debug_gnu_pubtypes") S->Live = false; - // Create a .gdb_index and returns it. - return make<GdbIndexSection>(std::move(Chunks)); -} - -static size_t getCuSize(ArrayRef<GdbIndexChunk> Arr) { - size_t Ret = 0; - for (const GdbIndexChunk &D : Arr) - Ret += D.CompilationUnits.size(); - return Ret; -} - -static size_t getAddressAreaSize(ArrayRef<GdbIndexChunk> Arr) { - size_t Ret = 0; - for (const GdbIndexChunk &D : Arr) - Ret += D.AddressAreas.size(); - return Ret; -} + std::vector<GdbChunk> Chunks(Sections.size()); + std::vector<std::vector<NameTypeEntry>> NameTypes(Sections.size()); -std::vector<GdbSymbol *> GdbIndexSection::createGdbSymtab() { - uint32_t Size = NextPowerOf2(Symbols.size() * 4 / 3); - if (Size < 1024) - Size = 1024; - - uint32_t Mask = Size - 1; - std::vector<GdbSymbol *> Ret(Size); + parallelForEachN(0, Sections.size(), [&](size_t I) { + ObjFile<ELFT> *File = Sections[I]->getFile<ELFT>(); + DWARFContext Dwarf(make_unique<LLDDwarfObj<ELFT>>(File)); - for (auto &KV : Symbols) { - GdbSymbol *Sym = KV.second; - uint32_t I = Sym->NameHash & Mask; - uint32_t Step = ((Sym->NameHash * 17) & Mask) | 1; + Chunks[I].Sec = Sections[I]; + Chunks[I].CompilationUnits = readCuList(Dwarf); + Chunks[I].AddressAreas = readAddressAreas(Dwarf, Sections[I]); + NameTypes[I] = readPubNamesAndTypes(Dwarf, I); + }); - while (Ret[I]) - I = (I + Step) & Mask; - Ret[I] = Sym; - } + auto *Ret = make<GdbIndexSection>(); + Ret->Chunks = std::move(Chunks); + Ret->Symbols = createSymbols(NameTypes); + Ret->initOutputSize(); return Ret; } -GdbIndexSection::GdbIndexSection(std::vector<GdbIndexChunk> &&C) - : SyntheticSection(0, SHT_PROGBITS, 1, ".gdb_index"), Chunks(std::move(C)) { - fixCuIndex(); - CuVectors = createCuVectors(); - GdbSymtab = createGdbSymtab(); - - // Compute offsets early to know the section size. - // Each chunk size needs to be in sync with what we write in writeTo. - CuTypesOffset = CuListOffset + getCuSize(Chunks) * 16; - SymtabOffset = CuTypesOffset + getAddressAreaSize(Chunks) * 20; - ConstantPoolOffset = SymtabOffset + GdbSymtab.size() * 8; - - size_t Off = 0; - for (ArrayRef<uint32_t> Vec : CuVectors) { - CuVectorOffsets.push_back(Off); - Off += (Vec.size() + 1) * 4; - } - StringPoolOffset = ConstantPoolOffset + Off; -} - -size_t GdbIndexSection::getSize() const { - return StringPoolOffset + StringPoolSize; -} - void GdbIndexSection::writeTo(uint8_t *Buf) { - // Write the section header. - write32le(Buf, 7); - write32le(Buf + 4, CuListOffset); - write32le(Buf + 8, CuTypesOffset); - write32le(Buf + 12, CuTypesOffset); - write32le(Buf + 16, SymtabOffset); - write32le(Buf + 20, ConstantPoolOffset); - Buf += 24; + // Write the header. + auto *Hdr = reinterpret_cast<GdbIndexHeader *>(Buf); + uint8_t *Start = Buf; + Hdr->Version = 7; + Buf += sizeof(*Hdr); // Write the CU list. - for (GdbIndexChunk &D : Chunks) { - for (GdbIndexChunk::CuEntry &Cu : D.CompilationUnits) { - write64le(Buf, D.DebugInfoSec->OutSecOff + Cu.CuOffset); + Hdr->CuListOff = Buf - Start; + for (GdbChunk &Chunk : Chunks) { + for (CuEntry &Cu : Chunk.CompilationUnits) { + write64le(Buf, Chunk.Sec->OutSecOff + Cu.CuOffset); write64le(Buf + 8, Cu.CuLength); Buf += 16; } } // Write the address area. - for (GdbIndexChunk &D : Chunks) { - for (GdbIndexChunk::AddressEntry &E : D.AddressAreas) { - uint64_t BaseAddr = - E.Section->getParent()->Addr + E.Section->getOffset(0); + Hdr->CuTypesOff = Buf - Start; + Hdr->AddressAreaOff = Buf - Start; + uint32_t CuOff = 0; + for (GdbChunk &Chunk : Chunks) { + for (AddressEntry &E : Chunk.AddressAreas) { + uint64_t BaseAddr = E.Section->getVA(0); write64le(Buf, BaseAddr + E.LowAddress); write64le(Buf + 8, BaseAddr + E.HighAddress); - write32le(Buf + 16, E.CuIndex); + write32le(Buf + 16, E.CuIndex + CuOff); Buf += 20; } + CuOff += Chunk.CompilationUnits.size(); } - // Write the symbol table. - for (GdbSymbol *Sym : GdbSymtab) { - if (Sym) { - write32le(Buf, Sym->NameOffset + StringPoolOffset - ConstantPoolOffset); - write32le(Buf + 4, CuVectorOffsets[Sym->CuVectorIndex]); - } - Buf += 8; + // Write the on-disk open-addressing hash table containing symbols. + Hdr->SymtabOff = Buf - Start; + size_t SymtabSize = computeSymtabSize(); + uint32_t Mask = SymtabSize - 1; + + for (GdbSymbol &Sym : Symbols) { + uint32_t H = Sym.Name.hash(); + uint32_t I = H & Mask; + uint32_t Step = ((H * 17) & Mask) | 1; + + while (read32le(Buf + I * 8)) + I = (I + Step) & Mask; + + write32le(Buf + I * 8, Sym.NameOff); + write32le(Buf + I * 8 + 4, Sym.CuVectorOff); } + Buf += SymtabSize * 8; + + // Write the string pool. + Hdr->ConstantPoolOff = Buf - Start; + for (GdbSymbol &Sym : Symbols) + memcpy(Buf + Sym.NameOff, Sym.Name.data(), Sym.Name.size()); + // Write the CU vectors. - for (ArrayRef<uint32_t> Vec : CuVectors) { - write32le(Buf, Vec.size()); + for (GdbSymbol &Sym : Symbols) { + write32le(Buf, Sym.CuVector.size()); Buf += 4; - for (uint32_t Val : Vec) { + for (uint32_t Val : Sym.CuVector) { write32le(Buf, Val); Buf += 4; } } - - // Write the string pool. - for (auto &KV : Symbols) { - CachedHashStringRef S = KV.first; - GdbSymbol *Sym = KV.second; - size_t Off = Sym->NameOffset; - memcpy(Buf + Off, S.val().data(), S.size()); - Buf[Off + S.size()] = '\0'; - } } bool GdbIndexSection::empty() const { return !Out::DebugInfo; } EhFrameHeader::EhFrameHeader() - : SyntheticSection(SHF_ALLOC, SHT_PROGBITS, 1, ".eh_frame_hdr") {} + : SyntheticSection(SHF_ALLOC, SHT_PROGBITS, 4, ".eh_frame_hdr") {} // .eh_frame_hdr contains a binary search table of pointers to FDEs. // Each entry of the search table consists of two values, @@ -2169,14 +2598,6 @@ void EhFrameHeader::writeTo(uint8_t *Buf) { std::vector<FdeData> Fdes = InX::EhFrame->getFdeData(); - // Sort the FDE list by their PC and uniqueify. Usually there is only - // one FDE for a PC (i.e. function), but if ICF merges two functions - // into one, there can be more than one FDEs pointing to the address. - auto Less = [](const FdeData &A, const FdeData &B) { return A.Pc < B.Pc; }; - std::stable_sort(Fdes.begin(), Fdes.end(), Less); - auto Eq = [](const FdeData &A, const FdeData &B) { return A.Pc == B.Pc; }; - Fdes.erase(std::unique(Fdes.begin(), Fdes.end(), Eq), Fdes.end()); - Buf[0] = 1; Buf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4; Buf[2] = DW_EH_PE_udata4; @@ -2185,10 +2606,9 @@ void EhFrameHeader::writeTo(uint8_t *Buf) { write32(Buf + 8, Fdes.size()); Buf += 12; - uint64_t VA = this->getVA(); for (FdeData &Fde : Fdes) { - write32(Buf, Fde.Pc - VA); - write32(Buf + 4, Fde.FdeVA - VA); + write32(Buf, Fde.PcRel); + write32(Buf + 4, Fde.FdeVARel); Buf += 8; } } @@ -2298,11 +2718,9 @@ VersionNeedSection<ELFT>::VersionNeedSection() NextIndex = getVerDefNum() + 1; } -template <class ELFT> -void VersionNeedSection<ELFT>::addSymbol(SharedSymbol *SS) { - SharedFile<ELFT> &File = SS->getFile<ELFT>(); - const typename ELFT::Verdef *Ver = File.Verdefs[SS->VerdefIndex]; - if (!Ver) { +template <class ELFT> void VersionNeedSection<ELFT>::addSymbol(Symbol *SS) { + auto &File = cast<SharedFile<ELFT>>(*SS->File); + if (SS->VerdefIndex == VER_NDX_GLOBAL) { SS->VersionId = VER_NDX_GLOBAL; return; } @@ -2312,7 +2730,9 @@ void VersionNeedSection<ELFT>::addSymbol(SharedSymbol *SS) { // for the soname. if (File.VerdefMap.empty()) Needed.push_back({&File, InX::DynStrTab->addString(File.SoName)}); + const typename ELFT::Verdef *Ver = File.Verdefs[SS->VerdefIndex]; typename SharedFile<ELFT>::NeededVer &NV = File.VerdefMap[Ver]; + // If we don't already know that we need an Elf_Vernaux for this Elf_Verdef, // prepare to create one by allocating a version identifier and creating a // dynstr entry for the version name. @@ -2476,11 +2896,20 @@ static MergeSyntheticSection *createMergeSynthetic(StringRef Name, return make<MergeNoTailSection>(Name, Type, Flags, Alignment); } -// Debug sections may be compressed by zlib. Uncompress if exists. +// Debug sections may be compressed by zlib. Decompress if exists. void elf::decompressSections() { + parallelForEach(InputSections, + [](InputSectionBase *Sec) { Sec->maybeDecompress(); }); +} + +template <class ELFT> void elf::splitSections() { + // splitIntoPieces needs to be called on each MergeInputSection + // before calling finalizeContents(). parallelForEach(InputSections, [](InputSectionBase *Sec) { - if (Sec->Live) - Sec->maybeUncompress(); + if (auto *S = dyn_cast<MergeInputSection>(Sec)) + S->splitIntoPieces(); + else if (auto *Eh = dyn_cast<EhInputSection>(Sec)) + Eh->split<ELFT>(); }); } @@ -2492,14 +2921,6 @@ void elf::decompressSections() { // that it replaces. It then finalizes each synthetic section in order // to compute an output offset for each piece of each input section. void elf::mergeSections() { - // splitIntoPieces needs to be called on each MergeInputSection - // before calling finalizeContents(). Do that first. - parallelForEach(InputSections, [](InputSectionBase *Sec) { - if (Sec->Live) - if (auto *S = dyn_cast<MergeInputSection>(Sec)) - S->splitIntoPieces(); - }); - std::vector<MergeSyntheticSection *> MergeSections; for (InputSectionBase *&S : InputSections) { MergeInputSection *MS = dyn_cast<MergeInputSection>(S); @@ -2561,8 +2982,7 @@ ARMExidxSentinelSection::ARMExidxSentinelSection() // address described by any other table entry. void ARMExidxSentinelSection::writeTo(uint8_t *Buf) { assert(Highest); - uint64_t S = - Highest->getParent()->Addr + Highest->getOffset(Highest->getSize()); + uint64_t S = Highest->getVA(Highest->getSize()); uint64_t P = getVA(); Target->relocateOne(Buf, R_ARM_PREL31, S - P); write32le(Buf + 4, 1); @@ -2570,15 +2990,16 @@ void ARMExidxSentinelSection::writeTo(uint8_t *Buf) { // The sentinel has to be removed if there are no other .ARM.exidx entries. bool ARMExidxSentinelSection::empty() const { - OutputSection *OS = getParent(); - for (auto *B : OS->SectionCommands) - if (auto *ISD = dyn_cast<InputSectionDescription>(B)) - for (auto *S : ISD->Sections) - if (!isa<ARMExidxSentinelSection>(S)) - return false; + for (InputSection *IS : getInputSections(getParent())) + if (!isa<ARMExidxSentinelSection>(IS)) + return false; return true; } +bool ARMExidxSentinelSection::classof(const SectionBase *D) { + return D->kind() == InputSectionBase::Synthetic && D->Type == SHT_ARM_EXIDX; +} + ThunkSection::ThunkSection(OutputSection *OS, uint64_t Off) : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, Config->Wordsize, ".text.thunk") { @@ -2587,16 +3008,13 @@ ThunkSection::ThunkSection(OutputSection *OS, uint64_t Off) } void ThunkSection::addThunk(Thunk *T) { - uint64_t Off = alignTo(Size, T->Alignment); - T->Offset = Off; Thunks.push_back(T); T->addSymbols(*this); - Size = Off + T->size(); } void ThunkSection::writeTo(uint8_t *Buf) { - for (const Thunk *T : Thunks) - T->writeTo(Buf + T->Offset, *this); + for (Thunk *T : Thunks) + T->writeTo(Buf + T->Offset); } InputSection *ThunkSection::getTargetInputSection() const { @@ -2606,6 +3024,20 @@ InputSection *ThunkSection::getTargetInputSection() const { return T->getTargetInputSection(); } +bool ThunkSection::assignOffsets() { + uint64_t Off = 0; + for (Thunk *T : Thunks) { + Off = alignTo(Off, T->Alignment); + T->setOffset(Off); + uint32_t Size = T->size(); + T->getThunkTargetSym()->Size = Size; + Off += Size; + } + bool Changed = Off != Size; + Size = Off; + return Changed; +} + InputSection *InX::ARMAttributes; BssSection *InX::Bss; BssSection *InX::BssRelRo; @@ -2627,16 +3059,23 @@ MipsRldMapSection *InX::MipsRldMap; PltSection *InX::Plt; PltSection *InX::Iplt; RelocationBaseSection *InX::RelaDyn; +RelrBaseSection *InX::RelrDyn; RelocationBaseSection *InX::RelaPlt; RelocationBaseSection *InX::RelaIplt; StringTableSection *InX::ShStrTab; StringTableSection *InX::StrTab; SymbolTableBaseSection *InX::SymTab; +SymtabShndxSection *InX::SymTabShndx; + +template GdbIndexSection *GdbIndexSection::create<ELF32LE>(); +template GdbIndexSection *GdbIndexSection::create<ELF32BE>(); +template GdbIndexSection *GdbIndexSection::create<ELF64LE>(); +template GdbIndexSection *GdbIndexSection::create<ELF64BE>(); -template GdbIndexSection *elf::createGdbIndex<ELF32LE>(); -template GdbIndexSection *elf::createGdbIndex<ELF32BE>(); -template GdbIndexSection *elf::createGdbIndex<ELF64LE>(); -template GdbIndexSection *elf::createGdbIndex<ELF64BE>(); +template void elf::splitSections<ELF32LE>(); +template void elf::splitSections<ELF32BE>(); +template void elf::splitSections<ELF64LE>(); +template void elf::splitSections<ELF64BE>(); template void EhFrameSection::addSection<ELF32LE>(InputSectionBase *); template void EhFrameSection::addSection<ELF32BE>(InputSectionBase *); @@ -2648,6 +3087,11 @@ template void PltSection::addEntry<ELF32BE>(Symbol &Sym); template void PltSection::addEntry<ELF64LE>(Symbol &Sym); template void PltSection::addEntry<ELF64BE>(Symbol &Sym); +template void MipsGotSection::build<ELF32LE>(); +template void MipsGotSection::build<ELF32BE>(); +template void MipsGotSection::build<ELF64LE>(); +template void MipsGotSection::build<ELF64BE>(); + template class elf::MipsAbiFlagsSection<ELF32LE>; template class elf::MipsAbiFlagsSection<ELF32BE>; template class elf::MipsAbiFlagsSection<ELF64LE>; @@ -2678,6 +3122,11 @@ template class elf::AndroidPackedRelocationSection<ELF32BE>; template class elf::AndroidPackedRelocationSection<ELF64LE>; template class elf::AndroidPackedRelocationSection<ELF64BE>; +template class elf::RelrSection<ELF32LE>; +template class elf::RelrSection<ELF32BE>; +template class elf::RelrSection<ELF64LE>; +template class elf::RelrSection<ELF64BE>; + template class elf::SymbolTableSection<ELF32LE>; template class elf::SymbolTableSection<ELF32BE>; template class elf::SymbolTableSection<ELF64LE>; diff --git a/contrib/llvm/tools/lld/ELF/SyntheticSections.h b/contrib/llvm/tools/lld/ELF/SyntheticSections.h index a990590513bb..a780c6631374 100644 --- a/contrib/llvm/tools/lld/ELF/SyntheticSections.h +++ b/contrib/llvm/tools/lld/ELF/SyntheticSections.h @@ -26,10 +26,12 @@ #include "InputSection.h" #include "llvm/ADT/MapVector.h" #include "llvm/MC/StringTableBuilder.h" +#include "llvm/Support/Endian.h" #include <functional> namespace lld { namespace elf { +class Defined; class SharedSymbol; class SyntheticSection : public InputSection { @@ -51,7 +53,6 @@ public: // If any additional finalization of contents are needed post thunk creation. virtual void postThunkContents() {} virtual bool empty() const { return false; } - uint64_t getVA() const; static bool classof(const SectionBase *D) { return D->kind() == InputSectionBase::Synthetic; @@ -78,13 +79,18 @@ public: size_t NumFdes = 0; struct FdeData { - uint32_t Pc; - uint32_t FdeVA; + uint32_t PcRel; + uint32_t FdeVARel; }; std::vector<FdeData> getFdeData() const; + ArrayRef<CieRecord *> getCieRecords() const { return CieRecords; } private: + // This is used only when parsing EhInputSection. We keep it here to avoid + // allocating one for each EhInputSection. + llvm::DenseMap<size_t, CieRecord *> OffsetToCie; + uint64_t Size = 0; template <class ELFT, class RelTy> @@ -173,12 +179,21 @@ public: bool updateAllocSize() override; void finalizeContents() override; bool empty() const override; - void addEntry(Symbol &Sym, int64_t Addend, RelExpr Expr); - bool addDynTlsEntry(Symbol &Sym); - bool addTlsIndex(); - uint64_t getPageEntryOffset(const Symbol &B, int64_t Addend) const; - uint64_t getSymEntryOffset(const Symbol &B, int64_t Addend) const; - uint64_t getGlobalDynOffset(const Symbol &B) const; + + // Join separate GOTs built for each input file to generate + // primary and optional multiple secondary GOTs. + template <class ELFT> void build(); + + void addEntry(InputFile &File, Symbol &Sym, int64_t Addend, RelExpr Expr); + void addDynTlsEntry(InputFile &File, Symbol &Sym); + void addTlsIndex(InputFile &File); + + uint64_t getPageEntryOffset(const InputFile *F, const Symbol &S, + int64_t Addend) const; + uint64_t getSymEntryOffset(const InputFile *F, const Symbol &S, + int64_t Addend) const; + uint64_t getGlobalDynOffset(const InputFile *F, const Symbol &S) const; + uint64_t getTlsIndexOffset(const InputFile *F) const; // Returns the symbol which corresponds to the first entry of the global part // of GOT on MIPS platform. It is required to fill up MIPS-specific dynamic @@ -190,13 +205,8 @@ public: // the number of reserved entries. unsigned getLocalEntriesNum() const; - // Returns offset of TLS part of the MIPS GOT table. This part goes - // after 'local' and 'global' entries. - uint64_t getTlsOffset() const; - - uint32_t getTlsIndexOff() const { return TlsIndexOff; } - - uint64_t getGp() const; + // Return _gp value for primary GOT (nullptr) or particular input file. + uint64_t getGp(const InputFile *F = nullptr) const; private: // MIPS GOT consists of three parts: local, global and tls. Each part @@ -235,32 +245,110 @@ private: // addressing, but MIPS ABI requires that these entries be present in GOT. // TLS entries: // Entries created by TLS relocations. + // + // If the sum of local, global and tls entries is less than 64K only single + // got is enough. Otherwise, multi-got is created. Series of primary and + // multiple secondary GOTs have the following layout: + // - Primary GOT + // Header + // Local entries + // Global entries + // Relocation only entries + // TLS entries + // + // - Secondary GOT + // Local entries + // Global entries + // TLS entries + // ... + // + // All GOT entries required by relocations from a single input file entirely + // belong to either primary or one of secondary GOTs. To reference GOT entries + // each GOT has its own _gp value points to the "middle" of the GOT. + // In the code this value loaded to the register which is used for GOT access. + // + // MIPS 32 function's prologue: + // lui v0,0x0 + // 0: R_MIPS_HI16 _gp_disp + // addiu v0,v0,0 + // 4: R_MIPS_LO16 _gp_disp + // + // MIPS 64: + // lui at,0x0 + // 14: R_MIPS_GPREL16 main + // + // Dynamic linker does not know anything about secondary GOTs and cannot + // use a regular MIPS mechanism for GOT entries initialization. So we have + // to use an approach accepted by other architectures and create dynamic + // relocations R_MIPS_REL32 to initialize global entries (and local in case + // of PIC code) in secondary GOTs. But ironically MIPS dynamic linker + // requires GOT entries and correspondingly ordered dynamic symbol table + // entries to deal with dynamic relocations. To handle this problem + // relocation-only section in the primary GOT contains entries for all + // symbols referenced in global parts of secondary GOTs. Although the sum + // of local and normal global entries of the primary got should be less + // than 64K, the size of the primary got (including relocation-only entries + // can be greater than 64K, because parts of the primary got that overflow + // the 64K limit are used only by the dynamic linker at dynamic link-time + // and not by 16-bit gp-relative addressing at run-time. + // + // For complete multi-GOT description see the following link + // https://dmz-portal.mips.com/wiki/MIPS_Multi_GOT // Number of "Header" entries. static const unsigned HeaderEntriesNum = 2; - // Number of allocated "Page" entries. - uint32_t PageEntriesNum = 0; - // Map output sections referenced by MIPS GOT relocations - // to the first index of "Page" entries allocated for this section. - llvm::SmallMapVector<const OutputSection *, size_t, 16> PageIndexMap; - - typedef std::pair<const Symbol *, uint64_t> GotEntry; - typedef std::vector<GotEntry> GotEntries; - // Map from Symbol-Addend pair to the GOT index. - llvm::DenseMap<GotEntry, size_t> EntryIndexMap; - // Local entries (16-bit access). - GotEntries LocalEntries; - // Local entries (32-bit access). - GotEntries LocalEntries32; - - // Normal and reloc-only global entries. - GotEntries GlobalEntries; - - // TLS entries. - std::vector<const Symbol *> TlsEntries; - uint32_t TlsIndexOff = -1; uint64_t Size = 0; + + size_t LocalEntriesNum = 0; + + // Symbol and addend. + typedef std::pair<Symbol *, int64_t> GotEntry; + + struct FileGot { + InputFile *File = nullptr; + size_t StartIndex = 0; + + struct PageBlock { + size_t FirstIndex = 0; + size_t Count = 0; + }; + + // Map output sections referenced by MIPS GOT relocations + // to the description (index/count) "page" entries allocated + // for this section. + llvm::SmallMapVector<const OutputSection *, PageBlock, 16> PagesMap; + // Maps from Symbol+Addend pair or just Symbol to the GOT entry index. + llvm::MapVector<GotEntry, size_t> Local16; + llvm::MapVector<GotEntry, size_t> Local32; + llvm::MapVector<Symbol *, size_t> Global; + llvm::MapVector<Symbol *, size_t> Relocs; + llvm::MapVector<Symbol *, size_t> Tls; + // Set of symbols referenced by dynamic TLS relocations. + llvm::MapVector<Symbol *, size_t> DynTlsSymbols; + + // Total number of all entries. + size_t getEntriesNum() const; + // Number of "page" entries. + size_t getPageEntriesNum() const; + // Number of entries require 16-bit index to access. + size_t getIndexedEntriesNum() const; + + bool isOverflow() const; + }; + + // Container of GOT created for each input file. + // After building a final series of GOTs this container + // holds primary and secondary GOT's. + std::vector<FileGot> Gots; + + // Return (and create if necessary) `FileGot`. + FileGot &getGot(InputFile &F); + + // Try to merge two GOTs. In case of success the `Dst` contains + // result of merging and the function returns true. In case of + // ovwerflow the `Dst` is unchanged and the function returns false. + bool tryMergeGots(FileGot & Dst, FileGot & Src, bool IsPrimary); }; class GotPltSection final : public SyntheticSection { @@ -269,7 +357,7 @@ public: void addEntry(Symbol &Sym); size_t getSize() const override; void writeTo(uint8_t *Buf) override; - bool empty() const override { return Entries.empty(); } + bool empty() const override; private: std::vector<const Symbol *> Entries; @@ -310,30 +398,48 @@ private: class DynamicReloc { public: - DynamicReloc(uint32_t Type, const InputSectionBase *InputSec, + 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) {} + 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. + 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) {} uint64_t getOffset() const; - int64_t getAddend() const; uint32_t getSymIndex() const; const InputSectionBase *getInputSec() const { return InputSec; } - uint32_t Type; + // 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. + int64_t computeAddend() const; + + RelType Type; private: Symbol *Sym; const InputSectionBase *InputSec = nullptr; 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; int64_t Addend; + const OutputSection *OutputSec; }; template <class ELFT> class DynamicSection final : public SyntheticSection { typedef typename ELFT::Dyn Elf_Dyn; typedef typename ELFT::Rel Elf_Rel; typedef typename ELFT::Rela Elf_Rela; + typedef typename ELFT::Relr Elf_Relr; typedef typename ELFT::Shdr Elf_Shdr; typedef typename ELFT::Sym Elf_Sym; @@ -350,6 +456,7 @@ private: void add(int32_t Tag, std::function<uint64_t()> Fn); void addInt(int32_t Tag, uint64_t Val); void addInSec(int32_t Tag, InputSection *Sec); + void addInSecRelative(int32_t Tag, InputSection *Sec); void addOutSec(int32_t Tag, OutputSection *Sec); void addSize(int32_t Tag, OutputSection *Sec); void addSym(int32_t Tag, Symbol *Sym); @@ -361,6 +468,13 @@ class RelocationBaseSection : public SyntheticSection { public: RelocationBaseSection(StringRef Name, uint32_t Type, int32_t DynamicTag, int32_t SizeDynamicTag); + void addReloc(RelType DynType, InputSectionBase *IS, 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); void addReloc(const DynamicReloc &Reloc); bool empty() const override { return Relocs.empty(); } size_t getSize() const override { return Relocs.size() * this->Entsize; } @@ -405,6 +519,39 @@ private: SmallVector<char, 0> RelocData; }; +struct RelativeReloc { + uint64_t getOffset() const { return InputSec->getVA(OffsetInSec); } + + const InputSectionBase *InputSec; + uint64_t OffsetInSec; +}; + +class RelrBaseSection : public SyntheticSection { +public: + RelrBaseSection(); + std::vector<RelativeReloc> Relocs; +}; + +// RelrSection is used to encode offsets for relative relocations. +// Proposal for adding SHT_RELR sections to generic-abi is here: +// https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg +// For more details, see the comment in RelrSection::updateAllocSize(). +template <class ELFT> class RelrSection final : public RelrBaseSection { + typedef typename ELFT::Relr Elf_Relr; + +public: + RelrSection(); + + bool updateAllocSize() override; + size_t getSize() const override { return RelrRelocs.size() * this->Entsize; } + void writeTo(uint8_t *Buf) override { + memcpy(Buf, RelrRelocs.data(), getSize()); + } + +private: + std::vector<Elf_Relr> RelrRelocs; +}; + struct SymbolTableEntry { Symbol *Sym; size_t StrTabOffset; @@ -441,6 +588,16 @@ public: void writeTo(uint8_t *Buf) override; }; +class SymtabShndxSection final : public SyntheticSection { +public: + SymtabShndxSection(); + + void writeTo(uint8_t *Buf) override; + size_t getSize() const override; + bool empty() const override; + void finalizeContents() override; +}; + // Outputs GNU Hash section. For detailed explanation see: // https://blogs.oracle.com/ali/entry/gnu_hash_elf_sections class GnuHashTableSection final : public SyntheticSection { @@ -455,7 +612,7 @@ public: void addSymbols(std::vector<SymbolTableEntry> &Symbols); private: - size_t getShift2() const { return Config->Is64 ? 6 : 5; } + enum { Shift2 = 6 }; void writeBloomFilter(uint8_t *Buf); void writeHashTable(uint8_t *Buf); @@ -484,13 +641,13 @@ private: size_t Size = 0; }; -// The PltSection is used for both the Plt and Iplt. The former always has a +// The PltSection is used for both the Plt and Iplt. The former usually has a // header as its first entry that is used at run-time to resolve lazy binding. // The latter is used for GNU Ifunc symbols, that will be subject to a // Target->IRelativeRel. class PltSection : public SyntheticSection { public: - PltSection(size_t HeaderSize); + PltSection(bool IsIplt); void writeTo(uint8_t *Buf) override; size_t getSize() const override; bool empty() const override { return Entries.empty(); } @@ -501,13 +658,12 @@ public: private: unsigned getPltRelocOff() const; std::vector<std::pair<const Symbol *, unsigned>> Entries; - // Iplt always has HeaderSize of 0, the Plt HeaderSize is always non-zero size_t HeaderSize; + bool IsIplt; }; -// GdbIndexChunk is created for each .debug_info section and contains -// information to create a part of .gdb_index for a given input section. -struct GdbIndexChunk { +class GdbIndexSection final : public SyntheticSection { +public: struct AddressEntry { InputSection *Section; uint64_t LowAddress; @@ -522,59 +678,51 @@ struct GdbIndexChunk { struct NameTypeEntry { llvm::CachedHashStringRef Name; - uint8_t Type; + uint32_t Type; }; - InputSection *DebugInfoSec; - std::vector<AddressEntry> AddressAreas; - std::vector<CuEntry> CompilationUnits; - std::vector<NameTypeEntry> NamesAndTypes; -}; + struct GdbChunk { + InputSection *Sec; + std::vector<AddressEntry> AddressAreas; + std::vector<CuEntry> CompilationUnits; + }; -// The symbol type for the .gdb_index section. -struct GdbSymbol { - uint32_t NameHash; - size_t NameOffset; - size_t CuVectorIndex; -}; + struct GdbSymbol { + llvm::CachedHashStringRef Name; + std::vector<uint32_t> CuVector; + uint32_t NameOff; + uint32_t CuVectorOff; + }; -class GdbIndexSection final : public SyntheticSection { -public: - GdbIndexSection(std::vector<GdbIndexChunk> &&Chunks); + GdbIndexSection(); + template <typename ELFT> static GdbIndexSection *create(); void writeTo(uint8_t *Buf) override; - size_t getSize() const override; + size_t getSize() const override { return Size; } bool empty() const override; private: - void fixCuIndex(); - std::vector<std::vector<uint32_t>> createCuVectors(); - std::vector<GdbSymbol *> createGdbSymtab(); - - // A symbol table for this .gdb_index section. - std::vector<GdbSymbol *> GdbSymtab; - - // CU vector is a part of constant pool area of section. - std::vector<std::vector<uint32_t>> CuVectors; + struct GdbIndexHeader { + llvm::support::ulittle32_t Version; + llvm::support::ulittle32_t CuListOff; + llvm::support::ulittle32_t CuTypesOff; + llvm::support::ulittle32_t AddressAreaOff; + llvm::support::ulittle32_t SymtabOff; + llvm::support::ulittle32_t ConstantPoolOff; + }; - // Symbol table contents. - llvm::DenseMap<llvm::CachedHashStringRef, GdbSymbol *> Symbols; + void initOutputSize(); + size_t computeSymtabSize() const; - // Each chunk contains information gathered from a debug sections of single - // object and used to build different areas of gdb index. - std::vector<GdbIndexChunk> Chunks; + // Each chunk contains information gathered from debug sections of a + // single object file. + std::vector<GdbChunk> Chunks; - static constexpr uint32_t CuListOffset = 24; - uint32_t CuTypesOffset; - uint32_t SymtabOffset; - uint32_t ConstantPoolOffset; - uint32_t StringPoolOffset; - uint32_t StringPoolSize; + // A symbol table for this .gdb_index section. + std::vector<GdbSymbol> Symbols; - std::vector<size_t> CuVectorOffsets; + size_t Size; }; -template <class ELFT> GdbIndexSection *createGdbIndex(); - // --eh-frame-hdr option tells linker to construct a header for all the // .eh_frame sections. This header is placed to a section named .eh_frame_hdr // and also to a PT_GNU_EH_FRAME segment. @@ -653,7 +801,7 @@ template <class ELFT> class VersionNeedSection final : public SyntheticSection { public: VersionNeedSection(); - void addSymbol(SharedSymbol *SS); + void addSymbol(Symbol *Sym); void finalizeContents() override; void writeTo(uint8_t *Buf) override; size_t getSize() const override; @@ -668,13 +816,12 @@ public: class MergeSyntheticSection : public SyntheticSection { public: void addSection(MergeInputSection *MS); + std::vector<MergeInputSection *> Sections; protected: MergeSyntheticSection(StringRef Name, uint32_t Type, uint64_t Flags, uint32_t Alignment) : SyntheticSection(Flags, Type, Alignment, Name) {} - - std::vector<MergeInputSection *> Sections; }; class MergeTailSection final : public MergeSyntheticSection { @@ -787,7 +934,12 @@ public: void writeTo(uint8_t *Buf) override; bool empty() const override; - InputSection *Highest = 0; + static bool classof(const SectionBase *D); + + // The last section referenced by a regular .ARM.exidx section. + // It is found and filled in Writer<ELFT>::resolveShfLinkOrder(). + // The sentinel points at the end of that section. + InputSection *Highest = nullptr; }; // A container for one or more linker generated thunks. Instances of these @@ -805,19 +957,21 @@ public: size_t getSize() const override { return Size; } void writeTo(uint8_t *Buf) override; InputSection *getTargetInputSection() const; + bool assignOffsets(); private: - std::vector<const Thunk *> Thunks; + std::vector<Thunk *> Thunks; size_t Size = 0; }; InputSection *createInterpSection(); MergeInputSection *createCommentSection(); void decompressSections(); +template <class ELFT> void splitSections(); void mergeSections(); -Symbol *addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value, - uint64_t Size, InputSectionBase &Section); +Defined *addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value, + uint64_t Size, InputSectionBase &Section); // Linker generated sections which can be used as inputs. struct InX { @@ -842,11 +996,13 @@ struct InX { static PltSection *Plt; static PltSection *Iplt; static RelocationBaseSection *RelaDyn; + static RelrBaseSection *RelrDyn; static RelocationBaseSection *RelaPlt; static RelocationBaseSection *RelaIplt; static StringTableSection *ShStrTab; static StringTableSection *StrTab; static SymbolTableBaseSection *SymTab; + static SymtabShndxSection* SymTabShndx; }; template <class ELFT> struct In { diff --git a/contrib/llvm/tools/lld/ELF/Target.cpp b/contrib/llvm/tools/lld/ELF/Target.cpp index b528fd583c1a..815f3a045551 100644 --- a/contrib/llvm/tools/lld/ELF/Target.cpp +++ b/contrib/llvm/tools/lld/ELF/Target.cpp @@ -60,6 +60,8 @@ TargetInfo *elf::getTarget() { return getARMTargetInfo(); case EM_AVR: return getAVRTargetInfo(); + case EM_HEXAGON: + return getHexagonTargetInfo(); case EM_MIPS: switch (Config->EKind) { case ELF32LEKind: @@ -87,29 +89,29 @@ TargetInfo *elf::getTarget() { fatal("unknown target machine"); } -template <class ELFT> static std::string getErrorLoc(const uint8_t *Loc) { +template <class ELFT> static ErrorPlace getErrPlace(const uint8_t *Loc) { for (InputSectionBase *D : InputSections) { - auto *IS = dyn_cast<InputSection>(D); - if (!IS || !IS->getParent()) + auto *IS = cast<InputSection>(D); + if (!IS->getParent()) continue; uint8_t *ISLoc = IS->getParent()->Loc + IS->OutSecOff; if (ISLoc <= Loc && Loc < ISLoc + IS->getSize()) - return IS->template getLocation<ELFT>(Loc - ISLoc) + ": "; + return {IS, IS->template getLocation<ELFT>(Loc - ISLoc) + ": "}; } - return ""; + return {}; } -std::string elf::getErrorLocation(const uint8_t *Loc) { +ErrorPlace elf::getErrorPlace(const uint8_t *Loc) { switch (Config->EKind) { case ELF32LEKind: - return getErrorLoc<ELF32LE>(Loc); + return getErrPlace<ELF32LE>(Loc); case ELF32BEKind: - return getErrorLoc<ELF32BE>(Loc); + return getErrPlace<ELF32BE>(Loc); case ELF64LEKind: - return getErrorLoc<ELF64LE>(Loc); + return getErrPlace<ELF64LE>(Loc); case ELF64BEKind: - return getErrorLoc<ELF64BE>(Loc); + return getErrPlace<ELF64BE>(Loc); default: llvm_unreachable("unknown ELF type"); } @@ -128,6 +130,12 @@ bool TargetInfo::needsThunk(RelExpr Expr, RelType Type, const InputFile *File, return false; } +bool TargetInfo::adjustPrologueForCrossSplitStack(uint8_t *Loc, + uint8_t *End) const { + llvm_unreachable("Target doesn't support split stacks."); +} + + bool TargetInfo::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const { return true; } diff --git a/contrib/llvm/tools/lld/ELF/Target.h b/contrib/llvm/tools/lld/ELF/Target.h index 1f58adba1817..82c7b8f7b6c5 100644 --- a/contrib/llvm/tools/lld/ELF/Target.h +++ b/contrib/llvm/tools/lld/ELF/Target.h @@ -25,9 +25,9 @@ class Symbol; class TargetInfo { public: virtual uint32_t calcEFlags() const { return 0; } - virtual bool isPicRel(RelType Type) const { return true; } virtual RelType getDynRel(RelType Type) const { return Type; } virtual void writeGotPltHeader(uint8_t *Buf) const {} + virtual void writeGotHeader(uint8_t *Buf) const {} virtual void writeGotPlt(uint8_t *Buf, const Symbol &S) const {}; virtual void writeIgotPlt(uint8_t *Buf, const Symbol &S) const; virtual int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const; @@ -43,8 +43,12 @@ public: virtual void addPltHeaderSymbols(InputSection &IS) const {} virtual void addPltSymbols(InputSection &IS, uint64_t Off) const {} + unsigned getPltEntryOffset(unsigned Index) const { + return Index * PltEntrySize + PltHeaderSize; + } + // Returns true if a relocation only uses the low bits of a value such that - // all those bits are in in the same page. For example, if the relocation + // all those bits are in the same page. For example, if the relocation // only uses the low 12 bits in a system with 4k pages. If this is true, the // bits will always have the same value at runtime and we don't have to emit // a dynamic relocation. @@ -55,6 +59,13 @@ public: virtual bool needsThunk(RelExpr Expr, RelType RelocType, const InputFile *File, uint64_t BranchAddr, const Symbol &S) const; + + // The function with a prologue starting at Loc was compiled with + // -fsplit-stack and it calls a function compiled without. Adjust the prologue + // to do the right thing. See https://gcc.gnu.org/wiki/SplitStacks. + virtual bool adjustPrologueForCrossSplitStack(uint8_t *Loc, + uint8_t *End) const; + // Return true if we can reach Dst from Src with Relocation RelocType virtual bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const; @@ -71,9 +82,10 @@ public: uint64_t getImageBase(); - // Offset of _GLOBAL_OFFSET_TABLE_ from base of .got section. Use -1 for - // end of .got + // Offset of _GLOBAL_OFFSET_TABLE_ from base of .got or .got.plt section. uint64_t GotBaseSymOff = 0; + // True if _GLOBAL_OFFSET_TABLE_ is relative to .got.plt, false if .got. + bool GotBaseSymInGotPlt = true; // On systems with range extensions we place collections of Thunks at // regular spacings that enable the majority of branches reach the Thunks. @@ -97,9 +109,18 @@ public: // to support lazy loading. unsigned GotPltHeaderEntriesNum = 3; - // Set to 0 for variant 2 + // On PPC ELF V2 abi, the first entry in the .got is the .TOC. + unsigned GotHeaderEntriesNum = 0; + + // For TLS variant 1, the TCB is a fixed size specified by the Target. + // For variant 2, the TCB is an unspecified size. + // Set to 0 for variant 2. unsigned TcbSize = 0; + // Set to the offset (in bytes) that the thread pointer is initialized to + // point to, relative to the start of the thread local storage. + unsigned TlsTpOffset = 0; + bool NeedsThunks = false; // A 4-byte field corresponding to one or more trap instructions, used to pad @@ -126,6 +147,7 @@ TargetInfo *getAArch64TargetInfo(); TargetInfo *getAMDGPUTargetInfo(); TargetInfo *getARMTargetInfo(); TargetInfo *getAVRTargetInfo(); +TargetInfo *getHexagonTargetInfo(); TargetInfo *getPPC64TargetInfo(); TargetInfo *getPPCTargetInfo(); TargetInfo *getSPARCV9TargetInfo(); @@ -134,7 +156,17 @@ TargetInfo *getX86TargetInfo(); TargetInfo *getX86_64TargetInfo(); template <class ELFT> TargetInfo *getMipsTargetInfo(); -std::string getErrorLocation(const uint8_t *Loc); +struct ErrorPlace { + InputSectionBase *IS; + std::string Loc; +}; + +// Returns input section and corresponding source string for the given location. +ErrorPlace getErrorPlace(const uint8_t *Loc); + +static inline std::string getErrorLocation(const uint8_t *Loc) { + return getErrorPlace(Loc).Loc; +} uint64_t getPPC64TocBase(); uint64_t getAArch64Page(uint64_t Expr); @@ -146,39 +178,74 @@ template <class ELFT> bool isMipsPIC(const Defined *Sym); static inline void reportRangeError(uint8_t *Loc, RelType Type, const Twine &V, int64_t Min, uint64_t Max) { - error(getErrorLocation(Loc) + "relocation " + lld::toString(Type) + - " out of range: " + V + " is not in [" + Twine(Min) + ", " + - Twine(Max) + "]"); + ErrorPlace ErrPlace = getErrorPlace(Loc); + StringRef Hint; + if (ErrPlace.IS && ErrPlace.IS->Name.startswith(".debug")) + Hint = "; consider recompiling with -fdebug-types-section to reduce size " + "of debug sections"; + + error(ErrPlace.Loc + "relocation " + lld::toString(Type) + + " out of range: " + V.str() + " is not in [" + Twine(Min).str() + ", " + + Twine(Max).str() + "]" + Hint); } -template <unsigned N> -static void checkInt(uint8_t *Loc, int64_t V, RelType Type) { - if (!llvm::isInt<N>(V)) +// Sign-extend Nth bit all the way to MSB. +inline int64_t signExtend(uint64_t V, int N) { + return int64_t(V << (64 - N)) >> (64 - N); +} + +// Make sure that V can be represented as an N bit signed integer. +inline void checkInt(uint8_t *Loc, int64_t V, int N, RelType Type) { + if (V != signExtend(V, N)) reportRangeError(Loc, Type, Twine(V), llvm::minIntN(N), llvm::maxIntN(N)); } -template <unsigned N> -static void checkUInt(uint8_t *Loc, uint64_t V, RelType Type) { - if (!llvm::isUInt<N>(V)) +// Make sure that V can be represented as an N bit unsigned integer. +inline void checkUInt(uint8_t *Loc, uint64_t V, int N, RelType Type) { + if ((V >> N) != 0) reportRangeError(Loc, Type, Twine(V), 0, llvm::maxUIntN(N)); } -template <unsigned N> -static void checkIntUInt(uint8_t *Loc, uint64_t V, RelType Type) { - if (!llvm::isInt<N>(V) && !llvm::isUInt<N>(V)) - // For the error message we should cast V to a signed integer so that error - // messages show a small negative value rather than an extremely large one +// Make sure that V can be represented as an N bit signed or unsigned integer. +inline void checkIntUInt(uint8_t *Loc, uint64_t V, int N, RelType Type) { + // For the error message we should cast V to a signed integer so that error + // messages show a small negative value rather than an extremely large one + if (V != (uint64_t)signExtend(V, N) && (V >> N) != 0) reportRangeError(Loc, Type, Twine((int64_t)V), llvm::minIntN(N), - llvm::maxUIntN(N)); + llvm::maxIntN(N)); } -template <unsigned N> -static void checkAlignment(uint8_t *Loc, uint64_t V, RelType Type) { +inline void checkAlignment(uint8_t *Loc, uint64_t V, int N, RelType Type) { if ((V & (N - 1)) != 0) error(getErrorLocation(Loc) + "improper alignment for relocation " + lld::toString(Type) + ": 0x" + llvm::utohexstr(V) + " is not aligned to " + Twine(N) + " bytes"); } + +// Endianness-aware read/write. +inline uint16_t read16(const void *P) { + return llvm::support::endian::read16(P, Config->Endianness); +} + +inline uint32_t read32(const void *P) { + return llvm::support::endian::read32(P, Config->Endianness); +} + +inline uint64_t read64(const void *P) { + return llvm::support::endian::read64(P, Config->Endianness); +} + +inline void write16(void *P, uint16_t V) { + llvm::support::endian::write16(P, V, Config->Endianness); +} + +inline void write32(void *P, uint32_t V) { + llvm::support::endian::write32(P, V, Config->Endianness); +} + +inline void write64(void *P, uint64_t V) { + llvm::support::endian::write64(P, V, Config->Endianness); +} } // namespace elf } // namespace lld diff --git a/contrib/llvm/tools/lld/ELF/Thunks.cpp b/contrib/llvm/tools/lld/ELF/Thunks.cpp index b0bbf6da705a..2cd7e51ae357 100644 --- a/contrib/llvm/tools/lld/ELF/Thunks.cpp +++ b/contrib/llvm/tools/lld/ELF/Thunks.cpp @@ -40,7 +40,6 @@ using namespace llvm; using namespace llvm::object; -using namespace llvm::support::endian; using namespace llvm::ELF; namespace lld { @@ -52,59 +51,112 @@ namespace { class AArch64ABSLongThunk final : public Thunk { public: AArch64ABSLongThunk(Symbol &Dest) : Thunk(Dest) {} - uint32_t size() const override { return 16; } - void writeTo(uint8_t *Buf, ThunkSection &IS) const override; + uint32_t size() override { return 16; } + void writeTo(uint8_t *Buf) override; void addSymbols(ThunkSection &IS) override; }; class AArch64ADRPThunk final : public Thunk { public: AArch64ADRPThunk(Symbol &Dest) : Thunk(Dest) {} - uint32_t size() const override { return 12; } - void writeTo(uint8_t *Buf, ThunkSection &IS) const override; + uint32_t size() override { return 12; } + void writeTo(uint8_t *Buf) override; void addSymbols(ThunkSection &IS) override; }; +// Base class for ARM thunks. +// +// An ARM thunk may be either short or long. A short thunk is simply a branch +// (B) instruction, and it may be used to call ARM functions when the distance +// from the thunk to the target is less than 32MB. Long thunks can branch to any +// virtual address and can switch between ARM and Thumb, and they are +// implemented in the derived classes. This class tries to create a short thunk +// if the target is in range, otherwise it creates a long thunk. +class ARMThunk : public Thunk { +public: + ARMThunk(Symbol &Dest) : Thunk(Dest) {} + + bool mayUseShortThunk(); + uint32_t size() override { return mayUseShortThunk() ? 4 : sizeLong(); } + void writeTo(uint8_t *Buf) override; + bool isCompatibleWith(RelType Type) const override; + + // Returns the size of a long thunk. + virtual uint32_t sizeLong() = 0; + + // Writes a long thunk to Buf. + virtual void writeLong(uint8_t *Buf) = 0; + +private: + // This field tracks whether all previously considered layouts would allow + // this thunk to be short. If we have ever needed a long thunk, we always + // create a long thunk, even if the thunk may be short given the current + // distance to the target. We do this because transitioning from long to short + // can create layout oscillations in certain corner cases which would prevent + // the layout from converging. + bool MayUseShortThunk = true; +}; + +// Base class for Thumb-2 thunks. +// +// This class is similar to ARMThunk, but it uses the Thumb-2 B.W instruction +// which has a range of 16MB. +class ThumbThunk : public Thunk { +public: + ThumbThunk(Symbol &Dest) : Thunk(Dest) { Alignment = 2; } + + bool mayUseShortThunk(); + uint32_t size() override { return mayUseShortThunk() ? 4 : sizeLong(); } + void writeTo(uint8_t *Buf) override; + bool isCompatibleWith(RelType Type) const override; + + // Returns the size of a long thunk. + virtual uint32_t sizeLong() = 0; + + // Writes a long thunk to Buf. + virtual void writeLong(uint8_t *Buf) = 0; + +private: + // See comment in ARMThunk above. + bool MayUseShortThunk = true; +}; + // Specific ARM Thunk implementations. The naming convention is: // Source State, TargetState, Target Requirement, ABS or PI, Range -class ARMV7ABSLongThunk final : public Thunk { +class ARMV7ABSLongThunk final : public ARMThunk { public: - ARMV7ABSLongThunk(Symbol &Dest) : Thunk(Dest) {} + ARMV7ABSLongThunk(Symbol &Dest) : ARMThunk(Dest) {} - uint32_t size() const override { return 12; } - void writeTo(uint8_t *Buf, ThunkSection &IS) const override; + uint32_t sizeLong() override { return 12; } + void writeLong(uint8_t *Buf) override; void addSymbols(ThunkSection &IS) override; - bool isCompatibleWith(RelType Type) const override; }; -class ARMV7PILongThunk final : public Thunk { +class ARMV7PILongThunk final : public ARMThunk { public: - ARMV7PILongThunk(Symbol &Dest) : Thunk(Dest) {} + ARMV7PILongThunk(Symbol &Dest) : ARMThunk(Dest) {} - uint32_t size() const override { return 16; } - void writeTo(uint8_t *Buf, ThunkSection &IS) const override; + uint32_t sizeLong() override { return 16; } + void writeLong(uint8_t *Buf) override; void addSymbols(ThunkSection &IS) override; - bool isCompatibleWith(RelType Type) const override; }; -class ThumbV7ABSLongThunk final : public Thunk { +class ThumbV7ABSLongThunk final : public ThumbThunk { public: - ThumbV7ABSLongThunk(Symbol &Dest) : Thunk(Dest) { Alignment = 2; } + ThumbV7ABSLongThunk(Symbol &Dest) : ThumbThunk(Dest) {} - uint32_t size() const override { return 10; } - void writeTo(uint8_t *Buf, ThunkSection &IS) const override; + uint32_t sizeLong() override { return 10; } + void writeLong(uint8_t *Buf) override; void addSymbols(ThunkSection &IS) override; - bool isCompatibleWith(RelType Type) const override; }; -class ThumbV7PILongThunk final : public Thunk { +class ThumbV7PILongThunk final : public ThumbThunk { public: - ThumbV7PILongThunk(Symbol &Dest) : Thunk(Dest) { Alignment = 2; } + ThumbV7PILongThunk(Symbol &Dest) : ThumbThunk(Dest) {} - uint32_t size() const override { return 12; } - void writeTo(uint8_t *Buf, ThunkSection &IS) const override; + uint32_t sizeLong() override { return 12; } + void writeLong(uint8_t *Buf) override; void addSymbols(ThunkSection &IS) override; - bool isCompatibleWith(RelType Type) const override; }; // MIPS LA25 thunk @@ -112,8 +164,8 @@ class MipsThunk final : public Thunk { public: MipsThunk(Symbol &Dest) : Thunk(Dest) {} - uint32_t size() const override { return 16; } - void writeTo(uint8_t *Buf, ThunkSection &IS) const override; + uint32_t size() override { return 16; } + void writeTo(uint8_t *Buf) override; void addSymbols(ThunkSection &IS) override; InputSection *getTargetInputSection() const override; }; @@ -123,8 +175,8 @@ class MicroMipsThunk final : public Thunk { public: MicroMipsThunk(Symbol &Dest) : Thunk(Dest) {} - uint32_t size() const override { return 14; } - void writeTo(uint8_t *Buf, ThunkSection &IS) const override; + uint32_t size() override { return 14; } + void writeTo(uint8_t *Buf) override; void addSymbols(ThunkSection &IS) override; InputSection *getTargetInputSection() const override; }; @@ -134,14 +186,44 @@ class MicroMipsR6Thunk final : public Thunk { public: MicroMipsR6Thunk(Symbol &Dest) : Thunk(Dest) {} - uint32_t size() const override { return 12; } - void writeTo(uint8_t *Buf, ThunkSection &IS) const override; + uint32_t size() override { return 12; } + void writeTo(uint8_t *Buf) override; void addSymbols(ThunkSection &IS) override; InputSection *getTargetInputSection() const override; }; + +// PPC64 Plt call stubs. +// Any call site that needs to call through a plt entry needs a call stub in +// the .text section. The call stub is responsible for: +// 1) Saving the toc-pointer to the stack. +// 2) Loading the target functions address from the procedure linkage table into +// r12 for use by the target functions global entry point, and into the count +// register. +// 3) Transfering control to the target function through an indirect branch. +class PPC64PltCallStub final : public Thunk { +public: + PPC64PltCallStub(Symbol &Dest) : Thunk(Dest) {} + uint32_t size() override { return 20; } + void writeTo(uint8_t *Buf) override; + void addSymbols(ThunkSection &IS) override; +}; + } // end anonymous namespace +Defined *Thunk::addSymbol(StringRef Name, uint8_t Type, uint64_t Value, + InputSectionBase &Section) { + Defined *D = addSyntheticLocal(Name, Type, Value, /*Size=*/0, Section); + Syms.push_back(D); + return D; +} + +void Thunk::setOffset(uint64_t NewOffset) { + for (Defined *D : Syms) + D->Value = D->Value - Offset + NewOffset; + Offset = NewOffset; +} + // AArch64 long range Thunks static uint64_t getAArch64ThunkDestVA(const Symbol &S) { @@ -149,7 +231,7 @@ static uint64_t getAArch64ThunkDestVA(const Symbol &S) { return V; } -void AArch64ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const { +void AArch64ABSLongThunk::writeTo(uint8_t *Buf) { const uint8_t Data[] = { 0x50, 0x00, 0x00, 0x58, // ldr x16, L0 0x00, 0x02, 0x1f, 0xd6, // br x16 @@ -162,11 +244,10 @@ void AArch64ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const { } void AArch64ABSLongThunk::addSymbols(ThunkSection &IS) { - ThunkSym = addSyntheticLocal( - Saver.save("__AArch64AbsLongThunk_" + Destination.getName()), STT_FUNC, - Offset, size(), IS); - addSyntheticLocal("$x", STT_NOTYPE, Offset, 0, IS); - addSyntheticLocal("$d", STT_NOTYPE, Offset + 8, 0, IS); + addSymbol(Saver.save("__AArch64AbsLongThunk_" + Destination.getName()), + STT_FUNC, 0, IS); + addSymbol("$x", STT_NOTYPE, 0, IS); + addSymbol("$d", STT_NOTYPE, 8, IS); } // This Thunk has a maximum range of 4Gb, this is sufficient for all programs @@ -174,26 +255,24 @@ void AArch64ABSLongThunk::addSymbols(ThunkSection &IS) { // clang and gcc do not support the large code model for position independent // code so it is safe to use this for position independent thunks without // worrying about the destination being more than 4Gb away. -void AArch64ADRPThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const { +void AArch64ADRPThunk::writeTo(uint8_t *Buf) { const uint8_t Data[] = { 0x10, 0x00, 0x00, 0x90, // adrp x16, Dest R_AARCH64_ADR_PREL_PG_HI21(Dest) 0x10, 0x02, 0x00, 0x91, // add x16, x16, R_AARCH64_ADD_ABS_LO12_NC(Dest) 0x00, 0x02, 0x1f, 0xd6, // br x16 }; uint64_t S = getAArch64ThunkDestVA(Destination); - uint64_t P = ThunkSym->getVA(); + uint64_t P = getThunkTargetSym()->getVA(); memcpy(Buf, Data, sizeof(Data)); Target->relocateOne(Buf, R_AARCH64_ADR_PREL_PG_HI21, getAArch64Page(S) - getAArch64Page(P)); Target->relocateOne(Buf + 4, R_AARCH64_ADD_ABS_LO12_NC, S); } -void AArch64ADRPThunk::addSymbols(ThunkSection &IS) -{ - ThunkSym = addSyntheticLocal( - Saver.save("__AArch64ADRPThunk_" + Destination.getName()), STT_FUNC, - Offset, size(), IS); - addSyntheticLocal("$x", STT_NOTYPE, Offset, 0, IS); +void AArch64ADRPThunk::addSymbols(ThunkSection &IS) { + addSymbol(Saver.save("__AArch64ADRPThunk_" + Destination.getName()), STT_FUNC, + 0, IS); + addSymbol("$x", STT_NOTYPE, 0, IS); } // ARM Target Thunks @@ -202,7 +281,81 @@ static uint64_t getARMThunkDestVA(const Symbol &S) { return SignExtend64<32>(V); } -void ARMV7ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const { +// This function returns true if the target is not Thumb and is within 2^26, and +// it has not previously returned false (see comment for MayUseShortThunk). +bool ARMThunk::mayUseShortThunk() { + if (!MayUseShortThunk) + return false; + uint64_t S = getARMThunkDestVA(Destination); + if (S & 1) { + MayUseShortThunk = false; + return false; + } + uint64_t P = getThunkTargetSym()->getVA(); + int64_t Offset = S - P - 8; + MayUseShortThunk = llvm::isInt<26>(Offset); + return MayUseShortThunk; +} + +void ARMThunk::writeTo(uint8_t *Buf) { + if (!mayUseShortThunk()) { + writeLong(Buf); + return; + } + + uint64_t S = getARMThunkDestVA(Destination); + uint64_t P = getThunkTargetSym()->getVA(); + int64_t Offset = S - P - 8; + const uint8_t Data[] = { + 0x00, 0x00, 0x00, 0xea, // b S + }; + memcpy(Buf, Data, sizeof(Data)); + Target->relocateOne(Buf, R_ARM_JUMP24, Offset); +} + +bool ARMThunk::isCompatibleWith(RelType Type) const { + // Thumb branch relocations can't use BLX + return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24; +} + +// This function returns true if the target is Thumb and is within 2^25, and +// it has not previously returned false (see comment for MayUseShortThunk). +bool ThumbThunk::mayUseShortThunk() { + if (!MayUseShortThunk) + return false; + uint64_t S = getARMThunkDestVA(Destination); + if ((S & 1) == 0) { + MayUseShortThunk = false; + return false; + } + uint64_t P = getThunkTargetSym()->getVA() & ~1; + int64_t Offset = S - P - 4; + MayUseShortThunk = llvm::isInt<25>(Offset); + return MayUseShortThunk; +} + +void ThumbThunk::writeTo(uint8_t *Buf) { + if (!mayUseShortThunk()) { + writeLong(Buf); + return; + } + + uint64_t S = getARMThunkDestVA(Destination); + uint64_t P = getThunkTargetSym()->getVA(); + int64_t Offset = S - P - 4; + const uint8_t Data[] = { + 0x00, 0xf0, 0x00, 0xb0, // b.w S + }; + memcpy(Buf, Data, sizeof(Data)); + Target->relocateOne(Buf, R_ARM_THM_JUMP24, Offset); +} + +bool ThumbThunk::isCompatibleWith(RelType Type) const { + // ARM branch relocations can't use BLX + return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32; +} + +void ARMV7ABSLongThunk::writeLong(uint8_t *Buf) { const uint8_t Data[] = { 0x00, 0xc0, 0x00, 0xe3, // movw ip,:lower16:S 0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S @@ -215,18 +368,12 @@ void ARMV7ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const { } void ARMV7ABSLongThunk::addSymbols(ThunkSection &IS) { - ThunkSym = addSyntheticLocal( - Saver.save("__ARMv7ABSLongThunk_" + Destination.getName()), STT_FUNC, - Offset, size(), IS); - addSyntheticLocal("$a", STT_NOTYPE, Offset, 0, IS); -} - -bool ARMV7ABSLongThunk::isCompatibleWith(RelType Type) const { - // Thumb branch relocations can't use BLX - return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24; + addSymbol(Saver.save("__ARMv7ABSLongThunk_" + Destination.getName()), + STT_FUNC, 0, IS); + addSymbol("$a", STT_NOTYPE, 0, IS); } -void ThumbV7ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const { +void ThumbV7ABSLongThunk::writeLong(uint8_t *Buf) { const uint8_t Data[] = { 0x40, 0xf2, 0x00, 0x0c, // movw ip, :lower16:S 0xc0, 0xf2, 0x00, 0x0c, // movt ip, :upper16:S @@ -239,18 +386,12 @@ void ThumbV7ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const { } void ThumbV7ABSLongThunk::addSymbols(ThunkSection &IS) { - ThunkSym = addSyntheticLocal( - Saver.save("__Thumbv7ABSLongThunk_" + Destination.getName()), STT_FUNC, - Offset | 0x1, size(), IS); - addSyntheticLocal("$t", STT_NOTYPE, Offset, 0, IS); + addSymbol(Saver.save("__Thumbv7ABSLongThunk_" + Destination.getName()), + STT_FUNC, 1, IS); + addSymbol("$t", STT_NOTYPE, 0, IS); } -bool ThumbV7ABSLongThunk::isCompatibleWith(RelType Type) const { - // ARM branch relocations can't use BLX - return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32; -} - -void ARMV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const { +void ARMV7PILongThunk::writeLong(uint8_t *Buf) { const uint8_t Data[] = { 0xf0, 0xcf, 0x0f, 0xe3, // P: movw ip,:lower16:S - (P + (L1-P) + 8) 0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S - (P + (L1-P) + 8) @@ -258,7 +399,7 @@ void ARMV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const { 0x1c, 0xff, 0x2f, 0xe1, // bx r12 }; uint64_t S = getARMThunkDestVA(Destination); - uint64_t P = ThunkSym->getVA(); + uint64_t P = getThunkTargetSym()->getVA(); uint64_t Offset = S - P - 16; memcpy(Buf, Data, sizeof(Data)); Target->relocateOne(Buf, R_ARM_MOVW_PREL_NC, Offset); @@ -266,18 +407,12 @@ void ARMV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const { } void ARMV7PILongThunk::addSymbols(ThunkSection &IS) { - ThunkSym = addSyntheticLocal( - Saver.save("__ARMV7PILongThunk_" + Destination.getName()), STT_FUNC, - Offset, size(), IS); - addSyntheticLocal("$a", STT_NOTYPE, Offset, 0, IS); + addSymbol(Saver.save("__ARMV7PILongThunk_" + Destination.getName()), STT_FUNC, + 0, IS); + addSymbol("$a", STT_NOTYPE, 0, IS); } -bool ARMV7PILongThunk::isCompatibleWith(RelType Type) const { - // Thumb branch relocations can't use BLX - return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24; -} - -void ThumbV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const { +void ThumbV7PILongThunk::writeLong(uint8_t *Buf) { const uint8_t Data[] = { 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) @@ -285,7 +420,7 @@ void ThumbV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const { 0x60, 0x47, // bx r12 }; uint64_t S = getARMThunkDestVA(Destination); - uint64_t P = ThunkSym->getVA() & ~0x1; + uint64_t P = getThunkTargetSym()->getVA() & ~0x1; uint64_t Offset = S - P - 12; memcpy(Buf, Data, sizeof(Data)); Target->relocateOne(Buf, R_ARM_THM_MOVW_PREL_NC, Offset); @@ -293,32 +428,25 @@ void ThumbV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const { } void ThumbV7PILongThunk::addSymbols(ThunkSection &IS) { - ThunkSym = addSyntheticLocal( - Saver.save("__ThumbV7PILongThunk_" + Destination.getName()), STT_FUNC, - Offset | 0x1, size(), IS); - addSyntheticLocal("$t", STT_NOTYPE, Offset, 0, IS); -} - -bool ThumbV7PILongThunk::isCompatibleWith(RelType Type) const { - // ARM branch relocations can't use BLX - return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32; + addSymbol(Saver.save("__ThumbV7PILongThunk_" + Destination.getName()), + STT_FUNC, 1, IS); + addSymbol("$t", STT_NOTYPE, 0, IS); } // Write MIPS LA25 thunk code to call PIC function from the non-PIC one. -void MipsThunk::writeTo(uint8_t *Buf, ThunkSection &) const { +void MipsThunk::writeTo(uint8_t *Buf) { uint64_t S = Destination.getVA(); - write32(Buf, 0x3c190000, Config->Endianness); // lui $25, %hi(func) - write32(Buf + 4, 0x08000000 | (S >> 2), Config->Endianness); // j func - write32(Buf + 8, 0x27390000, Config->Endianness); // addiu $25, $25, %lo(func) - write32(Buf + 12, 0x00000000, Config->Endianness); // nop + write32(Buf, 0x3c190000); // lui $25, %hi(func) + write32(Buf + 4, 0x08000000 | (S >> 2)); // j func + write32(Buf + 8, 0x27390000); // addiu $25, $25, %lo(func) + write32(Buf + 12, 0x00000000); // nop Target->relocateOne(Buf, R_MIPS_HI16, S); Target->relocateOne(Buf + 8, R_MIPS_LO16, S); } void MipsThunk::addSymbols(ThunkSection &IS) { - ThunkSym = - addSyntheticLocal(Saver.save("__LA25Thunk_" + Destination.getName()), - STT_FUNC, Offset, size(), IS); + addSymbol(Saver.save("__LA25Thunk_" + Destination.getName()), STT_FUNC, 0, + IS); } InputSection *MipsThunk::getTargetInputSection() const { @@ -328,22 +456,21 @@ InputSection *MipsThunk::getTargetInputSection() const { // Write microMIPS R2-R5 LA25 thunk code // to call PIC function from the non-PIC one. -void MicroMipsThunk::writeTo(uint8_t *Buf, ThunkSection &) const { +void MicroMipsThunk::writeTo(uint8_t *Buf) { uint64_t S = Destination.getVA() | 1; - write16(Buf, 0x41b9, Config->Endianness); // lui $25, %hi(func) - write16(Buf + 4, 0xd400, Config->Endianness); // j func - write16(Buf + 8, 0x3339, Config->Endianness); // addiu $25, $25, %lo(func) - write16(Buf + 12, 0x0c00, Config->Endianness); // nop + write16(Buf, 0x41b9); // lui $25, %hi(func) + write16(Buf + 4, 0xd400); // j func + write16(Buf + 8, 0x3339); // addiu $25, $25, %lo(func) + write16(Buf + 12, 0x0c00); // nop Target->relocateOne(Buf, R_MICROMIPS_HI16, S); Target->relocateOne(Buf + 4, R_MICROMIPS_26_S1, S); Target->relocateOne(Buf + 8, R_MICROMIPS_LO16, S); } void MicroMipsThunk::addSymbols(ThunkSection &IS) { - ThunkSym = - addSyntheticLocal(Saver.save("__microLA25Thunk_" + Destination.getName()), - STT_FUNC, Offset, size(), IS); - ThunkSym->StOther |= STO_MIPS_MICROMIPS; + Defined *D = addSymbol( + Saver.save("__microLA25Thunk_" + Destination.getName()), STT_FUNC, 0, IS); + D->StOther |= STO_MIPS_MICROMIPS; } InputSection *MicroMipsThunk::getTargetInputSection() const { @@ -353,22 +480,21 @@ InputSection *MicroMipsThunk::getTargetInputSection() const { // Write microMIPS R6 LA25 thunk code // to call PIC function from the non-PIC one. -void MicroMipsR6Thunk::writeTo(uint8_t *Buf, ThunkSection &) const { +void MicroMipsR6Thunk::writeTo(uint8_t *Buf) { uint64_t S = Destination.getVA() | 1; - uint64_t P = ThunkSym->getVA(); - write16(Buf, 0x1320, Config->Endianness); // lui $25, %hi(func) - write16(Buf + 4, 0x3339, Config->Endianness); // addiu $25, $25, %lo(func) - write16(Buf + 8, 0x9400, Config->Endianness); // bc func + uint64_t P = getThunkTargetSym()->getVA(); + write16(Buf, 0x1320); // lui $25, %hi(func) + write16(Buf + 4, 0x3339); // addiu $25, $25, %lo(func) + write16(Buf + 8, 0x9400); // bc func Target->relocateOne(Buf, R_MICROMIPS_HI16, S); Target->relocateOne(Buf + 4, R_MICROMIPS_LO16, S); Target->relocateOne(Buf + 8, R_MICROMIPS_PC26_S1, S - P - 12); } void MicroMipsR6Thunk::addSymbols(ThunkSection &IS) { - ThunkSym = - addSyntheticLocal(Saver.save("__microLA25Thunk_" + Destination.getName()), - STT_FUNC, Offset, size(), IS); - ThunkSym->StOther |= STO_MIPS_MICROMIPS; + Defined *D = addSymbol( + Saver.save("__microLA25Thunk_" + Destination.getName()), STT_FUNC, 0, IS); + D->StOther |= STO_MIPS_MICROMIPS; } InputSection *MicroMipsR6Thunk::getTargetInputSection() const { @@ -376,6 +502,25 @@ InputSection *MicroMipsR6Thunk::getTargetInputSection() const { return dyn_cast<InputSection>(DR.Section); } +void PPC64PltCallStub::writeTo(uint8_t *Buf) { + int64_t Off = Destination.getGotPltVA() - getPPC64TocBase(); + // Need to add 0x8000 to offset to account for the low bits being signed. + uint16_t OffHa = (Off + 0x8000) >> 16; + uint16_t OffLo = Off; + + write32(Buf + 0, 0xf8410018); // std r2,24(r1) + write32(Buf + 4, 0x3d820000 | OffHa); // addis r12,r2, X@plt@to@ha + write32(Buf + 8, 0xe98c0000 | OffLo); // ld r12,X@plt@toc@l(r12) + write32(Buf + 12, 0x7d8903a6); // mtctr r12 + write32(Buf + 16, 0x4e800420); // bctr +} + +void PPC64PltCallStub::addSymbols(ThunkSection &IS) { + Defined *S = addSymbol(Saver.save("__plt_" + Destination.getName()), STT_FUNC, + 0, IS); + S->NeedsTocRestore = true; +} + Thunk::Thunk(Symbol &D) : Destination(D), Offset(0) {} Thunk::~Thunk() = default; @@ -419,15 +564,26 @@ static Thunk *addThunkMips(RelType Type, Symbol &S) { return make<MipsThunk>(S); } +static Thunk *addThunkPPC64(RelType Type, Symbol &S) { + if (Type == R_PPC64_REL24) + return make<PPC64PltCallStub>(S); + fatal("unexpected relocation type"); +} + Thunk *addThunk(RelType Type, Symbol &S) { if (Config->EMachine == EM_AARCH64) return addThunkAArch64(Type, S); - else if (Config->EMachine == EM_ARM) + + if (Config->EMachine == EM_ARM) return addThunkArm(Type, S); - else if (Config->EMachine == EM_MIPS) + + if (Config->EMachine == EM_MIPS) return addThunkMips(Type, S); - llvm_unreachable("add Thunk only supported for ARM and Mips"); - return nullptr; + + if (Config->EMachine == EM_PPC64) + return addThunkPPC64(Type, S); + + llvm_unreachable("add Thunk only supported for ARM, Mips and PowerPC"); } } // end namespace elf diff --git a/contrib/llvm/tools/lld/ELF/Thunks.h b/contrib/llvm/tools/lld/ELF/Thunks.h index 828fac0bf53b..ed82b4d946ac 100644 --- a/contrib/llvm/tools/lld/ELF/Thunks.h +++ b/contrib/llvm/tools/lld/ELF/Thunks.h @@ -14,6 +14,7 @@ namespace lld { namespace elf { +class Defined; class Symbol; class ThunkSection; // Class to describe an instance of a Thunk. @@ -30,12 +31,17 @@ public: Thunk(Symbol &Destination); virtual ~Thunk(); - virtual uint32_t size() const { return 0; } - virtual void writeTo(uint8_t *Buf, ThunkSection &IS) const {} + virtual uint32_t size() = 0; + virtual void writeTo(uint8_t *Buf) = 0; - // All Thunks must define at least one symbol ThunkSym so that we can - // redirect relocations to it. - virtual void addSymbols(ThunkSection &IS) {} + // All Thunks must define at least one symbol, known as the thunk target + // symbol, so that we can redirect relocations to it. The thunk may define + // additional symbols, but these are never targets for relocations. + virtual void addSymbols(ThunkSection &IS) = 0; + + void setOffset(uint64_t Offset); + Defined *addSymbol(StringRef Name, uint8_t Type, uint64_t Value, + InputSectionBase &Section); // Some Thunks must be placed immediately before their Target as they elide // a branch and fall through to the first Symbol in the Target. @@ -45,10 +51,12 @@ public: // compatible with it. virtual bool isCompatibleWith(RelType Type) const { return true; } + Defined *getThunkTargetSym() const { return Syms[0]; } + // The alignment requirement for this Thunk, defaults to the size of the // typical code section alignment. Symbol &Destination; - Symbol *ThunkSym; + llvm::SmallVector<Defined *, 3> Syms; uint64_t Offset = 0; uint32_t Alignment = 4; }; diff --git a/contrib/llvm/tools/lld/ELF/Writer.cpp b/contrib/llvm/tools/lld/ELF/Writer.cpp index d130bd603933..73a97380b54b 100644 --- a/contrib/llvm/tools/lld/ELF/Writer.cpp +++ b/contrib/llvm/tools/lld/ELF/Writer.cpp @@ -9,18 +9,19 @@ #include "Writer.h" #include "AArch64ErrataFix.h" +#include "CallGraphSort.h" #include "Config.h" #include "Filesystem.h" #include "LinkerScript.h" #include "MapFile.h" #include "OutputSections.h" #include "Relocations.h" -#include "Strings.h" #include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/Memory.h" +#include "lld/Common/Strings.h" #include "lld/Common/Threads.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSwitch.h" @@ -49,7 +50,7 @@ public: private: void copyLocalSymbols(); void addSectionSymbols(); - void forEachRelSec(std::function<void(InputSectionBase &)> Fn); + void forEachRelSec(llvm::function_ref<void(InputSectionBase &)> Fn); void sortSections(); void resolveShfLinkOrder(); void sortInputSections(); @@ -62,6 +63,7 @@ private: void assignFileOffsets(); void assignFileOffsetsBinary(); void setPhdrs(); + void checkSections(); void fixSectionAlignments(); void openFile(); void writeTrapInstr(); @@ -81,39 +83,48 @@ private: uint64_t FileSize; uint64_t SectionHeaderOff; - - bool HasGotBaseSym = false; }; } // anonymous namespace -StringRef elf::getOutputSectionName(InputSectionBase *S) { - // ".zdebug_" is a prefix for ZLIB-compressed sections. - // Because we decompressed input sections, we want to remove 'z'. - if (S->Name.startswith(".zdebug_")) - return Saver.save("." + S->Name.substr(2)); +static bool isSectionPrefix(StringRef Prefix, StringRef Name) { + return Name.startswith(Prefix) || Name == Prefix.drop_back(); +} +StringRef elf::getOutputSectionName(const InputSectionBase *S) { if (Config->Relocatable) return S->Name; // This is for --emit-relocs. If .text.foo is emitted as .text.bar, we want // to emit .rela.text.foo as .rela.text.bar for consistency (this is not // technically required, but not doing it is odd). This code guarantees that. - if ((S->Type == SHT_REL || S->Type == SHT_RELA) && - !isa<SyntheticSection>(S)) { - OutputSection *Out = - cast<InputSection>(S)->getRelocatedSection()->getOutputSection(); - if (S->Type == SHT_RELA) - return Saver.save(".rela" + Out->Name); - return Saver.save(".rel" + Out->Name); + if (auto *IS = dyn_cast<InputSection>(S)) { + if (InputSectionBase *Rel = IS->getRelocatedSection()) { + OutputSection *Out = Rel->getOutputSection(); + if (S->Type == SHT_RELA) + return Saver.save(".rela" + Out->Name); + return Saver.save(".rel" + Out->Name); + } } + // This check is for -z keep-text-section-prefix. This option separates text + // sections with prefix ".text.hot", ".text.unlikely", ".text.startup" or + // ".text.exit". + // When enabled, this allows identifying the hot code region (.text.hot) in + // the final binary which can be selectively mapped to huge pages or mlocked, + // for instance. + if (Config->ZKeepTextSectionPrefix) + for (StringRef V : + {".text.hot.", ".text.unlikely.", ".text.startup.", ".text.exit."}) { + if (isSectionPrefix(V, S->Name)) + return V.drop_back(); + } + for (StringRef V : {".text.", ".rodata.", ".data.rel.ro.", ".data.", ".bss.rel.ro.", ".bss.", ".init_array.", ".fini_array.", ".ctors.", ".dtors.", ".tbss.", ".gcc_except_table.", ".tdata.", ".ARM.exidx.", ".ARM.extab."}) { - StringRef Prefix = V.drop_back(); - if (S->Name.startswith(V) || S->Name == Prefix) - return Prefix; + if (isSectionPrefix(V, S->Name)) + return V.drop_back(); } // CommonSection is identified as "COMMON" in linker scripts. @@ -194,21 +205,30 @@ void elf::addReservedSymbols() { Symtab->addAbsolute("__gnu_local_gp", STV_HIDDEN, STB_GLOBAL); } + // The Power Architecture 64-bit v2 ABI defines a TableOfContents (TOC) which + // combines the typical ELF GOT with the small data sections. It commonly + // includes .got .toc .sdata .sbss. The .TOC. symbol replaces both + // _GLOBAL_OFFSET_TABLE_ and _SDA_BASE_ from the 32-bit ABI. It is used to + // represent the TOC base which is offset by 0x8000 bytes from the start of + // the .got section. ElfSym::GlobalOffsetTable = addOptionalRegular( - "_GLOBAL_OFFSET_TABLE_", Out::ElfHeader, Target->GotBaseSymOff); + (Config->EMachine == EM_PPC64) ? ".TOC." : "_GLOBAL_OFFSET_TABLE_", + Out::ElfHeader, Target->GotBaseSymOff); // __ehdr_start is the location of ELF file headers. Note that we define // this symbol unconditionally even when using a linker script, which // differs from the behavior implemented by GNU linker which only define // this symbol if ELF headers are in the memory mapped segment. + addOptionalRegular("__ehdr_start", Out::ElfHeader, 0, STV_HIDDEN); + // __executable_start is not documented, but the expectation of at - // least the android libc is that it points to the elf header too. + // least the Android libc is that it points to the ELF header. + addOptionalRegular("__executable_start", Out::ElfHeader, 0, STV_HIDDEN); + // __dso_handle symbol is passed to cxa_finalize as a marker to identify // each DSO. The address of the symbol doesn't matter as long as they are // different in different DSOs, so we chose the start address of the DSO. - for (const char *Name : - {"__ehdr_start", "__executable_start", "__dso_handle"}) - addOptionalRegular(Name, Out::ElfHeader, 0, STV_HIDDEN); + addOptionalRegular("__dso_handle", Out::ElfHeader, 0, STV_HIDDEN); // If linker script do layout we do not need to create any standart symbols. if (Script->HasSectionsCommand) @@ -267,6 +287,7 @@ template <class ELFT> static void createSyntheticSections() { if (Config->Strip != StripPolicy::All) { InX::StrTab = make<StringTableSection>(".strtab", false); InX::SymTab = make<SymbolTableSection<ELFT>>(*InX::StrTab); + InX::SymTabShndx = make<SymtabShndxSection>(); } if (Config->BuildId != BuildIdKind::None) { @@ -280,10 +301,9 @@ template <class ELFT> static void createSyntheticSections() { // If there is a SECTIONS command and a .data.rel.ro section name use name // .data.rel.ro.bss so that we match in the .data.rel.ro output section. // This makes sure our relro is contiguous. - bool HasDataRelRo = - Script->HasSectionsCommand && findSection(".data.rel.ro"); - InX::BssRelRo = make<BssSection>( - HasDataRelRo ? ".data.rel.ro.bss" : ".bss.rel.ro", 0, 1); + bool HasDataRelRo = Script->HasSectionsCommand && findSection(".data.rel.ro"); + InX::BssRelRo = + make<BssSection>(HasDataRelRo ? ".data.rel.ro.bss" : ".bss.rel.ro", 0, 1); Add(InX::BssRelRo); // Add MIPS-specific sections. @@ -330,6 +350,11 @@ template <class ELFT> static void createSyntheticSections() { Add(InX::RelaDyn); } + if (Config->RelrPackDynRelocs) { + InX::RelrDyn = make<RelrSection<ELFT>>(); + Add(InX::RelrDyn); + } + // Add .got. MIPS' .got is so different from the other archs, // it has its own class. if (Config->EMachine == EM_MIPS) { @@ -346,7 +371,7 @@ template <class ELFT> static void createSyntheticSections() { Add(InX::IgotPlt); if (Config->GdbIndex) { - InX::GdbIndex = createGdbIndex<ELFT>(); + InX::GdbIndex = GdbIndexSection::create<ELFT>(); Add(InX::GdbIndex); } @@ -369,9 +394,9 @@ template <class ELFT> static void createSyntheticSections() { false /*Sort*/); Add(InX::RelaIplt); - InX::Plt = make<PltSection>(Target->PltHeaderSize); + InX::Plt = make<PltSection>(false); Add(InX::Plt); - InX::Iplt = make<PltSection>(0); + InX::Iplt = make<PltSection>(true); Add(InX::Iplt); if (!Config->Relocatable) { @@ -385,6 +410,8 @@ template <class ELFT> static void createSyntheticSections() { if (InX::SymTab) Add(InX::SymTab); + if (InX::SymTabShndx) + Add(InX::SymTabShndx); Add(InX::ShStrTab); if (InX::StrTab) Add(InX::StrTab); @@ -454,6 +481,9 @@ template <class ELFT> void Writer<ELFT>::run() { Sec->Addr = 0; } + if (Config->CheckSections) + checkSections(); + // It does not make sense try to open the file if we have error already. if (errorCount()) return; @@ -476,8 +506,9 @@ template <class ELFT> void Writer<ELFT>::run() { if (errorCount()) return; - // Handle -Map option. + // Handle -Map and -cref options. writeMapFile(); + writeCrossReferenceTable(); if (errorCount()) return; @@ -487,11 +518,7 @@ template <class ELFT> void Writer<ELFT>::run() { static bool shouldKeepInSymtab(SectionBase *Sec, StringRef SymName, const Symbol &B) { - if (B.isFile() || B.isSection()) - return false; - - // If sym references a section in a discarded group, don't keep it. - if (Sec == &InputSection::Discarded) + if (B.isSection()) return false; if (Config->Discard == DiscardPolicy::None) @@ -638,6 +665,9 @@ static bool isRelroSection(const OutputSection *Sec) { if (InX::Got && Sec == InX::Got->getParent()) return true; + if (Sec->Name.equals(".toc")) + return true; + // .got.plt contains pointers to external function symbols. They are // by default resolved lazily, so we usually cannot put it into RELRO. // However, if "-z now" is given, the lazy symbol resolution is @@ -669,20 +699,22 @@ static bool isRelroSection(const OutputSection *Sec) { // * It is easy to check if a give branch was taken. // * It is easy two see how similar two ranks are (see getRankProximity). enum RankFlags { - RF_NOT_ADDR_SET = 1 << 16, - RF_NOT_INTERP = 1 << 15, - RF_NOT_ALLOC = 1 << 14, - RF_WRITE = 1 << 13, - RF_EXEC_WRITE = 1 << 12, - RF_EXEC = 1 << 11, - RF_NON_TLS_BSS = 1 << 10, - RF_NON_TLS_BSS_RO = 1 << 9, - RF_NOT_TLS = 1 << 8, - RF_BSS = 1 << 7, + RF_NOT_ADDR_SET = 1 << 18, + RF_NOT_INTERP = 1 << 17, + RF_NOT_ALLOC = 1 << 16, + RF_WRITE = 1 << 15, + RF_EXEC_WRITE = 1 << 14, + RF_EXEC = 1 << 13, + RF_RODATA = 1 << 12, + RF_NON_TLS_BSS = 1 << 11, + RF_NON_TLS_BSS_RO = 1 << 10, + RF_NOT_TLS = 1 << 9, + RF_BSS = 1 << 8, + RF_NOTE = 1 << 7, RF_PPC_NOT_TOCBSS = 1 << 6, - RF_PPC_OPD = 1 << 5, - RF_PPC_TOCL = 1 << 4, - RF_PPC_TOC = 1 << 3, + RF_PPC_TOCL = 1 << 5, + RF_PPC_TOC = 1 << 4, + RF_PPC_GOT = 1 << 3, RF_PPC_BRANCH_LT = 1 << 2, RF_MIPS_GPREL = 1 << 1, RF_MIPS_NOT_GOT = 1 << 0 @@ -713,8 +745,7 @@ static unsigned getSectionRank(const OutputSection *Sec) { // considerations: // * Read-only sections come first such that they go in the // PT_LOAD covering the program headers at the start of the file. - // * Read-only, executable sections come next, unless the - // -no-rosegment option is used. + // * Read-only, executable sections come next. // * Writable, executable sections follow such that .plt on // architectures where it needs to be writable will be placed // between .text and .data. @@ -726,11 +757,16 @@ static unsigned getSectionRank(const OutputSection *Sec) { if (IsExec) { if (IsWrite) Rank |= RF_EXEC_WRITE; - else if (!Config->SingleRoRx) + else Rank |= RF_EXEC; - } else { - if (IsWrite) - Rank |= RF_WRITE; + } else if (IsWrite) { + Rank |= RF_WRITE; + } else if (Sec->Type == SHT_PROGBITS) { + // Make non-executable and non-writable PROGBITS sections (e.g .rodata + // .eh_frame) closer to .text. They likely contain PC or GOT relative + // relocations and there could be relocation overflow if other huge sections + // (.dynstr .dynsym) were placed in between. + Rank |= RF_RODATA; } // If we got here we know that both A and B are in the same PT_LOAD. @@ -766,6 +802,12 @@ static unsigned getSectionRank(const OutputSection *Sec) { if (IsNoBits) Rank |= RF_BSS; + // We create a NOTE segment for contiguous .note sections, so make + // them contigous if there are more than one .note section with the + // same attributes. + if (Sec->Type == SHT_NOTE) + Rank |= RF_NOTE; + // Some architectures have additional ordering restrictions for sections // within the same PT_LOAD. if (Config->EMachine == EM_PPC64) { @@ -779,18 +821,19 @@ static unsigned getSectionRank(const OutputSection *Sec) { if (Name != ".tocbss") Rank |= RF_PPC_NOT_TOCBSS; - if (Name == ".opd") - Rank |= RF_PPC_OPD; - if (Name == ".toc1") Rank |= RF_PPC_TOCL; if (Name == ".toc") Rank |= RF_PPC_TOC; + if (Name == ".got") + Rank |= RF_PPC_GOT; + if (Name == ".branch_lt") Rank |= RF_PPC_BRANCH_LT; } + if (Config->EMachine == EM_MIPS) { // All sections with SHF_MIPS_GPREL flag should be grouped together // because data in these sections is addressable with a gp relative address. @@ -822,8 +865,6 @@ void PhdrEntry::add(OutputSection *Sec) { p_align = std::max(p_align, Sec->Alignment); if (p_type == PT_LOAD) Sec->PtLoad = this; - if (Sec->LMAExpr) - ASectionHasLMA = true; } // The beginning and the ending of .rel[a].plt section are marked @@ -833,17 +874,19 @@ void PhdrEntry::add(OutputSection *Sec) { // need these symbols, since IRELATIVE relocs are resolved through GOT // and PLT. For details, see http://www.airs.com/blog/archives/403. template <class ELFT> void Writer<ELFT>::addRelIpltSymbols() { - if (!Config->Static) + if (needsInterpSection()) return; StringRef S = Config->IsRela ? "__rela_iplt_start" : "__rel_iplt_start"; addOptionalRegular(S, InX::RelaIplt, 0, STV_HIDDEN, STB_WEAK); S = Config->IsRela ? "__rela_iplt_end" : "__rel_iplt_end"; - addOptionalRegular(S, InX::RelaIplt, -1, STV_HIDDEN, STB_WEAK); + ElfSym::RelaIpltEnd = + addOptionalRegular(S, InX::RelaIplt, 0, STV_HIDDEN, STB_WEAK); } template <class ELFT> -void Writer<ELFT>::forEachRelSec(std::function<void(InputSectionBase &)> Fn) { +void Writer<ELFT>::forEachRelSec( + llvm::function_ref<void(InputSectionBase &)> Fn) { // Scan all relocations. Each relocation goes through a series // of tests to determine if it needs special treatment, such as // creating GOT, PLT, copy relocations, etc. @@ -863,14 +906,18 @@ void Writer<ELFT>::forEachRelSec(std::function<void(InputSectionBase &)> Fn) { // defining these symbols explicitly in the linker script. template <class ELFT> void Writer<ELFT>::setReservedSymbolSections() { if (ElfSym::GlobalOffsetTable) { - // The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention to - // be at some offset from the base of the .got section, usually 0 or the end - // of the .got - InputSection *GotSection = InX::MipsGot ? cast<InputSection>(InX::MipsGot) - : cast<InputSection>(InX::Got); + // The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention usually + // to the start of the .got or .got.plt section. + InputSection *GotSection = InX::GotPlt; + if (!Target->GotBaseSymInGotPlt) + GotSection = InX::MipsGot ? cast<InputSection>(InX::MipsGot) + : cast<InputSection>(InX::Got); ElfSym::GlobalOffsetTable->Section = GotSection; } + if (ElfSym::RelaIpltEnd) + ElfSym::RelaIpltEnd->Value = InX::RelaIplt->getSize(); + PhdrEntry *Last = nullptr; PhdrEntry *LastRO = nullptr; @@ -940,8 +987,7 @@ static int getRankProximityAux(OutputSection *A, OutputSection *B) { static int getRankProximity(OutputSection *A, BaseCommand *B) { if (auto *Sec = dyn_cast<OutputSection>(B)) - if (Sec->Live) - return getRankProximityAux(A, Sec); + return getRankProximityAux(A, Sec); return -1; } @@ -960,11 +1006,9 @@ static int getRankProximity(OutputSection *A, BaseCommand *B) { // rw_sec : { *(rw_sec) } // would mean that the RW PT_LOAD would become unaligned. static bool shouldSkip(BaseCommand *Cmd) { - if (isa<OutputSection>(Cmd)) - return false; if (auto *Assign = dyn_cast<SymbolAssignment>(Cmd)) return Assign->Name != "."; - return true; + return false; } // We want to place orphan sections so that they share as much @@ -987,20 +1031,16 @@ findOrphanPos(std::vector<BaseCommand *>::iterator B, int Proximity = getRankProximity(Sec, *I); for (; I != E; ++I) { auto *CurSec = dyn_cast<OutputSection>(*I); - if (!CurSec || !CurSec->Live) + if (!CurSec) continue; if (getRankProximity(Sec, CurSec) != Proximity || Sec->SortRank < CurSec->SortRank) break; } - auto IsLiveSection = [](BaseCommand *Cmd) { - auto *OS = dyn_cast<OutputSection>(Cmd); - return OS && OS->Live; - }; - + auto IsOutputSec = [](BaseCommand *Cmd) { return isa<OutputSection>(Cmd); }; auto J = std::find_if(llvm::make_reverse_iterator(I), - llvm::make_reverse_iterator(B), IsLiveSection); + llvm::make_reverse_iterator(B), IsOutputSec); I = J.base(); // As a special case, if the orphan section is the last section, put @@ -1008,7 +1048,7 @@ findOrphanPos(std::vector<BaseCommand *>::iterator B, // This matches bfd's behavior and is convenient when the linker script fully // specifies the start of the file, but doesn't care about the end (the non // alloc sections for example). - auto NextSec = std::find_if(I, E, IsLiveSection); + auto NextSec = std::find_if(I, E, IsOutputSec); if (NextSec == E) return E; @@ -1017,32 +1057,172 @@ findOrphanPos(std::vector<BaseCommand *>::iterator B, return I; } -// If no layout was provided by linker script, we want to apply default -// sorting for special input sections and handle --symbol-ordering-file. -template <class ELFT> void Writer<ELFT>::sortInputSections() { - assert(!Script->HasSectionsCommand); +// Builds section order for handling --symbol-ordering-file. +static DenseMap<const InputSectionBase *, int> buildSectionOrder() { + DenseMap<const InputSectionBase *, int> SectionOrder; + // Use the rarely used option -call-graph-ordering-file to sort sections. + if (!Config->CallGraphProfile.empty()) + return computeCallGraphProfileOrder(); - // Sort input sections by priority using the list provided - // by --symbol-ordering-file. - DenseMap<SectionBase *, int> Order = buildSectionOrder(); - if (!Order.empty()) - for (BaseCommand *Base : Script->SectionCommands) - if (auto *Sec = dyn_cast<OutputSection>(Base)) - if (Sec->Live) - Sec->sort([&](InputSectionBase *S) { return Order.lookup(S); }); + if (Config->SymbolOrderingFile.empty()) + return SectionOrder; + + struct SymbolOrderEntry { + int Priority; + bool Present; + }; + + // Build a map from symbols to their priorities. Symbols that didn't + // appear in the symbol ordering file have the lowest priority 0. + // All explicitly mentioned symbols have negative (higher) priorities. + DenseMap<StringRef, SymbolOrderEntry> SymbolOrder; + int Priority = -Config->SymbolOrderingFile.size(); + for (StringRef S : Config->SymbolOrderingFile) + SymbolOrder.insert({S, {Priority++, false}}); + + // Build a map from sections to their priorities. + auto AddSym = [&](Symbol &Sym) { + auto It = SymbolOrder.find(Sym.getName()); + if (It == SymbolOrder.end()) + return; + SymbolOrderEntry &Ent = It->second; + Ent.Present = true; + + warnUnorderableSymbol(&Sym); + + if (auto *D = dyn_cast<Defined>(&Sym)) { + if (auto *Sec = dyn_cast_or_null<InputSectionBase>(D->Section)) { + int &Priority = SectionOrder[cast<InputSectionBase>(Sec->Repl)]; + Priority = std::min(Priority, Ent.Priority); + } + } + }; + // We want both global and local symbols. We get the global ones from the + // symbol table and iterate the object files for the local ones. + for (Symbol *Sym : Symtab->getSymbols()) + if (!Sym->isLazy()) + AddSym(*Sym); + for (InputFile *File : ObjectFiles) + for (Symbol *Sym : File->getSymbols()) + if (Sym->isLocal()) + AddSym(*Sym); + + if (Config->WarnSymbolOrdering) + for (auto OrderEntry : SymbolOrder) + if (!OrderEntry.second.Present) + warn("symbol ordering file: no such symbol: " + OrderEntry.first); + + return SectionOrder; +} + +// Sorts the sections in ISD according to the provided section order. +static void +sortISDBySectionOrder(InputSectionDescription *ISD, + const DenseMap<const InputSectionBase *, int> &Order) { + std::vector<InputSection *> UnorderedSections; + std::vector<std::pair<InputSection *, int>> OrderedSections; + uint64_t UnorderedSize = 0; + + for (InputSection *IS : ISD->Sections) { + auto I = Order.find(IS); + if (I == Order.end()) { + UnorderedSections.push_back(IS); + UnorderedSize += IS->getSize(); + continue; + } + OrderedSections.push_back({IS, I->second}); + } + llvm::sort( + OrderedSections.begin(), OrderedSections.end(), + [&](std::pair<InputSection *, int> A, std::pair<InputSection *, int> B) { + return A.second < B.second; + }); + + // Find an insertion point for the ordered section list in the unordered + // section list. On targets with limited-range branches, this is the mid-point + // of the unordered section list. This decreases the likelihood that a range + // extension thunk will be needed to enter or exit the ordered region. If the + // ordered section list is a list of hot functions, we can generally expect + // the ordered functions to be called more often than the unordered functions, + // making it more likely that any particular call will be within range, and + // therefore reducing the number of thunks required. + // + // For example, imagine that you have 8MB of hot code and 32MB of cold code. + // If the layout is: + // + // 8MB hot + // 32MB cold + // + // only the first 8-16MB of the cold code (depending on which hot function it + // is actually calling) can call the hot code without a range extension thunk. + // However, if we use this layout: + // + // 16MB cold + // 8MB hot + // 16MB cold + // + // both the last 8-16MB of the first block of cold code and the first 8-16MB + // of the second block of cold code can call the hot code without a thunk. So + // we effectively double the amount of code that could potentially call into + // the hot code without a thunk. + size_t InsPt = 0; + if (Target->ThunkSectionSpacing && !OrderedSections.empty()) { + uint64_t UnorderedPos = 0; + for (; InsPt != UnorderedSections.size(); ++InsPt) { + UnorderedPos += UnorderedSections[InsPt]->getSize(); + if (UnorderedPos > UnorderedSize / 2) + break; + } + } + + ISD->Sections.clear(); + for (InputSection *IS : makeArrayRef(UnorderedSections).slice(0, InsPt)) + ISD->Sections.push_back(IS); + for (std::pair<InputSection *, int> P : OrderedSections) + ISD->Sections.push_back(P.first); + for (InputSection *IS : makeArrayRef(UnorderedSections).slice(InsPt)) + ISD->Sections.push_back(IS); +} + +static void sortSection(OutputSection *Sec, + const DenseMap<const InputSectionBase *, int> &Order) { + StringRef Name = Sec->Name; // Sort input sections by section name suffixes for // __attribute__((init_priority(N))). - if (OutputSection *Sec = findSection(".init_array")) - Sec->sortInitFini(); - if (OutputSection *Sec = findSection(".fini_array")) - Sec->sortInitFini(); + if (Name == ".init_array" || Name == ".fini_array") { + if (!Script->HasSectionsCommand) + Sec->sortInitFini(); + return; + } // Sort input sections by the special rule for .ctors and .dtors. - if (OutputSection *Sec = findSection(".ctors")) - Sec->sortCtorsDtors(); - if (OutputSection *Sec = findSection(".dtors")) - Sec->sortCtorsDtors(); + if (Name == ".ctors" || Name == ".dtors") { + if (!Script->HasSectionsCommand) + Sec->sortCtorsDtors(); + return; + } + + // Never sort these. + if (Name == ".init" || Name == ".fini") + return; + + // Sort input sections by priority using the list provided + // by --symbol-ordering-file. + if (!Order.empty()) + for (BaseCommand *B : Sec->SectionCommands) + if (auto *ISD = dyn_cast<InputSectionDescription>(B)) + sortISDBySectionOrder(ISD, Order); +} + +// If no layout was provided by linker script, we want to apply default +// sorting for special input sections. This also handles --symbol-ordering-file. +template <class ELFT> void Writer<ELFT>::sortInputSections() { + // Build the order once since it is expensive. + DenseMap<const InputSectionBase *, int> Order = buildSectionOrder(); + for (BaseCommand *Base : Script->SectionCommands) + if (auto *Sec = dyn_cast<OutputSection>(Base)) + sortSection(Sec, Order); } template <class ELFT> void Writer<ELFT>::sortSections() { @@ -1053,22 +1233,29 @@ template <class ELFT> void Writer<ELFT>::sortSections() { if (Config->Relocatable) return; - for (BaseCommand *Base : Script->SectionCommands) - if (auto *Sec = dyn_cast<OutputSection>(Base)) - Sec->SortRank = getSectionRank(Sec); + sortInputSections(); - if (!Script->HasSectionsCommand) { - sortInputSections(); + for (BaseCommand *Base : Script->SectionCommands) { + auto *OS = dyn_cast<OutputSection>(Base); + if (!OS) + continue; + OS->SortRank = getSectionRank(OS); + + // We want to assign rude approximation values to OutSecOff fields + // to know the relative order of the input sections. We use it for + // sorting SHF_LINK_ORDER sections. See resolveShfLinkOrder(). + uint64_t I = 0; + for (InputSection *Sec : getInputSections(OS)) + Sec->OutSecOff = I++; + } + if (!Script->HasSectionsCommand) { // We know that all the OutputSections are contiguous in this case. - auto E = Script->SectionCommands.end(); - auto I = Script->SectionCommands.begin(); auto IsSection = [](BaseCommand *Base) { return isa<OutputSection>(Base); }; - I = std::find_if(I, E, IsSection); - E = std::find_if(llvm::make_reverse_iterator(E), - llvm::make_reverse_iterator(I), IsSection) - .base(); - std::stable_sort(I, E, compareSections); + std::stable_sort( + llvm::find_if(Script->SectionCommands, IsSection), + llvm::find_if(llvm::reverse(Script->SectionCommands), IsSection).base(), + compareSections); return; } @@ -1099,7 +1286,7 @@ template <class ELFT> void Writer<ELFT>::sortSections() { // The way we define an order then is: // * Sort only the orphan sections. They are in the end right now. // * Move each orphan section to its preferred position. We try - // to put each section in the last position where it it can share + // to put each section in the last position where it can share // a PT_LOAD. // // There is some ambiguity as to where exactly a new entry should be @@ -1115,7 +1302,7 @@ template <class ELFT> void Writer<ELFT>::sortSections() { auto E = Script->SectionCommands.end(); auto NonScriptI = std::find_if(I, E, [](BaseCommand *Base) { if (auto *Sec = dyn_cast<OutputSection>(Base)) - return Sec->Live && Sec->SectionIndex == INT_MAX; + return Sec->SectionIndex == UINT32_MAX; return false; }); @@ -1190,8 +1377,7 @@ static bool isDuplicateArmExidxSec(InputSection *Prev, InputSection *Cur) { }; // Get the last table Entry from the previous .ARM.exidx section. - const ExidxEntry &PrevEntry = *reinterpret_cast<const ExidxEntry *>( - Prev->Data.data() + Prev->getSize() - sizeof(ExidxEntry)); + const ExidxEntry &PrevEntry = Prev->getDataAs<ExidxEntry>().back(); if (IsExtabRef(PrevEntry.Unwind)) return false; @@ -1203,14 +1389,7 @@ static bool isDuplicateArmExidxSec(InputSection *Prev, InputSection *Cur) { // consecutive identical entries are rare and the effort to check that they // are identical is high. - if (isa<SyntheticSection>(Cur)) - // Exidx sentinel section has implicit EXIDX_CANTUNWIND; - return PrevEntry.Unwind == 0x1; - - ArrayRef<const ExidxEntry> Entries( - reinterpret_cast<const ExidxEntry *>(Cur->Data.data()), - Cur->getSize() / sizeof(ExidxEntry)); - for (const ExidxEntry &Entry : Entries) + for (const ExidxEntry Entry : Cur->getDataAs<ExidxEntry>()) if (IsExtabRef(Entry.Unwind) || Entry.Unwind != PrevEntry.Unwind) return false; // All table entries in this .ARM.exidx Section can be merged into the @@ -1240,13 +1419,12 @@ template <class ELFT> void Writer<ELFT>::resolveShfLinkOrder() { if (!Config->Relocatable && Config->EMachine == EM_ARM && Sec->Type == SHT_ARM_EXIDX) { - if (!Sections.empty() && isa<ARMExidxSentinelSection>(Sections.back())) { + if (auto *Sentinel = dyn_cast<ARMExidxSentinelSection>(Sections.back())) { assert(Sections.size() >= 2 && "We should create a sentinel section only if there are " "alive regular exidx sections."); // The last executable section is required to fill the sentinel. // Remember it here so that we don't have to find it again. - auto *Sentinel = cast<ARMExidxSentinelSection>(Sections.back()); Sentinel->Highest = Sections[Sections.size() - 2]->getLinkOrderDep(); } @@ -1257,16 +1435,13 @@ template <class ELFT> void Writer<ELFT>::resolveShfLinkOrder() { // previous one. This does not require any rewriting of InputSection // contents but misses opportunities for fine grained deduplication // where only a subset of the InputSection contents can be merged. - int Cur = 1; - int Prev = 0; + size_t Prev = 0; // The last one is a sentinel entry which should not be removed. - int N = Sections.size() - 1; - while (Cur < N) { - if (isDuplicateArmExidxSec(Sections[Prev], Sections[Cur])) - Sections[Cur] = nullptr; + for (size_t I = 1; I < Sections.size() - 1; ++I) { + if (isDuplicateArmExidxSec(Sections[Prev], Sections[I])) + Sections[I] = nullptr; else - Prev = Cur; - ++Cur; + Prev = I; } } } @@ -1277,14 +1452,12 @@ template <class ELFT> void Writer<ELFT>::resolveShfLinkOrder() { // Remove the Sections we marked as duplicate earlier. for (BaseCommand *Base : Sec->SectionCommands) if (auto *ISD = dyn_cast<InputSectionDescription>(Base)) - ISD->Sections.erase( - std::remove(ISD->Sections.begin(), ISD->Sections.end(), nullptr), - ISD->Sections.end()); + llvm::erase_if(ISD->Sections, [](InputSection *IS) { return !IS; }); } } static void applySynthetic(const std::vector<SyntheticSection *> &Sections, - std::function<void(SyntheticSection *)> Fn) { + llvm::function_ref<void(SyntheticSection *)> Fn) { for (SyntheticSection *SS : Sections) if (SS && SS->getParent() && !SS->empty()) Fn(SS); @@ -1311,27 +1484,15 @@ static void removeUnusedSyntheticSections() { if (!SS) return; OutputSection *OS = SS->getParent(); - if (!SS->empty() || !OS) + if (!OS || !SS->empty()) continue; - std::vector<BaseCommand *>::iterator Empty = OS->SectionCommands.end(); - for (auto I = OS->SectionCommands.begin(), E = OS->SectionCommands.end(); - I != E; ++I) { - BaseCommand *B = *I; - if (auto *ISD = dyn_cast<InputSectionDescription>(B)) { + // If we reach here, then SS is an unused synthetic section and we want to + // remove it from corresponding input section description of output section. + for (BaseCommand *B : OS->SectionCommands) + if (auto *ISD = dyn_cast<InputSectionDescription>(B)) llvm::erase_if(ISD->Sections, [=](InputSection *IS) { return IS == SS; }); - if (ISD->Sections.empty()) - Empty = I; - } - } - if (Empty != OS->SectionCommands.end()) - OS->SectionCommands.erase(Empty); - - // If there are no other sections in the output section, remove it from the - // output. - if (OS->SectionCommands.empty()) - OS->Live = false; } } @@ -1423,9 +1584,9 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() { if (InX::DynSymTab && Sym->includeInDynsym()) { InX::DynSymTab->addSymbol(Sym); - if (auto *SS = dyn_cast<SharedSymbol>(Sym)) - if (cast<SharedFile<ELFT>>(Sym->File)->IsNeeded) - In<ELFT>::VerNeed->addSymbol(SS); + if (auto *File = dyn_cast_or_null<SharedFile<ELFT>>(Sym->File)) + if (File->IsNeeded && !Sym->isUndefined()) + In<ELFT>::VerNeed->addSymbol(Sym); } } @@ -1433,10 +1594,12 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() { if (errorCount()) return; + if (InX::MipsGot) + InX::MipsGot->build<ELFT>(); + removeUnusedSyntheticSections(); sortSections(); - Script->removeEmptyCommands(); // Now that we have the final list, create a list of all the // OutputSections for convenience. @@ -1444,6 +1607,15 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() { if (auto *Sec = dyn_cast<OutputSection>(Base)) OutputSections.push_back(Sec); + // Ensure data sections are not mixed with executable sections when + // -execute-only is used. + if (Config->ExecuteOnly) + for (OutputSection *OS : OutputSections) + if (OS->Flags & SHF_EXECINSTR) + for (InputSection *IS : getInputSections(OS)) + if (!(IS->Flags & SHF_EXECINSTR)) + error("-execute-only does not support intermingling data and code"); + // Prefer command line supplied address over other constraints. for (OutputSection *Sec : OutputSections) { auto I = Config->SectionStartMap.find(Sec->Name); @@ -1452,7 +1624,7 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() { } // This is a bit of a hack. A value of 0 means undef, so we set it - // to 1 t make __ehdr_start defined. The section number is not + // to 1 to make __ehdr_start defined. The section number is not // particularly relevant. Out::ElfHeader->SectionIndex = 1; @@ -1478,12 +1650,13 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() { // Dynamic section must be the last one in this list and dynamic // symbol table section (DynSymTab) must be the first one. applySynthetic( - {InX::DynSymTab, InX::Bss, InX::BssRelRo, InX::GnuHashTab, - InX::HashTab, InX::SymTab, InX::ShStrTab, InX::StrTab, - In<ELFT>::VerDef, InX::DynStrTab, InX::Got, InX::MipsGot, - InX::IgotPlt, InX::GotPlt, InX::RelaDyn, InX::RelaIplt, - InX::RelaPlt, InX::Plt, InX::Iplt, InX::EhFrameHdr, - In<ELFT>::VerSym, In<ELFT>::VerNeed, InX::Dynamic}, + {InX::DynSymTab, InX::Bss, InX::BssRelRo, InX::GnuHashTab, + InX::HashTab, InX::SymTab, InX::SymTabShndx, InX::ShStrTab, + InX::StrTab, In<ELFT>::VerDef, InX::DynStrTab, InX::Got, + InX::MipsGot, InX::IgotPlt, InX::GotPlt, InX::RelaDyn, + InX::RelrDyn, InX::RelaIplt, InX::RelaPlt, InX::Plt, + InX::Iplt, InX::EhFrameHdr, In<ELFT>::VerSym, In<ELFT>::VerNeed, + InX::Dynamic}, [](SyntheticSection *SS) { SS->finalizeContents(); }); if (!Script->HasSectionsCommand && !Config->Relocatable) @@ -1495,10 +1668,11 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() { // Some architectures need to generate content that depends on the address // of InputSections. For example some architectures use small displacements - // for jump instructions that is is the linker's responsibility for creating + // for jump instructions that is the linker's responsibility for creating // range extension thunks for. As the generation of the content may also // alter InputSection addresses we must converge to a fixed point. - if (Target->NeedsThunks || Config->AndroidPackDynRelocs) { + if (Target->NeedsThunks || Config->AndroidPackDynRelocs || + Config->RelrPackDynRelocs) { ThunkCreator TC; AArch64Err843419Patcher A64P; bool Changed; @@ -1515,34 +1689,48 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() { if (InX::MipsGot) InX::MipsGot->updateAllocSize(); Changed |= InX::RelaDyn->updateAllocSize(); + if (InX::RelrDyn) + Changed |= InX::RelrDyn->updateAllocSize(); } while (Changed); } + // createThunks may have added local symbols to the static symbol table + applySynthetic({InX::SymTab}, + [](SyntheticSection *SS) { SS->postThunkContents(); }); + // Fill other section headers. The dynamic table is finalized // at the end because some tags like RELSZ depend on result // of finalizing other sections. for (OutputSection *Sec : OutputSections) Sec->finalize<ELFT>(); - - // createThunks may have added local symbols to the static symbol table - applySynthetic({InX::SymTab}, - [](SyntheticSection *SS) { SS->postThunkContents(); }); } // The linker is expected to define SECNAME_start and SECNAME_end // symbols for a few sections. This function defines them. template <class ELFT> void Writer<ELFT>::addStartEndSymbols() { - auto Define = [&](StringRef Start, StringRef End, OutputSection *OS) { - // These symbols resolve to the image base if the section does not exist. - // A special value -1 indicates end of the section. + // If a section does not exist, there's ambiguity as to how we + // define _start and _end symbols for an init/fini section. Since + // the loader assume that the symbols are always defined, we need to + // always define them. But what value? The loader iterates over all + // pointers between _start and _end to run global ctors/dtors, so if + // the section is empty, their symbol values don't actually matter + // as long as _start and _end point to the same location. + // + // That said, we don't want to set the symbols to 0 (which is + // probably the simplest value) because that could cause some + // program to fail to link due to relocation overflow, if their + // program text is above 2 GiB. We use the address of the .text + // section instead to prevent that failure. + OutputSection *Default = findSection(".text"); + if (!Default) + Default = Out::ElfHeader; + auto Define = [=](StringRef Start, StringRef End, OutputSection *OS) { if (OS) { addOptionalRegular(Start, OS, 0); addOptionalRegular(End, OS, -1); } else { - if (Config->Pic) - OS = Out::ElfHeader; - addOptionalRegular(Start, OS, 0); - addOptionalRegular(End, OS, 0); + addOptionalRegular(Start, Default, 0); + addOptionalRegular(End, Default, 0); } }; @@ -1564,12 +1752,12 @@ void Writer<ELFT>::addStartStopSymbols(OutputSection *Sec) { StringRef S = Sec->Name; if (!isValidCIdentifier(S)) return; - addOptionalRegular(Saver.save("__start_" + S), Sec, 0, STV_DEFAULT); - addOptionalRegular(Saver.save("__stop_" + S), Sec, -1, STV_DEFAULT); + addOptionalRegular(Saver.save("__start_" + S), Sec, 0, STV_PROTECTED); + addOptionalRegular(Saver.save("__stop_" + S), Sec, -1, STV_PROTECTED); } static bool needsPtLoad(OutputSection *Sec) { - if (!(Sec->Flags & SHF_ALLOC)) + if (!(Sec->Flags & SHF_ALLOC) || Sec->Noload) return false; // Don't allocate VA space for TLS NOBITS sections. The PT_TLS PHDR is @@ -1587,6 +1775,8 @@ static bool needsPtLoad(OutputSection *Sec) { static uint64_t computeFlags(uint64_t Flags) { if (Config->Omagic) return PF_R | PF_W | PF_X; + if (Config->ExecuteOnly && (Flags & PF_X)) + return Flags & ~PF_R; if (Config->SingleRoRx && !(Flags & PF_W)) return Flags | PF_X; return Flags; @@ -1626,9 +1816,11 @@ template <class ELFT> std::vector<PhdrEntry *> Writer<ELFT>::createPhdrs() { // (e.g. executable or writable). There is one phdr for each segment. // Therefore, we need to create a new phdr when the next section has // different flags or is loaded at a discontiguous address using AT linker - // script command. + // script command. At the same time, we don't want to create a separate + // load segment for the headers, even if the first output section has + // an AT attribute. uint64_t NewFlags = computeFlags(Sec->getPhdrFlags()); - if ((Sec->LMAExpr && Load->ASectionHasLMA) || + if ((Sec->LMAExpr && Load->LastSec != Out::ProgramHeaders) || Sec->MemRegion != Load->FirstSec->MemRegion || Flags != NewFlags) { Load = AddHdr(PT_LOAD, NewFlags); @@ -1691,11 +1883,9 @@ template <class ELFT> std::vector<PhdrEntry *> Writer<ELFT>::createPhdrs() { // pages for the stack non-executable. If you really want an executable // stack, you can pass -z execstack, but that's not recommended for // security reasons. - unsigned Perm; + unsigned Perm = PF_R | PF_W; if (Config->ZExecstack) - Perm = PF_R | PF_W | PF_X; - else - Perm = PF_R | PF_W; + Perm |= PF_X; AddHdr(PT_GNU_STACK, Perm)->p_memsz = Config->ZStackSize; // PT_OPENBSD_WXNEEDED is a OpenBSD-specific header to mark the executable @@ -1813,6 +2003,12 @@ template <class ELFT> void Writer<ELFT>::assignFileOffsetsBinary() { FileSize = alignTo(Off, Config->Wordsize); } +static std::string rangeToString(uint64_t Addr, uint64_t Len) { + if (Len == 0) + return "<empty range at 0x" + utohexstr(Addr) + ">"; + return "[0x" + utohexstr(Addr) + ", 0x" + utohexstr(Addr + Len - 1) + "]"; +} + // Assign file offsets to output sections. template <class ELFT> void Writer<ELFT>::assignFileOffsets() { uint64_t Off = 0; @@ -1837,6 +2033,24 @@ template <class ELFT> void Writer<ELFT>::assignFileOffsets() { SectionHeaderOff = alignTo(Off, Config->Wordsize); FileSize = SectionHeaderOff + (OutputSections.size() + 1) * sizeof(Elf_Shdr); + + // Our logic assumes that sections have rising VA within the same segment. + // With use of linker scripts it is possible to violate this rule and get file + // offset overlaps or overflows. That should never happen with a valid script + // which does not move the location counter backwards and usually scripts do + // not do that. Unfortunately, there are apps in the wild, for example, Linux + // kernel, which control segment distribution explicitly and move the counter + // backwards, so we have to allow doing that to support linking them. We + // perform non-critical checks for overlaps in checkSectionOverlap(), but here + // we want to prevent file size overflows because it would crash the linker. + for (OutputSection *Sec : OutputSections) { + if (Sec->Type == SHT_NOBITS) + continue; + if ((Sec->Offset > FileSize) || (Sec->Offset + Sec->Size > FileSize)) + error("unable to place section " + Sec->Name + " at file offset " + + rangeToString(Sec->Offset, Sec->Offset + Sec->Size) + + "; check your linker script for overflows"); + } } // Finalize the program headers. We call this function after we assign @@ -1875,6 +2089,97 @@ template <class ELFT> void Writer<ELFT>::setPhdrs() { } } +// A helper struct for checkSectionOverlap. +namespace { +struct SectionOffset { + OutputSection *Sec; + uint64_t Offset; +}; +} // namespace + +// Check whether sections overlap for a specific address range (file offsets, +// load and virtual adresses). +static void checkOverlap(StringRef Name, std::vector<SectionOffset> &Sections, + bool IsVirtualAddr) { + llvm::sort(Sections.begin(), Sections.end(), + [=](const SectionOffset &A, const SectionOffset &B) { + return A.Offset < B.Offset; + }); + + // Finding overlap is easy given a vector is sorted by start position. + // If an element starts before the end of the previous element, they overlap. + for (size_t I = 1, End = Sections.size(); I < End; ++I) { + SectionOffset A = Sections[I - 1]; + SectionOffset B = Sections[I]; + if (B.Offset >= A.Offset + A.Sec->Size) + continue; + + // If both sections are in OVERLAY we allow the overlapping of virtual + // addresses, because it is what OVERLAY was designed for. + if (IsVirtualAddr && A.Sec->InOverlay && B.Sec->InOverlay) + continue; + + errorOrWarn("section " + A.Sec->Name + " " + Name + + " range overlaps with " + B.Sec->Name + "\n>>> " + A.Sec->Name + + " range is " + rangeToString(A.Offset, A.Sec->Size) + "\n>>> " + + B.Sec->Name + " range is " + + rangeToString(B.Offset, B.Sec->Size)); + } +} + +// Check for overlapping sections and address overflows. +// +// In this function we check that none of the output sections have overlapping +// file offsets. For SHF_ALLOC sections we also check that the load address +// ranges and the virtual address ranges don't overlap +template <class ELFT> void Writer<ELFT>::checkSections() { + // First, check that section's VAs fit in available address space for target. + for (OutputSection *OS : OutputSections) + if ((OS->Addr + OS->Size < OS->Addr) || + (!ELFT::Is64Bits && OS->Addr + OS->Size > UINT32_MAX)) + errorOrWarn("section " + OS->Name + " at 0x" + utohexstr(OS->Addr) + + " of size 0x" + utohexstr(OS->Size) + + " exceeds available address space"); + + // Check for overlapping file offsets. In this case we need to skip any + // section marked as SHT_NOBITS. These sections don't actually occupy space in + // the file so Sec->Offset + Sec->Size can overlap with others. If --oformat + // binary is specified only add SHF_ALLOC sections are added to the output + // file so we skip any non-allocated sections in that case. + std::vector<SectionOffset> FileOffs; + for (OutputSection *Sec : OutputSections) + if (0 < Sec->Size && Sec->Type != SHT_NOBITS && + (!Config->OFormatBinary || (Sec->Flags & SHF_ALLOC))) + FileOffs.push_back({Sec, Sec->Offset}); + checkOverlap("file", FileOffs, false); + + // When linking with -r there is no need to check for overlapping virtual/load + // addresses since those addresses will only be assigned when the final + // executable/shared object is created. + if (Config->Relocatable) + return; + + // Checking for overlapping virtual and load addresses only needs to take + // into account SHF_ALLOC sections since others will not be loaded. + // Furthermore, we also need to skip SHF_TLS sections since these will be + // mapped to other addresses at runtime and can therefore have overlapping + // ranges in the file. + std::vector<SectionOffset> VMAs; + for (OutputSection *Sec : OutputSections) + if (0 < Sec->Size && (Sec->Flags & SHF_ALLOC) && !(Sec->Flags & SHF_TLS)) + VMAs.push_back({Sec, Sec->Addr}); + checkOverlap("virtual address", VMAs, true); + + // Finally, check that the load addresses don't overlap. This will usually be + // the same as the virtual addresses but can be different when using a linker + // script with AT(). + std::vector<SectionOffset> LMAs; + for (OutputSection *Sec : OutputSections) + if (0 < Sec->Size && (Sec->Flags & SHF_ALLOC) && !(Sec->Flags & SHF_TLS)) + LMAs.push_back({Sec, Sec->getLMA()}); + checkOverlap("load address", LMAs, false); +} + // The entry point address is chosen in the following ways. // // 1. the '-e' entry command-line option; @@ -1916,8 +2221,20 @@ static uint16_t getELFType() { return ET_EXEC; } +static uint8_t getAbiVersion() { + // MIPS non-PIC executable gets ABI version 1. + if (Config->EMachine == EM_MIPS && getELFType() == ET_EXEC && + (Config->EFlags & (EF_MIPS_PIC | EF_MIPS_CPIC)) == EF_MIPS_CPIC) + return 1; + return 0; +} + template <class ELFT> void Writer<ELFT>::writeHeader() { uint8_t *Buf = Buffer->getBufferStart(); + // For executable segments, the trap instructions are written before writing + // the header. Setting Elf header bytes to zero ensures that any unused bytes + // in header are zero-cleared, instead of having trap instructions. + memset(Buf, 0, sizeof(Elf_Ehdr)); memcpy(Buf, "\177ELF", 4); // Write the ELF header. @@ -1926,6 +2243,7 @@ template <class ELFT> void Writer<ELFT>::writeHeader() { EHdr->e_ident[EI_DATA] = Config->IsLE ? ELFDATA2LSB : ELFDATA2MSB; EHdr->e_ident[EI_VERSION] = EV_CURRENT; EHdr->e_ident[EI_OSABI] = Config->OSABI; + EHdr->e_ident[EI_ABIVERSION] = getAbiVersion(); EHdr->e_type = getELFType(); EHdr->e_machine = Config->EMachine; EHdr->e_version = EV_CURRENT; @@ -1935,8 +2253,6 @@ template <class ELFT> void Writer<ELFT>::writeHeader() { EHdr->e_ehsize = sizeof(Elf_Ehdr); EHdr->e_phnum = Phdrs.size(); EHdr->e_shentsize = sizeof(Elf_Shdr); - EHdr->e_shnum = OutputSections.size() + 1; - EHdr->e_shstrndx = InX::ShStrTab->getParent()->SectionIndex; if (!Config->Relocatable) { EHdr->e_phoff = sizeof(Elf_Ehdr); @@ -1957,8 +2273,30 @@ template <class ELFT> void Writer<ELFT>::writeHeader() { ++HBuf; } - // Write the section header table. Note that the first table entry is null. + // Write the section header table. + // + // The ELF header can only store numbers up to SHN_LORESERVE in the e_shnum + // and e_shstrndx fields. When the value of one of these fields exceeds + // SHN_LORESERVE ELF requires us to put sentinel values in the ELF header and + // use fields in the section header at index 0 to store + // the value. The sentinel values and fields are: + // e_shnum = 0, SHdrs[0].sh_size = number of sections. + // e_shstrndx = SHN_XINDEX, SHdrs[0].sh_link = .shstrtab section index. auto *SHdrs = reinterpret_cast<Elf_Shdr *>(Buf + EHdr->e_shoff); + size_t Num = OutputSections.size() + 1; + if (Num >= SHN_LORESERVE) + SHdrs->sh_size = Num; + else + EHdr->e_shnum = Num; + + uint32_t StrTabIndex = InX::ShStrTab->getParent()->SectionIndex; + if (StrTabIndex >= SHN_LORESERVE) { + SHdrs->sh_link = StrTabIndex; + EHdr->e_shstrndx = SHN_XINDEX; + } else { + EHdr->e_shstrndx = StrTabIndex; + } + for (OutputSection *Sec : OutputSections) Sec->writeHeaderTo<ELFT>(++SHdrs); } @@ -2029,14 +2367,6 @@ template <class ELFT> void Writer<ELFT>::writeTrapInstr() { template <class ELFT> void Writer<ELFT>::writeSections() { uint8_t *Buf = Buffer->getBufferStart(); - // PPC64 needs to process relocations in the .opd section - // before processing relocations in code-containing sections. - if (auto *OpdCmd = findSection(".opd")) { - Out::Opd = OpdCmd; - Out::OpdBuf = Buf + Out::Opd->Offset; - OpdCmd->template writeTo<ELFT>(Buf + Out::Opd->Offset); - } - OutputSection *EhFrameHdr = nullptr; if (InX::EhFrameHdr && !InX::EhFrameHdr->empty()) EhFrameHdr = InX::EhFrameHdr->getParent(); @@ -2049,8 +2379,7 @@ template <class ELFT> void Writer<ELFT>::writeSections() { Sec->writeTo<ELFT>(Buf + Sec->Offset); for (OutputSection *Sec : OutputSections) - if (Sec != Out::Opd && Sec != EhFrameHdr && Sec->Type != SHT_REL && - Sec->Type != SHT_RELA) + if (Sec != EhFrameHdr && Sec->Type != SHT_REL && Sec->Type != SHT_RELA) Sec->writeTo<ELFT>(Buf + Sec->Offset); // The .eh_frame_hdr depends on .eh_frame section contents, therefore diff --git a/contrib/llvm/tools/lld/ELF/Writer.h b/contrib/llvm/tools/lld/ELF/Writer.h index f48f9d1e01b2..7806f824c58f 100644 --- a/contrib/llvm/tools/lld/ELF/Writer.h +++ b/contrib/llvm/tools/lld/ELF/Writer.h @@ -23,7 +23,6 @@ class InputSectionBase; template <class ELFT> class ObjFile; class SymbolTable; template <class ELFT> void writeResult(); -template <class ELFT> void markLive(); // This describes a program header entry. // Each contains type, access flags and range of output sections that will be @@ -45,16 +44,11 @@ struct PhdrEntry { OutputSection *LastSec = nullptr; bool HasLMA = false; - // True if one of the sections in this program header has a LMA specified via - // linker script: AT(addr). We never allow 2 or more sections with LMA in the - // same program header. - bool ASectionHasLMA = false; - uint64_t LMAOffset = 0; }; void addReservedSymbols(); -llvm::StringRef getOutputSectionName(InputSectionBase *S); +llvm::StringRef getOutputSectionName(const InputSectionBase *S); template <class ELFT> uint32_t calcMipsEFlags(); diff --git a/contrib/llvm/tools/lld/LICENSE.TXT b/contrib/llvm/tools/lld/LICENSE.TXT index ec97986c86ba..e05e1845766e 100644 --- a/contrib/llvm/tools/lld/LICENSE.TXT +++ b/contrib/llvm/tools/lld/LICENSE.TXT @@ -4,7 +4,7 @@ lld License University of Illinois/NCSA Open Source License -Copyright (c) 2011-2016 by the contributors listed in CREDITS.TXT +Copyright (c) 2011-2018 by the contributors listed in CREDITS.TXT All rights reserved. Developed by: diff --git a/contrib/llvm/tools/lld/docs/AtomLLD.rst b/contrib/llvm/tools/lld/docs/AtomLLD.rst new file mode 100644 index 000000000000..614e568d1997 --- /dev/null +++ b/contrib/llvm/tools/lld/docs/AtomLLD.rst @@ -0,0 +1,62 @@ +ATOM-based lld +============== + +Note: this document discuss Mach-O port of LLD. For ELF and COFF, +see :doc:`index`. + +ATOM-based lld is a new set of modular code for creating linker tools. +Currently it supports Mach-O. + +* End-User Features: + + * Compatible with existing linker options + * Reads standard Object Files + * Writes standard Executable Files + * Remove clang's reliance on "the system linker" + * Uses the LLVM `"UIUC" BSD-Style license`__. + +* Applications: + + * Modular design + * Support cross linking + * Easy to add new CPU support + * Can be built as static tool or library + +* Design and Implementation: + + * Extensive unit tests + * Internal linker model can be dumped/read to textual format + * Additional linking features can be plugged in as "passes" + * OS specific and CPU specific code factored out + +Why a new linker? +----------------- + +The fact that clang relies on whatever linker tool you happen to have installed +means that clang has been very conservative adopting features which require a +recent linker. + +In the same way that the MC layer of LLVM has removed clang's reliance on the +system assembler tool, the lld project will remove clang's reliance on the +system linker tool. + + +Contents +-------- + +.. toctree:: + :maxdepth: 2 + + design + getting_started + development + open_projects + sphinx_intro + +Indices and tables +------------------ + +* :ref:`genindex` +* :ref:`search` + +__ http://llvm.org/docs/DeveloperPolicy.html#license diff --git a/contrib/llvm/tools/lld/docs/CMakeLists.txt b/contrib/llvm/tools/lld/docs/CMakeLists.txt new file mode 100644 index 000000000000..112ce35e8cf4 --- /dev/null +++ b/contrib/llvm/tools/lld/docs/CMakeLists.txt @@ -0,0 +1,8 @@ +if (LLVM_ENABLE_SPHINX) + include(AddSphinxTarget) + if (SPHINX_FOUND) + if (${SPHINX_OUTPUT_HTML}) + add_sphinx_target(html lld) + endif() + endif() +endif() diff --git a/contrib/llvm/tools/lld/docs/Driver.rst b/contrib/llvm/tools/lld/docs/Driver.rst new file mode 100644 index 000000000000..4ee6ce0c985f --- /dev/null +++ b/contrib/llvm/tools/lld/docs/Driver.rst @@ -0,0 +1,82 @@ +====== +Driver +====== + +Note: this document discuss Mach-O port of LLD. For ELF and COFF, +see :doc:`index`. + +.. contents:: + :local: + +Introduction +============ + +This document describes the lld driver. The purpose of this document is to +describe both the motivation and design goals for the driver, as well as details +of the internal implementation. + +Overview +======== + +The lld driver is designed to support a number of different command line +interfaces. The main interfaces we plan to support are binutils' ld, Apple's +ld, and Microsoft's link.exe. + +Flavors +------- + +Each of these different interfaces is referred to as a flavor. There is also an +extra flavor "core" which is used to exercise the core functionality of the +linker it the test suite. + +* gnu +* darwin +* link +* core + +Selecting a Flavor +^^^^^^^^^^^^^^^^^^ + +There are two different ways to tell lld which flavor to be. They are checked in +order, so the second overrides the first. The first is to symlink :program:`lld` +as :program:`lld-{flavor}` or just :program:`{flavor}`. You can also specify +it as the first command line argument using ``-flavor``:: + + $ lld -flavor gnu + +There is a shortcut for ``-flavor core`` as ``-core``. + + +Adding an Option to an existing Flavor +====================================== + +#. Add the option to the desired :file:`lib/Driver/{flavor}Options.td`. + +#. Add to :cpp:class:`lld::FlavorLinkingContext` a getter and setter method + for the option. + +#. Modify :cpp:func:`lld::FlavorDriver::parse` in :file: + `lib/Driver/{Flavor}Driver.cpp` to call the targetInfo setter + for corresponding to the option. + +#. Modify {Flavor}Reader and {Flavor}Writer to use the new targtInfo option. + + +Adding a Flavor +=============== + +#. Add an entry for the flavor in :file:`include/lld/Common/Driver.h` to + :cpp:class:`lld::UniversalDriver::Flavor`. + +#. Add an entry in :file:`lib/Driver/UniversalDriver.cpp` to + :cpp:func:`lld::Driver::strToFlavor` and + :cpp:func:`lld::UniversalDriver::link`. + This allows the flavor to be selected via symlink and `-flavor`. + +#. Add a tablegen file called :file:`lib/Driver/{flavor}Options.td` that + describes the options. If the options are a superset of another driver, that + driver's td file can simply be included. The :file:`{flavor}Options.td` file + must also be added to :file:`lib/Driver/CMakeLists.txt`. + +#. Add a ``{flavor}Driver`` as a subclass of :cpp:class:`lld::Driver` + in :file:`lib/Driver/{flavor}Driver.cpp`. diff --git a/contrib/llvm/tools/lld/docs/NewLLD.rst b/contrib/llvm/tools/lld/docs/NewLLD.rst new file mode 100644 index 000000000000..afdb41e0a145 --- /dev/null +++ b/contrib/llvm/tools/lld/docs/NewLLD.rst @@ -0,0 +1,309 @@ +The ELF, COFF and Wasm Linkers +============================== + +The ELF Linker as a Library +--------------------------- + +You can embed LLD to your program by linking against it and calling the linker's +entry point function lld::elf::link. + +The current policy is that it is your reponsibility to give trustworthy object +files. The function is guaranteed to return as long as you do not pass corrupted +or malicious object files. A corrupted file could cause a fatal error or SEGV. +That being said, you don't need to worry too much about it if you create object +files in the usual way and give them to the linker. It is naturally expected to +work, or otherwise it's a linker's bug. + +Design +====== + +We will describe the design of the linkers in the rest of the document. + +Key Concepts +------------ + +Linkers are fairly large pieces of software. +There are many design choices you have to make to create a complete linker. + +This is a list of design choices we've made for ELF and COFF LLD. +We believe that these high-level design choices achieved a right balance +between speed, simplicity and extensibility. + +* Implement as native linkers + + We implemented the linkers as native linkers for each file format. + + The linkers share the same design but share very little code. + Sharing code makes sense if the benefit is worth its cost. + In our case, the object formats are different enough that we thought the layer + to abstract the differences wouldn't be worth its complexity and run-time + cost. Elimination of the abstract layer has greatly simplified the + implementation. + +* Speed by design + + One of the most important things in archiving high performance is to + do less rather than do it efficiently. + Therefore, the high-level design matters more than local optimizations. + Since we are trying to create a high-performance linker, + it is very important to keep the design as efficient as possible. + + Broadly speaking, we do not do anything until we have to do it. + For example, we do not read section contents or relocations + until we need them to continue linking. + When we need to do some costly operation (such as looking up + a hash table for each symbol), we do it only once. + We obtain a handler (which is typically just a pointer to actual data) + on the first operation and use it throughout the process. + +* Efficient archive file handling + + LLD's handling of archive files (the files with ".a" file extension) is + different from the traditional Unix linkers and similar to Windows linkers. + We'll describe how the traditional Unix linker handles archive files, what the + problem is, and how LLD approached the problem. + + The traditional Unix linker maintains a set of undefined symbols during + linking. The linker visits each file in the order as they appeared in the + command line until the set becomes empty. What the linker would do depends on + file type. + + - If the linker visits an object file, the linker links object files to the + result, and undefined symbols in the object file are added to the set. + + - If the linker visits an archive file, it checks for the archive file's + symbol table and extracts all object files that have definitions for any + symbols in the set. + + This algorithm sometimes leads to a counter-intuitive behavior. If you give + archive files before object files, nothing will happen because when the linker + visits archives, there is no undefined symbols in the set. As a result, no + files are extracted from the first archive file, and the link is done at that + point because the set is empty after it visits one file. + + You can fix the problem by reordering the files, + but that cannot fix the issue of mutually-dependent archive files. + + Linking mutually-dependent archive files is tricky. You may specify the same + archive file multiple times to let the linker visit it more than once. Or, + you may use the special command line options, `--start-group` and + `--end-group`, to let the linker loop over the files between the options until + no new symbols are added to the set. + + Visiting the same archive files multiple makes the linker slower. + + Here is how LLD approaches the problem. Instead of memorizing only undefined + symbols, we program LLD so that it memorizes all symbols. When it sees an + undefined symbol that can be resolved by extracting an object file from an + archive file it previously visited, it immediately extracts the file and link + it. It is doable because LLD does not forget symbols it have seen in archive + files. + + We believe that the LLD's way is efficient and easy to justify. + + The semantics of LLD's archive handling is different from the traditional + Unix's. You can observe it if you carefully craft archive files to exploit + it. However, in reality, we don't know any program that cannot link with our + algorithm so far, so it's not going to cause trouble. + +Numbers You Want to Know +------------------------ + +To give you intuition about what kinds of data the linker is mainly working on, +I'll give you the list of objects and their numbers LLD has to read and process +in order to link a very large executable. In order to link Chrome with debug +info, which is roughly 2 GB in output size, LLD reads + +- 17,000 files, +- 1,800,000 sections, +- 6,300,000 symbols, and +- 13,000,000 relocations. + +LLD produces the 2 GB executable in 15 seconds. + +These numbers vary depending on your program, but in general, +you have a lot of relocations and symbols for each file. +If your program is written in C++, symbol names are likely to be +pretty long because of name mangling. + +It is important to not waste time on relocations and symbols. + +In the above case, the total amount of symbol strings is 450 MB, +and inserting all of them to a hash table takes 1.5 seconds. +Therefore, if you causally add a hash table lookup for each symbol, +it would slow down the linker by 10%. So, don't do that. + +On the other hand, you don't have to pursue efficiency +when handling files. + +Important Data Structures +------------------------- + +We will describe the key data structures in LLD in this section. The linker can +be understood as the interactions between them. Once you understand their +functions, the code of the linker should look obvious to you. + +* Symbol + + This class represents a symbol. + They are created for symbols in object files or archive files. + The linker creates linker-defined symbols as well. + + There are basically three types of Symbols: Defined, Undefined, or Lazy. + + - Defined symbols are for all symbols that are considered as "resolved", + including real defined symbols, COMDAT symbols, common symbols, + absolute symbols, linker-created symbols, etc. + - Undefined symbols represent undefined symbols, which need to be replaced by + Defined symbols by the resolver until the link is complete. + - Lazy symbols represent symbols we found in archive file headers + which can turn into Defined if we read archieve members. + + There's only one Symbol instance for each unique symbol name. This uniqueness + is guaranteed by the symbol table. As the resolver reads symbols from input + files, it replaces an existing Symbol with the "best" Symbol for its symbol + name using the placement new. + + The above mechanism allows you to use pointers to Symbols as a very cheap way + to access name resolution results. Assume for example that you have a pointer + to an undefined symbol before name resolution. If the symbol is resolved to a + defined symbol by the resolver, the pointer will "automatically" point to the + defined symbol, because the undefined symbol the pointer pointed to will have + been replaced by the defined symbol in-place. + +* SymbolTable + + SymbolTable is basically a hash table from strings to Symbols + with logic to resolve symbol conflicts. It resolves conflicts by symbol type. + + - If we add Defined and Undefined symbols, the symbol table will keep the + former. + - If we add Defined and Lazy symbols, it will keep the former. + - If we add Lazy and Undefined, it will keep the former, + but it will also trigger the Lazy symbol to load the archive member + to actually resolve the symbol. + +* Chunk (COFF specific) + + Chunk represents a chunk of data that will occupy space in an output. + Each regular section becomes a chunk. + Chunks created for common or BSS symbols are not backed by sections. + The linker may create chunks to append additional data to an output as well. + + Chunks know about their size, how to copy their data to mmap'ed outputs, + and how to apply relocations to them. + Specifically, section-based chunks know how to read relocation tables + and how to apply them. + +* InputSection (ELF specific) + + Since we have less synthesized data for ELF, we don't abstract slices of + input files as Chunks for ELF. Instead, we directly use the input section + as an internal data type. + + InputSection knows about their size and how to copy themselves to + mmap'ed outputs, just like COFF Chunks. + +* OutputSection + + OutputSection is a container of InputSections (ELF) or Chunks (COFF). + An InputSection or Chunk belongs to at most one OutputSection. + +There are mainly three actors in this linker. + +* InputFile + + InputFile is a superclass of file readers. + We have a different subclass for each input file type, + such as regular object file, archive file, etc. + They are responsible for creating and owning Symbols and InputSections/Chunks. + +* Writer + + The writer is responsible for writing file headers and InputSections/Chunks to + a file. It creates OutputSections, put all InputSections/Chunks into them, + assign unique, non-overlapping addresses and file offsets to them, and then + write them down to a file. + +* Driver + + The linking process is driven by the driver. The driver: + + - processes command line options, + - creates a symbol table, + - creates an InputFile for each input file and puts all symbols within into + the symbol table, + - checks if there's no remaining undefined symbols, + - creates a writer, + - and passes the symbol table to the writer to write the result to a file. + +Link-Time Optimization +---------------------- + +LTO is implemented by handling LLVM bitcode files as object files. +The linker resolves symbols in bitcode files normally. If all symbols +are successfully resolved, it then runs LLVM passes +with all bitcode files to convert them to one big regular ELF/COFF file. +Finally, the linker replaces bitcode symbols with ELF/COFF symbols, +so that they are linked as if they were in the native format from the beginning. + +The details are described in this document. +http://llvm.org/docs/LinkTimeOptimization.html + +Glossary +-------- + +* RVA (COFF) + + Short for Relative Virtual Address. + + Windows executables or DLLs are not position-independent; they are + linked against a fixed address called an image base. RVAs are + offsets from an image base. + + Default image bases are 0x140000000 for executables and 0x18000000 + for DLLs. For example, when we are creating an executable, we assume + that the executable will be loaded at address 0x140000000 by the + loader, so we apply relocations accordingly. Result texts and data + will contain raw absolute addresses. + +* VA + + Short for Virtual Address. For COFF, it is equivalent to RVA + image base. + +* Base relocations (COFF) + + Relocation information for the loader. If the loader decides to map + an executable or a DLL to a different address than their image + bases, it fixes up binaries using information contained in the base + relocation table. A base relocation table consists of a list of + locations containing addresses. The loader adds a difference between + RVA and actual load address to all locations listed there. + + Note that this run-time relocation mechanism is much simpler than ELF. + There's no PLT or GOT. Images are relocated as a whole just + by shifting entire images in memory by some offsets. Although doing + this breaks text sharing, I think this mechanism is not actually bad + on today's computers. + +* ICF + + Short for Identical COMDAT Folding (COFF) or Identical Code Folding (ELF). + + ICF is an optimization to reduce output size by merging read-only sections + by not only their names but by their contents. If two read-only sections + happen to have the same metadata, actual contents and relocations, + they are merged by ICF. It is known as an effective technique, + and it usually reduces C++ program's size by a few percent or more. + + Note that this is not an entirely sound optimization. C/C++ require + different functions have different addresses. If a program depends on + that property, it would fail at runtime. + + On Windows, that's not really an issue because MSVC link.exe enabled + the optimization by default. As long as your program works + with the linker's default settings, your program should be safe with ICF. + + On Unix, your program is generally not guaranteed to be safe with ICF, + although large programs happen to work correctly. + LLD works fine with ICF for example. diff --git a/contrib/llvm/tools/lld/docs/README.txt b/contrib/llvm/tools/lld/docs/README.txt new file mode 100644 index 000000000000..eb09a2d2b7ea --- /dev/null +++ b/contrib/llvm/tools/lld/docs/README.txt @@ -0,0 +1,12 @@ +lld Documentation +================= + +The lld documentation is written using the Sphinx documentation generator. It is +currently tested with Sphinx 1.1.3. + +We currently use the 'nature' theme and a Beaker inspired structure. + +To rebuild documents into html: + + [/lld/docs]> make html + diff --git a/contrib/llvm/tools/lld/docs/Readers.rst b/contrib/llvm/tools/lld/docs/Readers.rst new file mode 100644 index 000000000000..b69a0b34fabe --- /dev/null +++ b/contrib/llvm/tools/lld/docs/Readers.rst @@ -0,0 +1,174 @@ +.. _Readers: + +Developing lld Readers +====================== + +Note: this document discuss Mach-O port of LLD. For ELF and COFF, +see :doc:`index`. + +Introduction +------------ + +The purpose of a "Reader" is to take an object file in a particular format +and create an `lld::File`:cpp:class: (which is a graph of Atoms) +representing the object file. A Reader inherits from +`lld::Reader`:cpp:class: which lives in +:file:`include/lld/Core/Reader.h` and +:file:`lib/Core/Reader.cpp`. + +The Reader infrastructure for an object format ``Foo`` requires the +following pieces in order to fit into lld: + +:file:`include/lld/ReaderWriter/ReaderFoo.h` + + .. cpp:class:: ReaderOptionsFoo : public ReaderOptions + + This Options class is the only way to configure how the Reader will + parse any file into an `lld::Reader`:cpp:class: object. This class + should be declared in the `lld`:cpp:class: namespace. + + .. cpp:function:: Reader *createReaderFoo(ReaderOptionsFoo &reader) + + This factory function configures and create the Reader. This function + should be declared in the `lld`:cpp:class: namespace. + +:file:`lib/ReaderWriter/Foo/ReaderFoo.cpp` + + .. cpp:class:: ReaderFoo : public Reader + + This is the concrete Reader class which can be called to parse + object files. It should be declared in an anonymous namespace or + if there is shared code with the `lld::WriterFoo`:cpp:class: you + can make a nested namespace (e.g. `lld::foo`:cpp:class:). + +You may have noticed that :cpp:class:`ReaderFoo` is not declared in the +``.h`` file. An important design aspect of lld is that all Readers are +created *only* through an object-format-specific +:cpp:func:`createReaderFoo` factory function. The creation of the Reader is +parametrized through a :cpp:class:`ReaderOptionsFoo` class. This options +class is the one-and-only way to control how the Reader operates when +parsing an input file into an Atom graph. For instance, you may want the +Reader to only accept certain architectures. The options class can be +instantiated from command line options or be programmatically configured. + +Where to start +-------------- + +The lld project already has a skeleton of source code for Readers for +``ELF``, ``PECOFF``, ``MachO``, and lld's native ``YAML`` graph format. +If your file format is a variant of one of those, you should modify the +existing Reader to support your variant. This is done by customizing the Options +class for the Reader and making appropriate changes to the ``.cpp`` file to +interpret those options and act accordingly. + +If your object file format is not a variant of any existing Reader, you'll need +to create a new Reader subclass with the organization described above. + +Readers are factories +--------------------- + +The linker will usually only instantiate your Reader once. That one Reader will +have its loadFile() method called many times with different input files. +To support multithreaded linking, the Reader may be parsing multiple input +files in parallel. Therefore, there should be no parsing state in you Reader +object. Any parsing state should be in ivars of your File subclass or in +some temporary object. + +The key method to implement in a reader is:: + + virtual error_code loadFile(LinkerInput &input, + std::vector<std::unique_ptr<File>> &result); + +It takes a memory buffer (which contains the contents of the object file +being read) and returns an instantiated lld::File object which is +a collection of Atoms. The result is a vector of File pointers (instead of +simple a File pointer) because some file formats allow multiple object +"files" to be encoded in one file system file. + + +Memory Ownership +---------------- + +Atoms are always owned by their File object. During core linking when Atoms +are coalesced or stripped away, core linking does not delete them. +Core linking just removes those unused Atoms from its internal list. +The destructor of a File object is responsible for deleting all Atoms it +owns, and if ownership of the MemoryBuffer was passed to it, the File +destructor needs to delete that too. + +Making Atoms +------------ + +The internal model of lld is purely Atom based. But most object files do not +have an explicit concept of Atoms, instead most have "sections". The way +to think of this is that a section is just a list of Atoms with common +attributes. + +The first step in parsing section-based object files is to cleave each +section into a list of Atoms. The technique may vary by section type. For +code sections (e.g. .text), there are usually symbols at the start of each +function. Those symbol addresses are the points at which the section is +cleaved into discrete Atoms. Some file formats (like ELF) also include the +length of each symbol in the symbol table. Otherwise, the length of each +Atom is calculated to run to the start of the next symbol or the end of the +section. + +Other sections types can be implicitly cleaved. For instance c-string literals +or unwind info (e.g. .eh_frame) can be cleaved by having the Reader look at +the content of the section. It is important to cleave sections into Atoms +to remove false dependencies. For instance the .eh_frame section often +has no symbols, but contains "pointers" to the functions for which it +has unwind info. If the .eh_frame section was not cleaved (but left as one +big Atom), there would always be a reference (from the eh_frame Atom) to +each function. So the linker would be unable to coalesce or dead stripped +away the function atoms. + +The lld Atom model also requires that a reference to an undefined symbol be +modeled as a Reference to an UndefinedAtom. So the Reader also needs to +create an UndefinedAtom for each undefined symbol in the object file. + +Once all Atoms have been created, the second step is to create References +(recall that Atoms are "nodes" and References are "edges"). Most References +are created by looking at the "relocation records" in the object file. If +a function contains a call to "malloc", there is usually a relocation record +specifying the address in the section and the symbol table index. Your +Reader will need to convert the address to an Atom and offset and the symbol +table index into a target Atom. If "malloc" is not defined in the object file, +the target Atom of the Reference will be an UndefinedAtom. + + +Performance +----------- +Once you have the above working to parse an object file into Atoms and +References, you'll want to look at performance. Some techniques that can +help performance are: + +* Use llvm::BumpPtrAllocator or pre-allocate one big vector<Reference> and then + just have each atom point to its subrange of References in that vector. + This can be faster that allocating each Reference as separate object. +* Pre-scan the symbol table and determine how many atoms are in each section + then allocate space for all the Atom objects at once. +* Don't copy symbol names or section content to each Atom, instead use + StringRef and ArrayRef in each Atom to point to its name and content in the + MemoryBuffer. + + +Testing +------- + +We are still working on infrastructure to test Readers. The issue is that +you don't want to check in binary files to the test suite. And the tools +for creating your object file from assembly source may not be available on +every OS. + +We are investigating a way to use YAML to describe the section, symbols, +and content of a file. Then have some code which will write out an object +file from that YAML description. + +Once that is in place, you can write test cases that contain section/symbols +YAML and is run through the linker to produce Atom/References based YAML which +is then run through FileCheck to verify the Atoms and References are as +expected. + + + diff --git a/contrib/llvm/tools/lld/docs/ReleaseNotes.rst b/contrib/llvm/tools/lld/docs/ReleaseNotes.rst new file mode 100644 index 000000000000..7ac1f9ce565b --- /dev/null +++ b/contrib/llvm/tools/lld/docs/ReleaseNotes.rst @@ -0,0 +1,37 @@ +======================= +LLD 7.0.0 Release Notes +======================= + +.. contents:: + :local: + +.. warning:: + These are in-progress notes for the upcoming LLVM 7.0.0 release. + Release notes for previous releases can be found on + `the Download Page <http://releases.llvm.org/download.html>`_. + +Introduction +============ + +This document contains the release notes for the lld linker, release 7.0.0. +Here we describe the status of lld, including major improvements +from the previous release. All lld releases may be downloaded +from the `LLVM releases web site <http://llvm.org/releases/>`_. + +Non-comprehensive list of changes in this release +================================================= + +ELF Improvements +---------------- + +* Item 1. + +COFF Improvements +----------------- + +* Item 1. + +MachO Improvements +------------------ + +* Item 1. diff --git a/contrib/llvm/tools/lld/docs/WebAssembly.rst b/contrib/llvm/tools/lld/docs/WebAssembly.rst new file mode 100644 index 000000000000..264d221970b1 --- /dev/null +++ b/contrib/llvm/tools/lld/docs/WebAssembly.rst @@ -0,0 +1,36 @@ +WebAssembly lld port +==================== + +Note: The WebAssembly port is still a work in progress and is be lacking +certain features. + +The WebAssembly version of lld takes WebAssembly binaries as inputs and produces +a WebAssembly binary as its output. For the most part this port tried to mimic +the behaviour of traditional ELF linkers and specifically the ELF lld port. +Where possible that command line flags and the semantics should be the same. + + +Object file format +------------------ + +The format the input object files that lld expects is specified as part of the +the WebAssembly tool conventions +https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md. + +This is object format that the llvm will produce when run with the +``wasm32-unknown-unknown`` target. To build llvm with WebAssembly support +currently requires enabling the experimental backed using +``-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly``. + + +Missing features +---------------- + +There are several key features that are not yet implement in the WebAssembly +ports: + +- COMDAT support. This means that support for C++ is still very limited. +- Function stripping. Currently there is no support for ``--gc-sections`` so + functions and data from a given object will linked as a unit. +- Section start/end symbols. The synthetic symbols that mark the start and + of data regions are not yet created in the output file. diff --git a/contrib/llvm/tools/lld/docs/_static/favicon.ico b/contrib/llvm/tools/lld/docs/_static/favicon.ico Binary files differnew file mode 100644 index 000000000000..724ad6e12dd4 --- /dev/null +++ b/contrib/llvm/tools/lld/docs/_static/favicon.ico diff --git a/contrib/llvm/tools/lld/docs/_templates/indexsidebar.html b/contrib/llvm/tools/lld/docs/_templates/indexsidebar.html new file mode 100644 index 000000000000..588be9309bde --- /dev/null +++ b/contrib/llvm/tools/lld/docs/_templates/indexsidebar.html @@ -0,0 +1,4 @@ +<h3>Bugs</h3> + +<p>lld bugs should be reported at the + LLVM <a href="https://bugs.llvm.org/">Bugzilla</a>.</p> diff --git a/contrib/llvm/tools/lld/docs/_templates/layout.html b/contrib/llvm/tools/lld/docs/_templates/layout.html new file mode 100644 index 000000000000..519a24bce63a --- /dev/null +++ b/contrib/llvm/tools/lld/docs/_templates/layout.html @@ -0,0 +1,12 @@ +{% extends "!layout.html" %} + +{% block extrahead %} +<style type="text/css"> + table.right { float: right; margin-left: 20px; } + table.right td { border: 1px solid #ccc; } +</style> +{% endblock %} + +{% block rootrellink %} + <li><a href="{{ pathto('index') }}">lld Home</a> | </li> +{% endblock %} diff --git a/contrib/llvm/tools/lld/docs/conf.py b/contrib/llvm/tools/lld/docs/conf.py new file mode 100644 index 000000000000..3598fbf50f0d --- /dev/null +++ b/contrib/llvm/tools/lld/docs/conf.py @@ -0,0 +1,255 @@ +# -*- coding: utf-8 -*- +# +# lld documentation build configuration file. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os +from datetime import date + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.intersphinx', 'sphinx.ext.todo'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'lld' +copyright = u'2011-%d, LLVM Project' % date.today().year + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short version. +version = '7' +# The full version, including alpha/beta/rc tags. +release = '7' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +today_fmt = '%Y-%m-%d' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +show_authors = True + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'friendly' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'llvm-theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +html_theme_path = ["."] + +# The name for this set of Sphinx documents. If None, it defaults to +# "<project> v<release> documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# If given, this must be the name of an image file (path relative to the +# configuration directory) that is the favicon of the docs. Modern browsers use +# this as icon for tabs, windows and bookmarks. It should be a Windows-style +# icon file (.ico), which is 16x16 or 32x32 pixels large. Default: None. The +# image file will be copied to the _static directory of the output HTML, but +# only if the file does not already exist there. +html_favicon = '_static/favicon.ico' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +html_last_updated_fmt = '%Y-%m-%d' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +html_sidebars = {'index': 'indexsidebar.html'} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# html_additional_pages = {'index': 'index.html'} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a <link> tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'llddoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('contents', 'lld.tex', u'lld Documentation', + u'LLVM project', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('contents', 'lld', u'lld Documentation', + [u'LLVM project'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('contents', 'lld', u'lld Documentation', + u'LLVM project', 'lld', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + + +# FIXME: Define intersphinx configration. +intersphinx_mapping = {} + + +# -- Options for extensions ---------------------------------------------------- + +# Enable this if you want TODOs to show up in the generated documentation. +todo_include_todos = True diff --git a/contrib/llvm/tools/lld/docs/design.rst b/contrib/llvm/tools/lld/docs/design.rst new file mode 100644 index 000000000000..1e111f979bb5 --- /dev/null +++ b/contrib/llvm/tools/lld/docs/design.rst @@ -0,0 +1,421 @@ +.. _design: + +Linker Design +============= + +Note: this document discuss Mach-O port of LLD. For ELF and COFF, +see :doc:`index`. + +Introduction +------------ + +lld is a new generation of linker. It is not "section" based like traditional +linkers which mostly just interlace sections from multiple object files into the +output file. Instead, lld is based on "Atoms". Traditional section based +linking work well for simple linking, but their model makes advanced linking +features difficult to implement. Features like dead code stripping, reordering +functions for locality, and C++ coalescing require the linker to work at a finer +grain. + +An atom is an indivisible chunk of code or data. An atom has a set of +attributes, such as: name, scope, content-type, alignment, etc. An atom also +has a list of References. A Reference contains: a kind, an optional offset, an +optional addend, and an optional target atom. + +The Atom model allows the linker to use standard graph theory models for linking +data structures. Each atom is a node, and each Reference is an edge. The +feature of dead code stripping is implemented by following edges to mark all +live atoms, and then delete the non-live atoms. + + +Atom Model +---------- + +An atom is an indivisible chunk of code or data. Typically each user written +function or global variable is an atom. In addition, the compiler may emit +other atoms, such as for literal c-strings or floating point constants, or for +runtime data structures like dwarf unwind info or pointers to initializers. + +A simple "hello world" object file would be modeled like this: + +.. image:: hello.png + +There are three atoms: main, a proxy for printf, and an anonymous atom +containing the c-string literal "hello world". The Atom "main" has two +references. One is the call site for the call to printf, and the other is a +reference for the instruction that loads the address of the c-string literal. + +There are only four different types of atoms: + + * DefinedAtom + 95% of all atoms. This is a chunk of code or data + + * UndefinedAtom + This is a place holder in object files for a reference to some atom + outside the translation unit.During core linking it is usually replaced + by (coalesced into) another Atom. + + * SharedLibraryAtom + If a required symbol name turns out to be defined in a dynamic shared + library (and not some object file). A SharedLibraryAtom is the + placeholder Atom used to represent that fact. + + It is similar to an UndefinedAtom, but it also tracks information + about the associated shared library. + + * AbsoluteAtom + This is for embedded support where some stuff is implemented in ROM at + some fixed address. This atom has no content. It is just an address + that the Writer needs to fix up any references to point to. + + +File Model +---------- + +The linker views the input files as basically containers of Atoms and +References, and just a few attributes of their own. The linker works with three +kinds of files: object files, static libraries, and dynamic shared libraries. +Each kind of file has reader object which presents the file in the model +expected by the linker. + +Object File +~~~~~~~~~~~ + +An object file is just a container of atoms. When linking an object file, a +reader is instantiated which parses the object file and instantiates a set of +atoms representing all content in the .o file. The linker adds all those atoms +to a master graph. + +Static Library (Archive) +~~~~~~~~~~~~~~~~~~~~~~~~ + +This is the traditional unix static archive which is just a collection of object +files with a "table of contents". When linking with a static library, by default +nothing is added to the master graph of atoms. Instead, if after merging all +atoms from object files into a master graph, if any "undefined" atoms are left +remaining in the master graph, the linker reads the table of contents for each +static library to see if any have the needed definitions. If so, the set of +atoms from the specified object file in the static library is added to the +master graph of atoms. + +Dynamic Library (Shared Object) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Dynamic libraries are different than object files and static libraries in that +they don't directly add any content. Their purpose is to check at build time +that the remaining undefined references can be resolved at runtime, and provide +a list of dynamic libraries (SO_NEEDED) that will be needed at runtime. The way +this is modeled in the linker is that a dynamic library contributes no atoms to +the initial graph of atoms. Instead, (like static libraries) if there are +"undefined" atoms in the master graph of all atoms, then each dynamic library is +checked to see if exports the required symbol. If so, a "shared library" atom is +instantiated by the by the reader which the linker uses to replace the +"undefined" atom. + +Linking Steps +------------- + +Through the use of abstract Atoms, the core of linking is architecture +independent and file format independent. All command line parsing is factored +out into a separate "options" abstraction which enables the linker to be driven +with different command line sets. + +The overall steps in linking are: + + #. Command line processing + + #. Parsing input files + + #. Resolving + + #. Passes/Optimizations + + #. Generate output file + +The Resolving and Passes steps are done purely on the master graph of atoms, so +they have no notion of file formats such as mach-o or ELF. + + +Input Files +~~~~~~~~~~~ + +Existing developer tools using different file formats for object files. +A goal of lld is to be file format independent. This is done +through a plug-in model for reading object files. The lld::Reader is the base +class for all object file readers. A Reader follows the factory method pattern. +A Reader instantiates an lld::File object (which is a graph of Atoms) from a +given object file (on disk or in-memory). + +Every Reader subclass defines its own "options" class (for instance the mach-o +Reader defines the class ReaderOptionsMachO). This options class is the +one-and-only way to control how the Reader operates when parsing an input file +into an Atom graph. For instance, you may want the Reader to only accept +certain architectures. The options class can be instantiated from command +line options, or it can be subclassed and the ivars programmatically set. + +Resolving +~~~~~~~~~ + +The resolving step takes all the atoms' graphs from each object file and +combines them into one master object graph. Unfortunately, it is not as simple +as appending the atom list from each file into one big list. There are many +cases where atoms need to be coalesced. That is, two or more atoms need to be +coalesced into one atom. This is necessary to support: C language "tentative +definitions", C++ weak symbols for templates and inlines defined in headers, +replacing undefined atoms with actual definition atoms, and for merging copies +of constants like c-strings and floating point constants. + +The linker support coalescing by-name and by-content. By-name is used for +tentative definitions and weak symbols. By-content is used for constant data +that can be merged. + +The resolving process maintains some global linking "state", including a "symbol +table" which is a map from llvm::StringRef to lld::Atom*. With these data +structures, the linker iterates all atoms in all input files. For each atom, it +checks if the atom is named and has a global or hidden scope. If so, the atom +is added to the symbol table map. If there already is a matching atom in that +table, that means the current atom needs to be coalesced with the found atom, or +it is a multiple definition error. + +When all initial input file atoms have been processed by the resolver, a scan is +made to see if there are any undefined atoms in the graph. If there are, the +linker scans all libraries (both static and dynamic) looking for definitions to +replace the undefined atoms. It is an error if any undefined atoms are left +remaining. + +Dead code stripping (if requested) is done at the end of resolving. The linker +does a simple mark-and-sweep. It starts with "root" atoms (like "main" in a main +executable) and follows each references and marks each Atom that it visits as +"live". When done, all atoms not marked "live" are removed. + +The result of the Resolving phase is the creation of an lld::File object. The +goal is that the lld::File model is **the** internal representation +throughout the linker. The file readers parse (mach-o, ELF, COFF) into an +lld::File. The file writers (mach-o, ELF, COFF) taken an lld::File and produce +their file kind, and every Pass only operates on an lld::File. This is not only +a simpler, consistent model, but it enables the state of the linker to be dumped +at any point in the link for testing purposes. + + +Passes +~~~~~~ + +The Passes step is an open ended set of routines that each get a change to +modify or enhance the current lld::File object. Some example Passes are: + + * stub (PLT) generation + + * GOT instantiation + + * order_file optimization + + * branch island generation + + * branch shim generation + + * Objective-C optimizations (Darwin specific) + + * TLV instantiation (Darwin specific) + + * DTrace probe processing (Darwin specific) + + * compact unwind encoding (Darwin specific) + + +Some of these passes are specific to Darwin's runtime environments. But many of +the passes are applicable to any OS (such as generating branch island for out of +range branch instructions). + +The general structure of a pass is to iterate through the atoms in the current +lld::File object, inspecting each atom and doing something. For instance, the +stub pass, looks for call sites to shared library atoms (e.g. call to printf). +It then instantiates a "stub" atom (PLT entry) and a "lazy pointer" atom for +each proxy atom needed, and these new atoms are added to the current lld::File +object. Next, all the noted call sites to shared library atoms have their +References altered to point to the stub atom instead of the shared library atom. + + +Generate Output File +~~~~~~~~~~~~~~~~~~~~ + +Once the passes are done, the output file writer is given current lld::File +object. The writer's job is to create the executable content file wrapper and +place the content of the atoms into it. + +lld uses a plug-in model for writing output files. All concrete writers (e.g. +ELF, mach-o, etc) are subclasses of the lld::Writer class. + +Unlike the Reader class which has just one method to instantiate an lld::File, +the Writer class has multiple methods. The crucial method is to generate the +output file, but there are also methods which allow the Writer to contribute +Atoms to the resolver and specify passes to run. + +An example of contributing +atoms is that if the Writer knows a main executable is being linked and such +an executable requires a specially named entry point (e.g. "_main"), the Writer +can add an UndefinedAtom with that special name to the resolver. This will +cause the resolver to issue an error if that symbol is not defined. + +Sometimes a Writer supports lazily created symbols, such as names for the start +of sections. To support this, the Writer can create a File object which vends +no initial atoms, but does lazily supply atoms by name as needed. + +Every Writer subclass defines its own "options" class (for instance the mach-o +Writer defines the class WriterOptionsMachO). This options class is the +one-and-only way to control how the Writer operates when producing an output +file from an Atom graph. For instance, you may want the Writer to optimize +the output for certain OS versions, or strip local symbols, etc. The options +class can be instantiated from command line options, or it can be subclassed +and the ivars programmatically set. + + +lld::File representations +------------------------- + +Just as LLVM has three representations of its IR model, lld has two +representations of its File/Atom/Reference model: + + * In memory, abstract C++ classes (lld::Atom, lld::Reference, and lld::File). + + * textual (in YAML) + + +Textual representations in YAML +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In designing a textual format we want something easy for humans to read and easy +for the linker to parse. Since an atom has lots of attributes most of which are +usually just the default, we should define default values for every attribute so +that those can be omitted from the text representation. Here is the atoms for a +simple hello world program expressed in YAML:: + + target-triple: x86_64-apple-darwin11 + + atoms: + - name: _main + scope: global + type: code + content: [ 55, 48, 89, e5, 48, 8d, 3d, 00, 00, 00, 00, 30, c0, e8, 00, 00, + 00, 00, 31, c0, 5d, c3 ] + fixups: + - offset: 07 + kind: pcrel32 + target: 2 + - offset: 0E + kind: call32 + target: _fprintf + + - type: c-string + content: [ 73, 5A, 00 ] + + ... + +The biggest use for the textual format will be writing test cases. Writing test +cases in C is problematic because the compiler may vary its output over time for +its own optimization reasons which my inadvertently disable or break the linker +feature trying to be tested. By writing test cases in the linkers own textual +format, we can exactly specify every attribute of every atom and thus target +specific linker logic. + +The textual/YAML format follows the ReaderWriter patterns used in lld. The lld +library comes with the classes: ReaderYAML and WriterYAML. + + +Testing +------- + +The lld project contains a test suite which is being built up as new code is +added to lld. All new lld functionality should have a tests added to the test +suite. The test suite is `lit <http://llvm.org/cmds/lit.html/>`_ driven. Each +test is a text file with comments telling lit how to run the test and check the +result To facilitate testing, the lld project builds a tool called lld-core. +This tool reads a YAML file (default from stdin), parses it into one or more +lld::File objects in memory and then feeds those lld::File objects to the +resolver phase. + + +Resolver testing +~~~~~~~~~~~~~~~~ + +Basic testing is the "core linking" or resolving phase. That is where the +linker merges object files. All test cases are written in YAML. One feature of +YAML is that it allows multiple "documents" to be encoding in one YAML stream. +That means one text file can appear to the linker as multiple .o files - the +normal case for the linker. + +Here is a simple example of a core linking test case. It checks that an +undefined atom from one file will be replaced by a definition from another +file:: + + # RUN: lld-core %s | FileCheck %s + + # + # Test that undefined atoms are replaced with defined atoms. + # + + --- + atoms: + - name: foo + definition: undefined + --- + atoms: + - name: foo + scope: global + type: code + ... + + # CHECK: name: foo + # CHECK: scope: global + # CHECK: type: code + # CHECK-NOT: name: foo + # CHECK: ... + + +Passes testing +~~~~~~~~~~~~~~ + +Since Passes just operate on an lld::File object, the lld-core tool has the +option to run a particular pass (after resolving). Thus, you can write a YAML +test case with carefully crafted input to exercise areas of a Pass and the check +the resulting lld::File object as represented in YAML. + + +Design Issues +------------- + +There are a number of open issues in the design of lld. The plan is to wait and +make these design decisions when we need to. + + +Debug Info +~~~~~~~~~~ + +Currently, the lld model says nothing about debug info. But the most popular +debug format is DWARF and there is some impedance mismatch with the lld model +and DWARF. In lld there are just Atoms and only Atoms that need to be in a +special section at runtime have an associated section. Also, Atoms do not have +addresses. The way DWARF is spec'ed different parts of DWARF are supposed to go +into specially named sections and the DWARF references function code by address. + +CPU and OS specific functionality +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Currently, lld has an abstract "Platform" that deals with any CPU or OS specific +differences in linking. We just keep adding virtual methods to the base +Platform class as we find linking areas that might need customization. At some +point we'll need to structure this better. + + +File Attributes +~~~~~~~~~~~~~~~ + +Currently, lld::File just has a path and a way to iterate its atoms. We will +need to add more attributes on a File. For example, some equivalent to the +target triple. There is also a number of cached or computed attributes that +could make various Passes more efficient. For instance, on Darwin there are a +number of Objective-C optimizations that can be done by a Pass. But it would +improve the plain C case if the Objective-C optimization Pass did not have to +scan all atoms looking for any Objective-C data structures. This could be done +if the lld::File object had an attribute that said if the file had any +Objective-C data in it. The Resolving phase would then be required to "merge" +that attribute as object files are added. diff --git a/contrib/llvm/tools/lld/docs/development.rst b/contrib/llvm/tools/lld/docs/development.rst new file mode 100644 index 000000000000..ce91341d665f --- /dev/null +++ b/contrib/llvm/tools/lld/docs/development.rst @@ -0,0 +1,45 @@ +.. _development: + +Development +=========== + +Note: this document discuss Mach-O port of LLD. For ELF and COFF, +see :doc:`index`. + +lld is developed as part of the `LLVM <http://llvm.org>`_ project. + +Creating a Reader +----------------- + +See the :ref:`Creating a Reader <Readers>` guide. + + +Modifying the Driver +-------------------- + +See :doc:`Driver`. + + +Debugging +--------- + +You can run lld with ``-mllvm -debug`` command line options to enable debugging +printouts. If you want to enable debug information for some specific pass, you +can run it with ``-mllvm '-debug-only=<pass>'``, where pass is a name used in +the ``DEBUG_WITH_TYPE()`` macro. + + + +Documentation +------------- + +The project documentation is written in reStructuredText and generated using the +`Sphinx <http://sphinx.pocoo.org/>`_ documentation generator. For more +information on writing documentation for the project, see the +:ref:`sphinx_intro`. + +.. toctree:: + :hidden: + + Readers + Driver diff --git a/contrib/llvm/tools/lld/docs/getting_started.rst b/contrib/llvm/tools/lld/docs/getting_started.rst new file mode 100644 index 000000000000..97c3d1bccbda --- /dev/null +++ b/contrib/llvm/tools/lld/docs/getting_started.rst @@ -0,0 +1,106 @@ +.. _getting_started: + +Getting Started: Building and Running lld +========================================= + +This page gives you the shortest path to checking out and building lld. If you +run into problems, please file bugs in the `LLVM Bugzilla`__ + +__ http://llvm.org/bugs/ + +Building lld +------------ + +On Unix-like Systems +~~~~~~~~~~~~~~~~~~~~ + +1. Get the required tools. + + * `CMake 2.8`_\+. + * make (or any build system CMake supports). + * `Clang 3.1`_\+ or GCC 4.7+ (C++11 support is required). + + * If using Clang, you will also need `libc++`_. + * `Python 2.4`_\+ (not 3.x) for running tests. + +.. _CMake 2.8: http://www.cmake.org/cmake/resources/software.html +.. _Clang 3.1: http://clang.llvm.org/ +.. _libc++: http://libcxx.llvm.org/ +.. _Python 2.4: http://python.org/download/ + +2. Check out LLVM:: + + $ cd path/to/llvm-project + $ svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm + +3. Check out lld:: + + $ cd llvm/tools + $ svn co http://llvm.org/svn/llvm-project/lld/trunk lld + + * lld can also be checked out to ``path/to/llvm-project`` and built as an external + project. + +4. Build LLVM and lld:: + + $ cd path/to/llvm-build/llvm (out of source build required) + $ cmake -G "Unix Makefiles" path/to/llvm-project/llvm + $ make + + * If you want to build with clang and it is not the default compiler or + it is installed in an alternate location, you'll need to tell the cmake tool + the location of the C and C++ compiler via CMAKE_C_COMPILER and + CMAKE_CXX_COMPILER. For example:: + + $ cmake -DCMAKE_CXX_COMPILER=/path/to/clang++ -DCMAKE_C_COMPILER=/path/to/clang ... + +5. Test:: + + $ make check-lld + +Using Visual Studio +~~~~~~~~~~~~~~~~~~~ + +#. Get the required tools. + + * `CMake 2.8`_\+. + * `Visual Studio 12 (2013) or later`_ (required for C++11 support) + * `Python 2.4`_\+ (not 3.x) for running tests. + +.. _CMake 2.8: http://www.cmake.org/cmake/resources/software.html +.. _Visual Studio 12 (2013) or later: http://www.microsoft.com/visualstudio/11/en-us +.. _Python 2.4: http://python.org/download/ + +#. Check out LLVM:: + + $ cd path/to/llvm-project + $ svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm + +#. Check out lld:: + + $ cd llvm/tools + $ svn co http://llvm.org/svn/llvm-project/lld/trunk lld + + * lld can also be checked out to ``path/to/llvm-project`` and built as an external + project. + +#. Generate Visual Studio project files:: + + $ cd path/to/llvm-build/llvm (out of source build required) + $ cmake -G "Visual Studio 11" path/to/llvm-project/llvm + +#. Build + + * Open LLVM.sln in Visual Studio. + * Build the ``ALL_BUILD`` target. + +#. Test + + * Build the ``lld-test`` target. + +More Information +~~~~~~~~~~~~~~~~ + +For more information on using CMake see the `LLVM CMake guide`_. + +.. _LLVM CMake guide: http://llvm.org/docs/CMake.html diff --git a/contrib/llvm/tools/lld/docs/hello.png b/contrib/llvm/tools/lld/docs/hello.png Binary files differnew file mode 100644 index 000000000000..70df111f1abd --- /dev/null +++ b/contrib/llvm/tools/lld/docs/hello.png diff --git a/contrib/llvm/tools/lld/docs/index.rst b/contrib/llvm/tools/lld/docs/index.rst new file mode 100644 index 000000000000..2821ce4d214e --- /dev/null +++ b/contrib/llvm/tools/lld/docs/index.rst @@ -0,0 +1,178 @@ +LLD - The LLVM Linker +===================== + +LLD is a linker from the LLVM project. That is a drop-in replacement +for system linkers and runs much faster than them. It also provides +features that are useful for toolchain developers. + +The linker supports ELF (Unix), PE/COFF (Windows), Mach-O (macOS) and +WebAssembly in descending order of completeness. Internally, LLD consists of +several different linkers. The ELF port is the one that will be described in +this document. The PE/COFF port is complete, including +Windows debug info (PDB) support. The WebAssembly port is still a work in +progress (See :doc:`WebAssembly`). The Mach-O port is built based on a +different architecture than the others. For the details about Mach-O, please +read :doc:`AtomLLD`. + +Features +-------- + +- LLD is a drop-in replacement for the GNU linkers. That accepts the + same command line arguments and linker scripts as GNU. + + We are currently working closely with the FreeBSD project to make + LLD default system linker in future versions of the operating + system, so we are serious about addressing compatibility issues. As + of February 2017, LLD is able to link the entire FreeBSD/amd64 base + system including the kernel. With a few work-in-progress patches it + can link approximately 95% of the ports collection on AMD64. For the + details, see `FreeBSD quarterly status report + <https://www.freebsd.org/news/status/report-2016-10-2016-12.html#Using-LLVM%27s-LLD-Linker-as-FreeBSD%27s-System-Linker>`_. + +- LLD is very fast. When you link a large program on a multicore + machine, you can expect that LLD runs more than twice as fast as GNU + gold linker. Your milage may vary, though. + +- It supports various CPUs/ABIs including x86-64, x86, x32, AArch64, + ARM, MIPS 32/64 big/little-endian, PowerPC, PowerPC 64 and AMDGPU. + Among these, x86-64 is the most well-supported target and have + reached production quality. AArch64 and MIPS seem decent too. x86 + should be OK but not well tested yet. ARM support is being developed + actively. + +- It is always a cross-linker, meaning that it always supports all the + above targets however it was built. In fact, we don't provide a + build-time option to enable/disable each target. This should make it + easy to use our linker as part of a cross-compile toolchain. + +- You can embed LLD to your program to eliminate dependency to + external linkers. All you have to do is to construct object files + and command line arguments just like you would do to invoke an + external linker and then call the linker's main function, + ``lld::elf::link``, from your code. + +- It is small. We are using LLVM libObject library to read from object + files, so it is not completely a fair comparison, but as of February + 2017, LLD/ELF consists only of 21k lines of C++ code while GNU gold + consists of 198k lines of C++ code. + +- Link-time optimization (LTO) is supported by default. Essentially, + all you have to do to do LTO is to pass the ``-flto`` option to clang. + Then clang creates object files not in the native object file format + but in LLVM bitcode format. LLD reads bitcode object files, compile + them using LLVM and emit an output file. Because in this way LLD can + see the entire program, it can do the whole program optimization. + +- Some very old features for ancient Unix systems (pre-90s or even + before that) have been removed. Some default settings have been + tuned for the 21st century. For example, the stack is marked as + non-executable by default to tighten security. + +Performance +----------- + +This is a link time comparison on a 2-socket 20-core 40-thread Xeon +E5-2680 2.80 GHz machine with an SSD drive. We ran gold and lld with +or without multi-threading support. To disable multi-threading, we +added ``-no-threads`` to the command lines. + +============ =========== ============ ==================== ================== =============== ============= +Program Output size GNU ld GNU gold w/o threads GNU gold w/threads lld w/o threads lld w/threads +ffmpeg dbg 92 MiB 1.72s 1.16s 1.01s 0.60s 0.35s +mysqld dbg 154 MiB 8.50s 2.96s 2.68s 1.06s 0.68s +clang dbg 1.67 GiB 104.03s 34.18s 23.49s 14.82s 5.28s +chromium dbg 1.14 GiB 209.05s [1]_ 64.70s 60.82s 27.60s 16.70s +============ =========== ============ ==================== ================== =============== ============= + +As you can see, lld is significantly faster than GNU linkers. +Note that this is just a benchmark result of our environment. +Depending on number of available cores, available amount of memory or +disk latency/throughput, your results may vary. + +.. [1] Since GNU ld doesn't support the ``-icf=all`` and + ``-gdb-index`` options, we removed them from the command line + for GNU ld. GNU ld would have been slower than this if it had + these options. + +Build +----- + +If you have already checked out LLVM using SVN, you can check out LLD +under ``tools`` directory just like you probably did for clang. For the +details, see `Getting Started with the LLVM System +<http://llvm.org/docs/GettingStarted.html>`_. + +If you haven't checkout out LLVM, the easiest way to build LLD is to +checkout the entire LLVM projects/sub-projects from a git mirror and +build that tree. You need `cmake` and of course a C++ compiler. + +.. code-block:: console + + $ git clone https://github.com/llvm-project/llvm-project-20170507 llvm-project + $ mkdir build + $ cd build + $ cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS=lld -DCMAKE_INSTALL_PREFIX=/usr/local ../llvm-project/llvm + $ make install + +Using LLD +--------- + +LLD is installed as ``ld.lld``. On Unix, linkers are invoked by +compiler drivers, so you are not expected to use that command +directly. There are a few ways to tell compiler drivers to use ld.lld +instead of the default linker. + +The easiest way to do that is to overwrite the default linker. After +installing LLD to somewhere on your disk, you can create a symbolic +link by doing ``ln -s /path/to/ld.lld /usr/bin/ld`` so that +``/usr/bin/ld`` is resolved to LLD. + +If you don't want to change the system setting, you can use clang's +``-fuse-ld`` option. In this way, you want to set ``-fuse-ld=lld`` to +LDFLAGS when building your programs. + +LLD leaves its name and version number to a ``.comment`` section in an +output. If you are in doubt whether you are successfully using LLD or +not, run ``readelf --string-dump .comment <output-file>`` and examine the +output. If the string "Linker: LLD" is included in the output, you are +using LLD. + +History +------- + +Here is a brief project history of the ELF and COFF ports. + +- May 2015: We decided to rewrite the COFF linker and did that. + Noticed that the new linker is much faster than the MSVC linker. + +- July 2015: The new ELF port was developed based on the COFF linker + architecture. + +- September 2015: The first patches to support MIPS and AArch64 landed. + +- October 2015: Succeeded to self-host the ELF port. We have noticed + that the linker was faster than the GNU linkers, but we weren't sure + at the time if we would be able to keep the gap as we would add more + features to the linker. + +- July 2016: Started working on improving the linker script support. + +- December 2016: Succeeded to build the entire FreeBSD base system + including the kernel. We had widen the performance gap against the + GNU linkers. + +Internals +--------- + +For the internals of the linker, please read :doc:`NewLLD`. It is a bit +outdated but the fundamental concepts remain valid. We'll update the +document soon. + +.. toctree:: + :maxdepth: 1 + + NewLLD + AtomLLD + WebAssembly + windows_support + ReleaseNotes diff --git a/contrib/llvm/tools/lld/docs/ld.lld.1 b/contrib/llvm/tools/lld/docs/ld.lld.1 new file mode 100644 index 000000000000..92d890f8cadb --- /dev/null +++ b/contrib/llvm/tools/lld/docs/ld.lld.1 @@ -0,0 +1,570 @@ +.\" This file is distributed under the University of Illinois Open Source +.\" License. See LICENSE.TXT for details. +.\" +.\" This man page documents only lld's ELF linking support, obtained originally +.\" from FreeBSD. +.Dd July 30, 2018 +.Dt LD.LLD 1 +.Os +.Sh NAME +.Nm ld.lld +.Nd ELF linker from the LLVM project +.Sh SYNOPSIS +.Nm ld.lld +.Op Ar options +.Ar objfile ... +.Sh DESCRIPTION +A linker takes one or more object, archive, and library files, and combines +them into an output file (an executable, a shared library, or another object +file). +It relocates code and data from the input files and resolves symbol +references between them. +.Pp +.Nm +is a drop-in replacement for the GNU BFD and gold linkers. +It accepts most of the same command line arguments and linker scripts +as GNU linkers. +.Pp +Many options have both a single-letter and long form. +When using the long form options other than those beginning with the +letter +.Cm o +may be specified using either one or two dashes preceding the option name. +Long options beginning with +.Cm o +require two dashes to avoid confusion with the +.Fl o Ar path +option. +.Pp +These options are available: +.Bl -tag -width indent +.It Fl -allow-multiple-definition +Do not error if a symbol is defined multiple times. +The first definition will be used. +.It Fl -apply-dynamic-relocs +Apply link-time values for dynamic relocations. +.It Fl -as-needed +Only set +.Dv DT_NEEDED +for shared libraries if used. +.It Fl -auxiliary Ns = Ns Ar value +Set the +.Dv DT_AUXILIARY +field to the specified name. +.It Fl -Bdynamic , Fl -dy +Link against shared libraries. +.It Fl -Bstatic , Fl -static , Fl -dn +Do not link against shared libraries. +.It Fl -Bsymbolic +Bind defined symbols locally. +.It Fl -Bsymbolic-functions +Bind defined function symbols locally. +.It Fl -build-id Ns = Ns Ar value +Generate a build ID note. +.Ar value +may be one of +.Cm fast , +.Cm md5 , +.Cm sha1 , +.Cm tree , +.Cm uuid , +.Cm 0x Ns Ar hex-string , +and +.Cm none . +.Cm tree +is an alias for +.Cm sha1 . +Build-IDs of type +.Cm fast , +.Cm md5 , +.Cm sha1 , +and +.Cm tree +are calculated from the object contents. +.Cm fast +is not intended to be cryptographically secure. +.It Fl -build-id +Synonym for +.Fl -build-id Ns = Ns Cm fast . +.It Fl -color-diagnostics Ns = Ns Ar value +Use colors in diagnostics. +.Ar value +may be one of +.Cm always , +.Cm auto , +and +.Cm never . +.Cm auto +enables color if and only if output is to a terminal. +.It Fl -color-diagnostics +Alias for +.Fl -color-diagnostics Ns = Ns Cm auto . +.It Fl -compress-debug-sections Ns = Ns Ar value +Compress DWARF debug sections. +.Ar value +may be +.Cm none +or +.Cm zlib . +.It Fl -cref +Output cross reference table. +.It Fl -define-common , Fl d +Assign space to common symbols. +.It Fl -defsym Ns = Ns Ar symbol Ns = Ns Ar expression +Define a symbol alias. +.Ar expression +may be another symbol or a linker script expression. +For example, +.Ql --defsym=foo=bar +or +.Ql --defsym=foo=bar+0x100 . +.It Fl -demangle +Demangle symbol names. +.It Fl -disable-new-dtags +Disable new dynamic tags. +.It Fl -discard-all , Fl x +Delete all local symbols. +.It Fl -discard-locals , Fl X +Delete temporary local symbols. +.It Fl -discard-none +Keep all symbols in the symbol table. +.It Fl -dynamic-linker Ns = Ns Ar value +Specify the dynamic linker to be used for a dynamically linked executable. +This is recorded in an ELF segment of type +.Dv PT_INTERP . +.It Fl -dynamic-list Ns = Ns Ar file +Read a list of dynamic symbols from +.Ar file . +.It Fl -eh-frame-hdr +Request creation of +.Li .eh_frame_hdr +section and +.Dv PT_GNU_EH_FRAME +segment header. +.It Fl -emit-relocs , Fl q +Generate relocations in the output. +.It Fl -enable-new-dtags +Enable new dynamic tags. +.It Fl -end-lib +End a grouping of objects that should be treated as if they were together +in an archive. +.It Fl -entry Ns = Ns Ar entry +Name of entry point symbol. +.It Fl -error-limit Ns = Ns Ar value +Maximum number of errors to emit before stopping. +A value of zero indicates that there is no limit. +.It Fl -error-unresolved-symbols +Report unresolved symbols as errors. +.It Fl -exclude-libs Ns = Ns Ar value +Exclude static libraries from automatic export. +.It Fl -export-dynamic , Fl E +Put symbols in the dynamic symbol table. +.It Fl -export-dynamic-symbol Ns = Ns Ar symbol +Include +.Ar symbol +in the dynamic symbol table. +.It Fl -fatal-warnings +Treat warnings as errors. +.It Fl -filter Ns = Ns Ar value , Fl F Ar value +Set the +.Dv DT_FILTER +field to the specified value. +.It Fl -fini Ns = Ns Ar symbol +Specify a finalizer function. +.It Fl -format Ns = Ns Ar input-format , Fl b Ar input-format +Specify the format of the inputs following this option. +.Ar input-format +may be one of +.Cm binary , +.Cm elf , +and +.Cm default . +.Cm default +is a synonym for +.Cm elf . +.It Fl -gc-sections +Enable garbage collection of unused sections. +.It Fl -gdb-index +Generate +.Li .gdb_index +section. +.It Fl -hash-style Ns = Ns Ar value +Specify hash style. +.Ar value +may be +.Cm sysv , +.Cm gnu , +or +.Cm both . +.Cm both +is the default. +.It Fl -help +Print a help message. +.It Fl -icf Ns = Ns Cm all +Enable identical code folding. +.It Fl -icf Ns = Ns Cm safe +Enable safe identical code folding. +.It Fl -icf Ns = Ns Cm none +Disable identical code folding. +.It Fl -image-base Ns = Ns Ar value +Set the base address to +.Ar value . +.It Fl -init Ns = Ns Ar symbol +Specify an initializer function. +.It Fl -keep-unique Ns = Ns Ar symbol +Do not fold +.Ar symbol +during ICF. +.It Fl l Ar libName, Fl -library Ns = Ns Ar libName +Root name of library to use. +.It Fl L Ar dir , Fl -library-path Ns = Ns Ar dir +Add a directory to the library search path. +.It Fl -lto-aa-pipeline Ns = Ns Ar value +AA pipeline to run during LTO. +Used in conjunction with +.Fl -lto-newpm-passes . +.It Fl -lto-newpm-passes Ns = Ns Ar value +Passes to run during LTO. +.It Fl -lto-O Ns Ar opt-level +Optimization level for LTO. +.It Fl -lto-partitions Ns = Ns Ar value +Number of LTO codegen partitions. +.It Fl m Ar value +Set target emulation. +.It Fl -Map Ns = Ns Ar file , Fl M Ar file +Print a link map to +.Ar file . +.It Fl -no-as-needed +Always set +.Dv DT_NEEDED +for shared libraries. +.It Fl -no-color-diagnostics +Do not use colors in diagnostics. +.It Fl -no-define-common +Do not assign space to common symbols. +.It Fl -no-demangle +Do not demangle symbol names. +.It Fl -no-dynamic-linker +Inhibit output of an +.Li .interp +section. +.It Fl -no-gc-sections +Disable garbage collection of unused sections. +.It Fl -no-gnu-unique +Disable STB_GNU_UNIQUE symbol binding. +.It Fl -no-rosegment +Do not put read-only non-executable sections in their own segment. +.It Fl -no-threads +Do not run the linker multi-threaded. +.It Fl -no-undefined-version +Report version scripts that refer undefined symbols. +.It Fl -no-undefined +Report unresolved symbols even if the linker is creating a shared library. +.It Fl -no-whole-archive +Restores the default behavior of loading archive members. +.It Fl -no-pie +Do not create a position independent executable. +.It Fl -noinhibit-exec +Retain the executable output file whenever it is still usable. +.It Fl -nostdlib +Only search directories specified on the command line. +.It Fl o Ar path +Write the output executable, library, or object to +.Ar path . +If not specified, +.Dv a.out +is used as a default. +.It Fl O Ns Ar value +Optimize output file size. +.Ar value +may be: +.Pp +.Bl -tag -width 2n -compact +.It Cm 0 +Disable string merging. +.It Cm 1 +Enable string merging. +.It Cm 2 +Enable string tail merging. +.El +.Pp +.Fl O Ns Cm 1 +is the default. +.It Fl -oformat Ns = Ns Ar format +Specify the format for the output object file. +The only supported +.Ar format +is +.Cm binary , +which produces output with no ELF header. +.It Fl -omagic , Fl N +Set the text and data sections to be readable and writable. +.It Fl -opt-remarks-filename Ar file +Write optimization remarks in YAML format to +.Ar file . +.It Fl -opt-remarks-with-hotness +Include hotness information in the optimization remarks file. +.It Fl -pie +Create a position independent executable. +.It Fl -print-gc-sections +List removed unused sections. +.It Fl -print-map +Print a link map to the standard output. +.It Fl -push-state +Save the current state of +.Fl -as-needed , +.Fl -static , +and +.Fl -while-archive. +.It Fl -pop-state +Undo the effect of +.Fl -push-state. +.It Fl -relocatable , Fl r +Create relocatable object file. +.It Fl -reproduce Ns = Ns Ar value +Dump linker invocation and input files for debugging. +.It Fl -retain-symbols-file Ns = Ns Ar file +Retain only the symbols listed in the file. +.It Fl -rpath Ns = Ns Ar value , Fl R Ar value +Add a +.Dv DT_RUNPATH +to the output. +.It Fl -rsp-quoting Ns = Ns Ar value +Quoting style for response files. +The supported values are +.Cm windows +and +.Cm posix . +.It Fl -script Ns = Ns Ar file , Fl T Ar file +Read linker script from +.Ar file . +.It Fl -section-start Ns = Ns Ar section Ns = Ns Ar address +Set address of section. +.It Fl -shared , Fl -Bsharable +Build a shared object. +.It Fl -soname Ns = Ns Ar value , Fl h Ar value +Set +.Dv DT_SONAME +to +.Ar value . +.It Fl -sort-section Ns = Ns Ar value +Specifies sections sorting rule when linkerscript is used. +.It Fl -start-lib +Start a grouping of objects that should be treated as if they were together +in an archive. +.It Fl -strip-all , Fl s +Strip all symbols. +.It Fl -strip-debug , Fl S +Strip debugging information. +.It Fl -symbol-ordering-file Ns = Ns Ar file +Lay out sections in the order specified by +.Ar file . +.It Fl -sysroot Ns = Ns Ar value +Set the system root. +.It Fl -target1-abs +Interpret +.Dv R_ARM_TARGET1 +as +.Dv R_ARM_ABS32 . +.It Fl -target1-rel +Interpret +.Dv R_ARM_TARGET1 +as +.Dv R_ARM_REL32 . +.It Fl -target2 Ns = Ns Ar type +Interpret +.Dv R_ARM_TARGET2 +as +.Ar type , +where +.Ar type +is one of +.Cm rel , +.Cm abs , +or +.Cm got-rel . +.It Fl -Tbss Ns = Ns Ar value +Same as +.Fl -section-start +with +.Li .bss +as the sectionname. +.It Fl -Tdata Ns = Ns Ar value +Same as +.Fl -section-start +with +.Li .data +as the sectionname. +.It Fl -Ttext Ns = Ns Ar value +Same as +.Fl -section-start +with +.Li .text +as the sectionname. +.It Fl -thinlto-cache-dir Ns = Ns Ar value +Path to ThinLTO cached object file directory. +.It Fl -thinlto-cache-policy Ns = Ns Ar value +Pruning policy for the ThinLTO cache. +.It Fl -thinlto-jobs Ns = Ns Ar value +Number of ThinLTO jobs. +.It Fl -threads +Run the linker multi-threaded. +This option is enabled by default. +.It Fl -trace +Print the names of the input files. +.It Fl -trace-symbol Ns = Ns Ar symbol , Fl y Ar symbol +Trace references to +.Ar symbol . +.It Fl -undefined Ns = Ns Ar symbol , Fl u Ar symbol +Force +.Ar symbol +to be an undefined symbol during linking. +.It Fl -unresolved-symbols Ns = Ns Ar value +Determine how to handle unresolved symbols. +.It Fl v +Display the version number and proceed with linking if object files are +specified. +.It Fl V , Fl -version +Display the version number and exit. +.It Fl -verbose +Verbose mode. +.It Fl -version-script Ns = Ns Ar file +Read version script from +.Ar file . +.It Fl -warn-backrefs +Warn about reverse or cyclic dependencies to or between static archives. +This can be used to ensure linker invocation remains compatible with +traditional Unix-like linkers. +.It Fl -warn-common +Warn about duplicate common symbols. +.It Fl -warn-unresolved-symbols +Report unresolved symbols as warnings. +.It Fl -whole-archive +Force load of all members in a static library. +.It Fl -wrap Ns = Ns Ar symbol +Use wrapper functions for symbol. +.It Fl z Ar option +Linker option extensions. +.Bl -tag -width indent +.It Cm execstack +Make the main stack executable. +Stack permissions are recorded in the +.Dv PT_GNU_STACK +segment. +.It Cm initfirst +Sets the +.Dv DF_1_INITFIRST +flag to indicate the module should be initialized first. +.It Cm muldefs +Do not error if a symbol is defined multiple times. +The first definition will be used. +This is a synonym for +.Fl -allow-multiple-definition. +.It Cm nocombreloc +Disable combining and sorting multiple relocation sections. +.It Cm nocopyreloc +Disable the creation of copy relocations. +.It Cm nodelete +Set the +.Dv DF_1_NODELETE +flag to indicate that the object cannot be unloaded from a process. +.It Cm nodlopen +Set the +.Dv DF_1_NOOPEN +flag to indcate that the object may not be opened by +.Xr dlopen 3 . +.It Cm norelro +Do not indicate that portions of the object shold be mapped read-only +after initial relocation processing. +The object will omit the +.Dv PT_GNU_RELRO +segment. +.It Cm notext +Allow relocations against read-only segments. +Sets the +.Dv DT_TEXTREL flag in the +.Dv DYNAMIC +section. +.It Cm now +Set the +.Dv DF_BIND_NOW +flag to indicate that the run-time loader should perform all relocation +processing as part of object initialization. +By default relocations may be performed on demand. +.It Cm origin +Set the +.Dv DF_ORIGIN +flag to indicate that the object requires +$ORIGIN +processing. +.It Cm retpolineplt +Emit retpoline format PLT entries as a mitigation for CVE-2017-5715. +.It Cm rodynamic +Make the +.Li .dynamic +section read-only. +The +.Dv DT_DEBUG +tag will not be emitted. +.It Cm stack-size Ns = Ns Ar size +Set the main thread's stack size to +.Ar size . +The stack size is recorded as the size of the +.Ar size . +.Dv PT_GNU_STACK +program segment. +.It Cm text +Do not allow relocations against read-only segments. +This is the default. +.It Cm wxneeded +Create a +.Dv PT_OPENBSD_WXNEEDED +segment. +.El +.El +.Sh IMPLEMENTATION NOTES +.Nm Ap s +handing of archive files (those with a +.Pa .a +file extension) is different from traditional linkers used on Unix-like +systems. +.Pp +Traditional linkers maintain a set of undefined symbols during linking. +The linker processes each file in the order in which it appears on the +command line, until the set of undefined symbols becomes empty. +An object file is linked into the output object when it is encountered, +with its undefined symbols added to the set. +Upon encountering an archive file a traditional linker searches the objects +contained therein, and processes those that satisfy symbols in the unresolved +set. +.Pp +Handling mutually dependent archives may be awkward when using a traditional +linker. +Archive files may have to be specified multiple times, or the special command +line options +.Fl -start-group +and +.Fl -end-group +may be used to have the linker loop over the files in the group until no new +symbols are added to the set. +.Pp +.Nm +records all symbols found in objects and archives as it iterates over +command line arguments. +When +.Nm +encounters an undefined symbol that can be resolved by an object file +contained in a previously processed archive file, it immediately extracts +and links it into the output object. +.Pp +With certain archive inputs +.Nm +may produce different results compared to traditional linkers. +In practice, large bodies of third party software have been linked with +.Nm +without material issues. +.Pp +The +.Fl -warn-backrefs +option may be used to identify a linker invocation that may be incompatible +with traditional Unix-like linker behavior. diff --git a/contrib/llvm/tools/lld/docs/llvm-theme/layout.html b/contrib/llvm/tools/lld/docs/llvm-theme/layout.html new file mode 100644 index 000000000000..0cd0918eac2a --- /dev/null +++ b/contrib/llvm/tools/lld/docs/llvm-theme/layout.html @@ -0,0 +1,22 @@ +{# + sphinxdoc/layout.html + ~~~~~~~~~~~~~~~~~~~~~ + + Sphinx layout template for the sphinxdoc theme. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +#} +{% extends "basic/layout.html" %} + +{% block relbar1 %} +<div class="logo"> +<a href="{{ pathto('index') }}"><img src="{{ +pathto("_static/logo.png", 1) }}" alt="LLVM Documentation"/></a> +</div> +{{ super() }} +{% endblock %} + +{# put the sidebar before the body #} +{% block sidebar1 %}{{ sidebar() }}{% endblock %} +{% block sidebar2 %}{% endblock %} diff --git a/contrib/llvm/tools/lld/docs/llvm-theme/static/contents.png b/contrib/llvm/tools/lld/docs/llvm-theme/static/contents.png Binary files differnew file mode 100644 index 000000000000..7fb82154a174 --- /dev/null +++ b/contrib/llvm/tools/lld/docs/llvm-theme/static/contents.png diff --git a/contrib/llvm/tools/lld/docs/llvm-theme/static/llvm.css b/contrib/llvm/tools/lld/docs/llvm-theme/static/llvm.css new file mode 100644 index 000000000000..32802bb6a2d0 --- /dev/null +++ b/contrib/llvm/tools/lld/docs/llvm-theme/static/llvm.css @@ -0,0 +1,345 @@ +/* + * sphinxdoc.css_t + * ~~~~~~~~~~~~~~~ + * + * Sphinx stylesheet -- sphinxdoc theme. Originally created by + * Armin Ronacher for Werkzeug. + * + * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', + 'Verdana', sans-serif; + font-size: 14px; + letter-spacing: -0.01em; + line-height: 150%; + text-align: center; + background-color: #BFD1D4; + color: black; + padding: 0; + border: 1px solid #aaa; + + margin: 0px 80px 0px 80px; + min-width: 740px; +} + +div.logo { + background-color: white; + text-align: left; + padding: 10px 10px 15px 15px; +} + +div.document { + background-color: white; + text-align: left; + background-image: url(contents.png); + background-repeat: repeat-x; +} + +div.bodywrapper { + margin: 0 240px 0 0; + border-right: 1px solid #ccc; +} + +div.body { + margin: 0; + padding: 0.5em 20px 20px 20px; +} + +div.related { + font-size: 1em; +} + +div.related ul { + background-image: url(navigation.png); + height: 2em; + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; +} + +div.related ul li { + margin: 0; + padding: 0; + height: 2em; + float: left; +} + +div.related ul li.right { + float: right; + margin-right: 5px; +} + +div.related ul li a { + margin: 0; + padding: 0 5px 0 5px; + line-height: 1.75em; + color: #EE9816; +} + +div.related ul li a:hover { + color: #3CA8E7; +} + +div.sphinxsidebarwrapper { + padding: 0; +} + +div.sphinxsidebar { + margin: 0; + padding: 0.5em 15px 15px 0; + width: 210px; + float: right; + font-size: 1em; + text-align: left; +} + +div.sphinxsidebar h3, div.sphinxsidebar h4 { + margin: 1em 0 0.5em 0; + font-size: 1em; + padding: 0.1em 0 0.1em 0.5em; + color: white; + border: 1px solid #86989B; + background-color: #AFC1C4; +} + +div.sphinxsidebar h3 a { + color: white; +} + +div.sphinxsidebar ul { + padding-left: 1.5em; + margin-top: 7px; + padding: 0; + line-height: 130%; +} + +div.sphinxsidebar ul ul { + margin-left: 20px; +} + +div.footer { + background-color: #E3EFF1; + color: #86989B; + padding: 3px 8px 3px 0; + clear: both; + font-size: 0.8em; + text-align: right; +} + +div.footer a { + color: #86989B; + text-decoration: underline; +} + +/* -- body styles ----------------------------------------------------------- */ + +p { + margin: 0.8em 0 0.5em 0; +} + +a { + color: #CA7900; + text-decoration: none; +} + +a:hover { + color: #2491CF; +} + +div.body a { + text-decoration: underline; +} + +h1 { + margin: 0; + padding: 0.7em 0 0.3em 0; + font-size: 1.5em; + color: #11557C; +} + +h2 { + margin: 1.3em 0 0.2em 0; + font-size: 1.35em; + padding: 0; +} + +h3 { + margin: 1em 0 -0.3em 0; + font-size: 1.2em; +} + +div.body h1 a, div.body h2 a, div.body h3 a, div.body h4 a, div.body h5 a, div.body h6 a { + color: black!important; +} + +h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor { + display: none; + margin: 0 0 0 0.3em; + padding: 0 0.2em 0 0.2em; + color: #aaa!important; +} + +h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, +h5:hover a.anchor, h6:hover a.anchor { + display: inline; +} + +h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover, +h5 a.anchor:hover, h6 a.anchor:hover { + color: #777; + background-color: #eee; +} + +a.headerlink { + color: #c60f0f!important; + font-size: 1em; + margin-left: 6px; + padding: 0 4px 0 4px; + text-decoration: none!important; +} + +a.headerlink:hover { + background-color: #ccc; + color: white!important; +} + +cite, code, tt { + font-family: 'Consolas', 'Deja Vu Sans Mono', + 'Bitstream Vera Sans Mono', monospace; + font-size: 0.95em; + letter-spacing: 0.01em; +} + +tt { + background-color: #f2f2f2; + border-bottom: 1px solid #ddd; + color: #333; +} + +tt.descname, tt.descclassname, tt.xref { + border: 0; +} + +hr { + border: 1px solid #abc; + margin: 2em; +} + +a tt { + border: 0; + color: #CA7900; +} + +a tt:hover { + color: #2491CF; +} + +pre { + font-family: 'Consolas', 'Deja Vu Sans Mono', + 'Bitstream Vera Sans Mono', monospace; + font-size: 0.95em; + letter-spacing: 0.015em; + line-height: 120%; + padding: 0.5em; + border: 1px solid #ccc; + background-color: #f8f8f8; +} + +pre a { + color: inherit; + text-decoration: underline; +} + +td.linenos pre { + padding: 0.5em 0; +} + +div.quotebar { + background-color: #f8f8f8; + max-width: 250px; + float: right; + padding: 2px 7px; + border: 1px solid #ccc; +} + +div.topic { + background-color: #f8f8f8; +} + +table { + border-collapse: collapse; + margin: 0 -0.5em 0 -0.5em; +} + +table td, table th { + padding: 0.2em 0.5em 0.2em 0.5em; +} + +div.admonition, div.warning { + font-size: 0.9em; + margin: 1em 0 1em 0; + border: 1px solid #86989B; + background-color: #f7f7f7; + padding: 0; +} + +div.admonition p, div.warning p { + margin: 0.5em 1em 0.5em 1em; + padding: 0; +} + +div.admonition pre, div.warning pre { + margin: 0.4em 1em 0.4em 1em; +} + +div.admonition p.admonition-title, +div.warning p.admonition-title { + margin: 0; + padding: 0.1em 0 0.1em 0.5em; + color: white; + border-bottom: 1px solid #86989B; + font-weight: bold; + background-color: #AFC1C4; +} + +div.warning { + border: 1px solid #940000; +} + +div.warning p.admonition-title { + background-color: #CF0000; + border-bottom-color: #940000; +} + +div.admonition ul, div.admonition ol, +div.warning ul, div.warning ol { + margin: 0.1em 0.5em 0.5em 3em; + padding: 0; +} + +div.versioninfo { + margin: 1em 0 0 0; + border: 1px solid #ccc; + background-color: #DDEAF0; + padding: 8px; + line-height: 1.3em; + font-size: 0.9em; +} + +.viewcode-back { + font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', + 'Verdana', sans-serif; +} + +div.viewcode-block:target { + background-color: #f4debf; + border-top: 1px solid #ac9; + border-bottom: 1px solid #ac9; +} diff --git a/contrib/llvm/tools/lld/docs/llvm-theme/static/logo.png b/contrib/llvm/tools/lld/docs/llvm-theme/static/logo.png Binary files differnew file mode 100644 index 000000000000..4fc899028dc6 --- /dev/null +++ b/contrib/llvm/tools/lld/docs/llvm-theme/static/logo.png diff --git a/contrib/llvm/tools/lld/docs/llvm-theme/static/navigation.png b/contrib/llvm/tools/lld/docs/llvm-theme/static/navigation.png Binary files differnew file mode 100644 index 000000000000..1081dc1439fb --- /dev/null +++ b/contrib/llvm/tools/lld/docs/llvm-theme/static/navigation.png diff --git a/contrib/llvm/tools/lld/docs/llvm-theme/theme.conf b/contrib/llvm/tools/lld/docs/llvm-theme/theme.conf new file mode 100644 index 000000000000..330fc92ffa18 --- /dev/null +++ b/contrib/llvm/tools/lld/docs/llvm-theme/theme.conf @@ -0,0 +1,4 @@ +[theme] +inherit = basic +stylesheet = llvm.css +pygments_style = friendly diff --git a/contrib/llvm/tools/lld/docs/make.bat b/contrib/llvm/tools/lld/docs/make.bat new file mode 100644 index 000000000000..8471252d709f --- /dev/null +++ b/contrib/llvm/tools/lld/docs/make.bat @@ -0,0 +1,190 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^<target^>` where ^<target^> is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\lld.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\lld.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end diff --git a/contrib/llvm/tools/lld/docs/open_projects.rst b/contrib/llvm/tools/lld/docs/open_projects.rst new file mode 100644 index 000000000000..eeb9f9f48f34 --- /dev/null +++ b/contrib/llvm/tools/lld/docs/open_projects.rst @@ -0,0 +1,11 @@ +.. _open_projects: + +Open Projects +============= + +.. include:: ../include/lld/Core/TODO.txt + +Documentation TODOs +~~~~~~~~~~~~~~~~~~~ + +.. todolist:: diff --git a/contrib/llvm/tools/lld/docs/sphinx_intro.rst b/contrib/llvm/tools/lld/docs/sphinx_intro.rst new file mode 100644 index 000000000000..6bb9816b5ab4 --- /dev/null +++ b/contrib/llvm/tools/lld/docs/sphinx_intro.rst @@ -0,0 +1,127 @@ +.. _sphinx_intro: + +Sphinx Introduction for LLVM Developers +======================================= + +This document is intended as a short and simple introduction to the Sphinx +documentation generation system for LLVM developers. + +Quickstart +---------- + +To get started writing documentation, you will need to: + + 1. Have the Sphinx tools :ref:`installed <installing_sphinx>`. + + 2. Understand how to :ref:`build the documentation + <building_the_documentation>`. + + 3. Start :ref:`writing documentation <writing_documentation>`! + +.. _installing_sphinx: + +Installing Sphinx +~~~~~~~~~~~~~~~~~ + +You should be able to install Sphinx using the standard Python package +installation tool ``easy_install``, as follows:: + + $ sudo easy_install sphinx + Searching for sphinx + Reading http://pypi.python.org/simple/sphinx/ + Reading http://sphinx.pocoo.org/ + Best match: Sphinx 1.1.3 + ... more lines here .. + +If you do not have root access (or otherwise want to avoid installing Sphinx in +system directories) see the section on :ref:`installing_sphinx_in_a_venv` . + +If you do not have the ``easy_install`` tool on your system, you should be able +to install it using: + + Linux + Use your distribution's standard package management tool to install it, + i.e., ``apt-get install easy_install`` or ``yum install easy_install``. + + Mac OS X + All modern Mac OS X systems come with ``easy_install`` as part of the base + system. + + Windows + See the `setuptools <http://pypi.python.org/pypi/setuptools>`_ package web + page for instructions. + + +.. _building_the_documentation: + +Building the documentation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to build the documentation need to add ``-DLLVM_ENABLE_SPHINX=ON`` to +your ``cmake`` command. Once you do this you can build the docs using +``docs-lld-html`` build (``ninja`` or ``make``) target. + +That build target will invoke ``sphinx-build`` with the appropriate options for +the project, and generate the HTML documentation in a ``tools/lld/docs/html`` +subdirectory. + +.. _writing_documentation: + +Writing documentation +~~~~~~~~~~~~~~~~~~~~~ + +The documentation itself is written in the reStructuredText (ReST) format, and +Sphinx defines additional tags to support features like cross-referencing. + +The ReST format itself is organized around documents mostly being readable +plaintext documents. You should generally be able to write new documentation +easily just by following the style of the existing documentation. + +If you want to understand the formatting of the documents more, the best place +to start is Sphinx's own `ReST Primer <http://sphinx.pocoo.org/rest.html>`_. + + +Learning More +------------- + +If you want to learn more about the Sphinx system, the best place to start is +the Sphinx documentation itself, available `here +<http://sphinx.pocoo.org/contents.html>`_. + + +.. _installing_sphinx_in_a_venv: + +Installing Sphinx in a Virtual Environment +------------------------------------------ + +Most Python developers prefer to work with tools inside a *virtualenv* (virtual +environment) instance, which functions as an application sandbox. This avoids +polluting your system installation with different packages used by various +projects (and ensures that dependencies for different packages don't conflict +with one another). Of course, you need to first have the virtualenv software +itself which generally would be installed at the system level:: + + $ sudo easy_install virtualenv + +but after that you no longer need to install additional packages in the system +directories. + +Once you have the *virtualenv* tool itself installed, you can create a +virtualenv for Sphinx using:: + + $ virtualenv ~/my-sphinx-install + New python executable in /Users/dummy/my-sphinx-install/bin/python + Installing setuptools............done. + Installing pip...............done. + + $ ~/my-sphinx-install/bin/easy_install sphinx + ... install messages here ... + +and from now on you can "activate" the *virtualenv* using:: + + $ source ~/my-sphinx-install/bin/activate + +which will change your PATH to ensure the sphinx-build tool from inside the +virtual environment will be used. See the `virtualenv website +<http://www.virtualenv.org/en/latest/index.html>`_ for more information on using +virtual environments. diff --git a/contrib/llvm/tools/lld/docs/windows_support.rst b/contrib/llvm/tools/lld/docs/windows_support.rst new file mode 100644 index 000000000000..a0a2c4d9f1bc --- /dev/null +++ b/contrib/llvm/tools/lld/docs/windows_support.rst @@ -0,0 +1,97 @@ +.. raw:: html + + <style type="text/css"> + .none { background-color: #FFCCCC } + .partial { background-color: #FFFF99 } + .good { background-color: #CCFF99 } + </style> + +.. role:: none +.. role:: partial +.. role:: good + +=============== +Windows support +=============== + +LLD supports Windows operating system. When invoked as ``lld-link.exe`` or with +``-flavor link``, the driver for Windows operating system is used to parse +command line options, and it drives further linking processes. LLD accepts +almost all command line options that the linker shipped with Microsoft Visual +C++ (link.exe) supports. + +The current status is that LLD can link itself on Windows x86/x64 +using Visual C++ 2013 as the compiler. + +Development status +================== + +Driver + :good:`Mostly done`. Some exotic command line options that are not usually + used for application develompent, such as ``/DRIVER``, are not supported. + +Linking against DLL + :good:`Done`. LLD can read import libraries needed to link against DLL. Both + export-by-name and export-by-ordinal are supported. + +Linking against static library + :good:`Done`. The format of static library (.lib) on Windows is actually the + same as on Unix (.a). LLD can read it. + +Creating DLL + :good:`Done`. LLD creates a DLL if ``/DLL`` option is given. Exported + functions can be specified either via command line (``/EXPORT``) or via + module-definition file (.def). Both export-by-name and export-by-ordinal are + supported. + +Windows resource files support + :good:`Done`. If an ``.res`` file is given, LLD converts the file to a COFF + file using LLVM's Object library. + +Safe Structured Exception Handler (SEH) + :good:`Done` for both x86 and x64. + +Module-definition file + :partial:`Partially done`. LLD currently recognizes these directives: + ``EXPORTS``, ``HEAPSIZE``, ``STACKSIZE``, ``NAME``, and ``VERSION``. + +Debug info + :good:`Done`. LLD can emit PDBs that are at parity with those generated by + link.exe. However, LLD does not support /DEBUG:FASTLINK. + + +Downloading LLD +=============== + +The Windows version of LLD is included in the `pre-built binaries of LLVM's +releases <https://releases.llvm.org/download.html>`_ and in the `LLVM Snapshot +Builds <https://llvm.org/builds/>`_. + +Building LLD +============ + +Using Visual Studio IDE/MSBuild +------------------------------- + +1. Check out LLVM and LLD from the LLVM SVN repository (or Git mirror), +#. run ``cmake -G "Visual Studio 12" <llvm-source-dir>`` from VS command prompt, +#. open LLVM.sln with Visual Studio, and +#. build ``lld`` target in ``lld executables`` folder + +Alternatively, you can use msbuild if you don't like to work in an IDE:: + + msbuild LLVM.sln /m /target:"lld executables\lld" + +MSBuild.exe had been shipped as a component of the .NET framework, but since +2013 it's part of Visual Studio. You can find it at "C:\\Program Files +(x86)\\msbuild". + +You can build LLD as a 64 bit application. To do that, open VS2013 x64 command +prompt and run cmake for "Visual Studio 12 Win64" target. + +Using Ninja +----------- + +1. Check out LLVM and LLD from the LLVM SVN repository (or Git mirror), +#. run ``cmake -G ninja <llvm-source-dir>`` from VS command prompt, +#. run ``ninja lld`` diff --git a/contrib/llvm/tools/lld/include/lld/Common/Driver.h b/contrib/llvm/tools/lld/include/lld/Common/Driver.h index 15ec3cd44cb5..f6d92933af62 100644 --- a/contrib/llvm/tools/lld/include/lld/Common/Driver.h +++ b/contrib/llvm/tools/lld/include/lld/Common/Driver.h @@ -30,7 +30,7 @@ bool link(llvm::ArrayRef<const char *> Args, bool CanExitEarly, } namespace mach_o { -bool link(llvm::ArrayRef<const char *> Args, +bool link(llvm::ArrayRef<const char *> Args, bool CanExitEarly, llvm::raw_ostream &Diag = llvm::errs()); } diff --git a/contrib/llvm/tools/lld/include/lld/Common/ErrorHandler.h b/contrib/llvm/tools/lld/include/lld/Common/ErrorHandler.h index 8ae6f46ac59e..f17f7cc99035 100644 --- a/contrib/llvm/tools/lld/include/lld/Common/ErrorHandler.h +++ b/contrib/llvm/tools/lld/include/lld/Common/ErrorHandler.h @@ -7,21 +7,62 @@ // //===----------------------------------------------------------------------===// // -// In LLD, we have three levels of errors: fatal, error or warn. +// We designed lld's error handlers with the following goals in mind: // -// Fatal makes the program exit immediately with an error message. -// You shouldn't use it except for reporting a corrupted input file. +// - Errors can occur at any place where we handle user input, but we don't +// want them to affect the normal execution path too much. Ideally, +// handling errors should be as simple as reporting them and exit (but +// without actually doing exit). // -// Error prints out an error message and increment a global variable -// ErrorCount to record the fact that we met an error condition. It does -// not exit, so it is safe for a lld-as-a-library use case. It is generally -// useful because it can report more than one error in a single run. +// In particular, the design to wrap all functions that could fail with +// ErrorOr<T> is rejected because otherwise we would have to wrap a large +// number of functions in lld with ErrorOr. With that approach, if some +// function F can fail, not only F but all functions that transitively call +// F have to be wrapped with ErrorOr. That seemed too much. // -// Warn doesn't do anything but printing out a given message. +// - Finding only one error at a time is not sufficient. We want to find as +// many errors as possible with one execution of the linker. That means the +// linker needs to keep running after a first error and give up at some +// checkpoint (beyond which it would find cascading, false errors caused by +// the previous errors). // -// It is not recommended to use llvm::outs() or llvm::errs() directly -// in LLD because they are not thread-safe. The functions declared in -// this file are mutually excluded, so you want to use them instead. +// - We want a simple interface to report errors. Unlike Clang, the data we +// handle is compiled binary, so we don't need an error reporting mechanism +// that's as sophisticated as the one that Clang has. +// +// The current lld's error handling mechanism is simple: +// +// - When you find an error, report it using error() and continue as far as +// you can. An internal error counter is incremented by one every time you +// call error(). +// +// A common idiom to handle an error is calling error() and then returning +// a reasonable default value. For example, if your function handles a +// user-supplied alignment value, and if you find an invalid alignment +// (e.g. 17 which is not 2^n), you may report it using error() and continue +// as if it were alignment 1 (which is the simplest reasonable value). +// +// Note that you should not continue with an invalid value; that breaks the +// internal consistency. You need to maintain all variables have some sane +// value even after an error occurred. So, when you have to continue with +// some value, always use a dummy value. +// +// - Find a reasonable checkpoint at where you want to stop the linker, and +// add code to return from the function if errorCount() > 0. In most cases, +// a checkpoint already exists, so you don't need to do anything for this. +// +// This interface satisfies all the goals that we mentioned above. +// +// You should never call fatal() except for reporting a corrupted input file. +// fatal() immediately terminates the linker, so the function is not desirable +// if you are using lld as a subroutine in other program, and with that you +// can find only one error at a time. +// +// warn() doesn't do anything but printing out a given message. +// +// It is not recommended to use llvm::outs() or llvm::errs() directly in lld +// because they are not thread-safe. The functions declared in this file are +// thread-safe. // //===----------------------------------------------------------------------===// @@ -34,6 +75,10 @@ #include "llvm/Support/Error.h" #include "llvm/Support/FileOutputBuffer.h" +namespace llvm { +class DiagnosticInfo; +} + namespace lld { class ErrorHandler { @@ -74,6 +119,9 @@ inline uint64_t errorCount() { return errorHandler().ErrorCount; } LLVM_ATTRIBUTE_NORETURN void exitLld(int Val); +void diagnosticHandler(const llvm::DiagnosticInfo &DI); +void checkError(Error E); + // check functions are convenient functions to strip errors // from error-or-value objects. template <class T> T check(ErrorOr<T> E) { diff --git a/contrib/llvm/tools/lld/include/lld/Common/Strings.h b/contrib/llvm/tools/lld/include/lld/Common/Strings.h index 1a63f75f9ecf..e17b25763781 100644 --- a/contrib/llvm/tools/lld/include/lld/Common/Strings.h +++ b/contrib/llvm/tools/lld/include/lld/Common/Strings.h @@ -10,14 +10,40 @@ #ifndef LLD_STRINGS_H #define LLD_STRINGS_H +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/GlobPattern.h" #include <string> +#include <vector> namespace lld { // Returns a demangled C++ symbol name. If Name is not a mangled // name, it returns Optional::None. llvm::Optional<std::string> demangleItanium(llvm::StringRef Name); +llvm::Optional<std::string> demangleMSVC(llvm::StringRef S); + +std::vector<uint8_t> parseHex(llvm::StringRef S); +bool isValidCIdentifier(llvm::StringRef S); + +// Write the contents of the a buffer to a file +void saveBuffer(llvm::StringRef Buffer, const llvm::Twine &Path); + +// This class represents multiple glob patterns. +class StringMatcher { +public: + StringMatcher() = default; + explicit StringMatcher(llvm::ArrayRef<llvm::StringRef> Pat); + + bool match(llvm::StringRef S) const; + +private: + std::vector<llvm::GlobPattern> Patterns; +}; + +inline llvm::ArrayRef<uint8_t> toArrayRef(llvm::StringRef S) { + return {reinterpret_cast<const uint8_t *>(S.data()), S.size()}; } +} // namespace lld #endif diff --git a/contrib/llvm/tools/lld/include/lld/Common/TargetOptionsCommandFlags.h b/contrib/llvm/tools/lld/include/lld/Common/TargetOptionsCommandFlags.h index 9c4ff7cea3fb..8443b184aa70 100644 --- a/contrib/llvm/tools/lld/include/lld/Common/TargetOptionsCommandFlags.h +++ b/contrib/llvm/tools/lld/include/lld/Common/TargetOptionsCommandFlags.h @@ -18,4 +18,5 @@ namespace lld { llvm::TargetOptions InitTargetOptionsFromCodeGenFlags(); llvm::Optional<llvm::CodeModel::Model> GetCodeModelFromCMModel(); +std::string GetCPUStr(); } diff --git a/contrib/llvm/tools/lld/include/lld/Common/Timer.h b/contrib/llvm/tools/lld/include/lld/Common/Timer.h new file mode 100644 index 000000000000..6654af626919 --- /dev/null +++ b/contrib/llvm/tools/lld/include/lld/Common/Timer.h @@ -0,0 +1,59 @@ +//===- Timer.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_COMMON_TIMER_H +#define LLD_COMMON_TIMER_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include <assert.h> +#include <chrono> +#include <map> +#include <memory> + +namespace lld { + +class Timer; + +struct ScopedTimer { + explicit ScopedTimer(Timer &T); + + ~ScopedTimer(); + + void stop(); + + Timer *T = nullptr; +}; + +class Timer { +public: + Timer(llvm::StringRef Name, Timer &Parent); + + static Timer &root(); + + void start(); + void stop(); + void print(); + + double millis() const; + +private: + explicit Timer(llvm::StringRef Name); + void print(int Depth, double TotalDuration, bool Recurse = true) const; + + std::chrono::time_point<std::chrono::high_resolution_clock> StartTime; + std::chrono::nanoseconds Total; + std::vector<Timer *> Children; + std::string Name; + Timer *Parent; +}; + +} // namespace lld + +#endif diff --git a/contrib/llvm/tools/lld/include/lld/Common/Version.h b/contrib/llvm/tools/lld/include/lld/Common/Version.h index 93de77df5804..23a10ed6dbf3 100644 --- a/contrib/llvm/tools/lld/include/lld/Common/Version.h +++ b/contrib/llvm/tools/lld/include/lld/Common/Version.h @@ -18,7 +18,7 @@ #include "llvm/ADT/StringRef.h" namespace lld { -/// \brief Retrieves a string representing the complete lld version. +/// Retrieves a string representing the complete lld version. std::string getLLDVersion(); } diff --git a/contrib/llvm/tools/lld/include/lld/Core/DefinedAtom.h b/contrib/llvm/tools/lld/include/lld/Core/DefinedAtom.h index 6229d67e25a5..ba10b45411f1 100644 --- a/contrib/llvm/tools/lld/include/lld/Core/DefinedAtom.h +++ b/contrib/llvm/tools/lld/include/lld/Core/DefinedAtom.h @@ -18,7 +18,7 @@ namespace lld { class File; -/// \brief The fundamental unit of linking. +/// The fundamental unit of linking. /// /// A C function or global variable is an atom. An atom has content and /// attributes. The content of a function atom is the instructions that @@ -179,10 +179,10 @@ public: }; enum DynamicExport { - /// \brief The linker may or may not export this atom dynamically depending + /// The linker may or may not export this atom dynamically depending /// on the output type and other context of the link. dynamicExportNormal, - /// \brief The linker will always export this atom dynamically. + /// The linker will always export this atom dynamically. dynamicExportAlways, }; @@ -212,26 +212,26 @@ public: } }; - /// \brief returns a value for the order of this Atom within its file. + /// returns a value for the order of this Atom within its file. /// /// This is used by the linker to order the layout of Atoms so that the /// resulting image is stable and reproducible. virtual uint64_t ordinal() const = 0; - /// \brief the number of bytes of space this atom's content will occupy in the + /// the number of bytes of space this atom's content will occupy in the /// final linked image. /// /// For a function atom, it is the number of bytes of code in the function. virtual uint64_t size() const = 0; - /// \brief The size of the section from which the atom is instantiated. + /// The size of the section from which the atom is instantiated. /// /// Merge::mergeByLargestSection is defined in terms of section size /// and not in terms of atom size, so we need this function separate /// from size(). virtual uint64_t sectionSize() const { return 0; } - /// \brief The visibility of this atom to other atoms. + /// The visibility of this atom to other atoms. /// /// C static functions have scope scopeTranslationUnit. Regular C functions /// have scope scopeGlobal. Functions compiled with visibility=hidden have @@ -239,48 +239,48 @@ public: /// not by the OS loader. virtual Scope scope() const = 0; - /// \brief Whether the linker should use direct or indirect access to this + /// Whether the linker should use direct or indirect access to this /// atom. virtual Interposable interposable() const = 0; - /// \brief how the linker should handle if multiple atoms have the same name. + /// how the linker should handle if multiple atoms have the same name. virtual Merge merge() const = 0; - /// \brief The type of this atom, such as code or data. + /// The type of this atom, such as code or data. virtual ContentType contentType() const = 0; - /// \brief The alignment constraints on how this atom must be laid out in the + /// The alignment constraints on how this atom must be laid out in the /// final linked image (e.g. 16-byte aligned). virtual Alignment alignment() const = 0; - /// \brief Whether this atom must be in a specially named section in the final + /// Whether this atom must be in a specially named section in the final /// linked image, or if the linker can infer the section based on the /// contentType(). virtual SectionChoice sectionChoice() const = 0; - /// \brief If sectionChoice() != sectionBasedOnContent, then this return the + /// If sectionChoice() != sectionBasedOnContent, then this return the /// name of the section the atom should be placed into. virtual StringRef customSectionName() const = 0; - /// \brief constraints on whether the linker may dead strip away this atom. + /// constraints on whether the linker may dead strip away this atom. virtual DeadStripKind deadStrip() const = 0; - /// \brief Under which conditions should this atom be dynamically exported. + /// Under which conditions should this atom be dynamically exported. virtual DynamicExport dynamicExport() const { return dynamicExportNormal; } - /// \brief Code model used by the atom. + /// Code model used by the atom. virtual CodeModel codeModel() const { return codeNA; } - /// \brief Returns the OS memory protections required for this atom's content + /// Returns the OS memory protections required for this atom's content /// at runtime. /// /// A function atom is R_X, a global variable is RW_, and a read-only constant /// is R__. virtual ContentPermissions permissions() const; - /// \brief returns a reference to the raw (unrelocated) bytes of this Atom's + /// returns a reference to the raw (unrelocated) bytes of this Atom's /// content. virtual ArrayRef<uint8_t> rawContent() const = 0; @@ -317,10 +317,10 @@ public: const void *_it; }; - /// \brief Returns an iterator to the beginning of this Atom's References. + /// Returns an iterator to the beginning of this Atom's References. virtual reference_iterator begin() const = 0; - /// \brief Returns an iterator to the end of this Atom's References. + /// Returns an iterator to the end of this Atom's References. virtual reference_iterator end() const = 0; /// Adds a reference to this atom. @@ -361,11 +361,11 @@ protected: ~DefinedAtom() override = default; - /// \brief Returns a pointer to the Reference object that the abstract + /// Returns a pointer to the Reference object that the abstract /// iterator "points" to. virtual const Reference *derefIterator(const void *iter) const = 0; - /// \brief Adjusts the abstract iterator to "point" to the next Reference + /// Adjusts the abstract iterator to "point" to the next Reference /// object for this Atom. virtual void incrementIterator(const void *&iter) const = 0; }; diff --git a/contrib/llvm/tools/lld/include/lld/Core/File.h b/contrib/llvm/tools/lld/include/lld/Core/File.h index 20418688dfa0..54f533576a4b 100644 --- a/contrib/llvm/tools/lld/include/lld/Core/File.h +++ b/contrib/llvm/tools/lld/include/lld/Core/File.h @@ -43,7 +43,7 @@ class File { public: virtual ~File(); - /// \brief Kinds of files that are supported. + /// Kinds of files that are supported. enum Kind { kindErrorObject, ///< a error object file (.o) kindNormalizedObject, ///< a normalized file (.o) @@ -59,7 +59,7 @@ public: kindArchiveLibrary ///< archive (.a) }; - /// \brief Returns file kind. Need for dyn_cast<> on File objects. + /// Returns file kind. Need for dyn_cast<> on File objects. Kind kind() const { return _kind; } @@ -114,10 +114,8 @@ public: AtomRange(AtomVector<T> &v) : _v(v) {} AtomRange(const AtomVector<T> &v) : _v(const_cast<AtomVector<T> &>(v)) {} - typedef std::pointer_to_unary_function<const OwningAtomPtr<T>&, - const T*> ConstDerefFn; - - typedef std::pointer_to_unary_function<OwningAtomPtr<T>&, T*> DerefFn; + using ConstDerefFn = const T* (*)(const OwningAtomPtr<T>&); + using DerefFn = T* (*)(OwningAtomPtr<T>&); typedef llvm::mapped_iterator<typename AtomVector<T>::const_iterator, ConstDerefFn> ConstItTy; @@ -174,19 +172,19 @@ public: AtomVector<T> &_v; }; - /// \brief Must be implemented to return the AtomVector object for + /// Must be implemented to return the AtomVector object for /// all DefinedAtoms in this File. virtual const AtomRange<DefinedAtom> defined() const = 0; - /// \brief Must be implemented to return the AtomVector object for + /// Must be implemented to return the AtomVector object for /// all UndefinedAtomw in this File. virtual const AtomRange<UndefinedAtom> undefined() const = 0; - /// \brief Must be implemented to return the AtomVector object for + /// Must be implemented to return the AtomVector object for /// all SharedLibraryAtoms in this File. virtual const AtomRange<SharedLibraryAtom> sharedLibrary() const = 0; - /// \brief Must be implemented to return the AtomVector object for + /// Must be implemented to return the AtomVector object for /// all AbsoluteAtoms in this File. virtual const AtomRange<AbsoluteAtom> absolute() const = 0; @@ -196,7 +194,7 @@ public: /// of a different file. We need to destruct all atoms before any files. virtual void clearAtoms() = 0; - /// \brief If a file is parsed using a different method than doParse(), + /// If a file is parsed using a different method than doParse(), /// one must use this method to set the last error status, so that /// doParse will not be called twice. Only YAML reader uses this /// (because YAML reader does not read blobs but structured data). @@ -214,12 +212,12 @@ public: } protected: - /// \brief only subclasses of File can be instantiated + /// only subclasses of File can be instantiated File(StringRef p, Kind kind) : _path(p), _kind(kind), _ordinal(UINT64_MAX), _nextAtomOrdinal(0) {} - /// \brief Subclasses should override this method to parse the + /// Subclasses should override this method to parse the /// memory buffer passed to this file's constructor. virtual std::error_code doParse() { return std::error_code(); } diff --git a/contrib/llvm/tools/lld/include/lld/Core/Instrumentation.h b/contrib/llvm/tools/lld/include/lld/Core/Instrumentation.h index 162375905e17..939d64557587 100644 --- a/contrib/llvm/tools/lld/include/lld/Core/Instrumentation.h +++ b/contrib/llvm/tools/lld/include/lld/Core/Instrumentation.h @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief Provide an Instrumentation API that optionally uses VTune interfaces. +/// Provide an Instrumentation API that optionally uses VTune interfaces. /// //===----------------------------------------------------------------------===// @@ -24,7 +24,7 @@ namespace lld { #ifdef LLD_HAS_VTUNE -/// \brief A unique global scope for instrumentation data. +/// A unique global scope for instrumentation data. /// /// Domains last for the lifetime of the application and cannot be destroyed. /// Multiple Domains created with the same name represent the same domain. @@ -38,7 +38,7 @@ public: __itt_domain *operator->() const { return _domain; } }; -/// \brief A global reference to a string constant. +/// A global reference to a string constant. /// /// These are uniqued by the ITT runtime and cannot be deleted. They are not /// specific to a domain. @@ -54,7 +54,7 @@ public: operator __itt_string_handle *() const { return _handle; } }; -/// \brief A task on a single thread. Nests within other tasks. +/// A task on a single thread. Nests within other tasks. /// /// Each thread has its own task stack and tasks nest recursively on that stack. /// A task cannot transfer threads. @@ -68,7 +68,7 @@ class ScopedTask { ScopedTask &operator=(const ScopedTask &) = delete; public: - /// \brief Create a task in Domain \p d named \p s. + /// Create a task in Domain \p d named \p s. ScopedTask(const Domain &d, const StringHandle &s) : _domain(d) { __itt_task_begin(d, __itt_null, __itt_null, s); } @@ -83,7 +83,7 @@ public: return *this; } - /// \brief Prematurely end this task. + /// Prematurely end this task. void end() { if (_domain) __itt_task_end(_domain); @@ -93,7 +93,7 @@ public: ~ScopedTask() { end(); } }; -/// \brief A specific point in time. Allows metadata to be associated. +/// A specific point in time. Allows metadata to be associated. class Marker { public: Marker(const Domain &d, const StringHandle &s) { diff --git a/contrib/llvm/tools/lld/include/lld/Core/LinkingContext.h b/contrib/llvm/tools/lld/include/lld/Core/LinkingContext.h index eb9510cbd215..52ab1a2480e8 100644 --- a/contrib/llvm/tools/lld/include/lld/Core/LinkingContext.h +++ b/contrib/llvm/tools/lld/include/lld/Core/LinkingContext.h @@ -16,7 +16,6 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Error.h" -#include "llvm/Support/raw_ostream.h" #include <cassert> #include <cstdint> #include <memory> @@ -31,7 +30,7 @@ class Writer; class Node; class SharedLibraryFile; -/// \brief The LinkingContext class encapsulates "what and how" to link. +/// The LinkingContext class encapsulates "what and how" to link. /// /// The base class LinkingContext contains the options needed by core linking. /// Subclasses of LinkingContext have additional options needed by specific @@ -167,10 +166,10 @@ public: /// After all set* methods are called, the Driver calls this method /// to validate that there are no missing options or invalid combinations /// of options. If there is a problem, a description of the problem - /// is written to the supplied stream. + /// is written to the global error handler. /// /// \returns true if there is an error with the current settings. - bool validate(raw_ostream &diagnostics); + bool validate(); /// Formats symbol name for use in error messages. virtual std::string demangle(StringRef symbolName) const = 0; @@ -250,7 +249,7 @@ protected: private: /// Validate the subclass bits. Only called by validate. - virtual bool validateImpl(raw_ostream &diagnostics) = 0; + virtual bool validateImpl() = 0; }; } // end namespace lld diff --git a/contrib/llvm/tools/lld/include/lld/Core/PassManager.h b/contrib/llvm/tools/lld/include/lld/Core/PassManager.h index 2ea65ae13ace..f2ef10f406f2 100644 --- a/contrib/llvm/tools/lld/include/lld/Core/PassManager.h +++ b/contrib/llvm/tools/lld/include/lld/Core/PassManager.h @@ -20,7 +20,7 @@ namespace lld { class SimpleFile; class Pass; -/// \brief Owns and runs a collection of passes. +/// Owns and runs a collection of passes. /// /// This class is currently just a container for passes and a way to run them. /// @@ -40,7 +40,7 @@ public: } private: - /// \brief Passes in the order they should run. + /// Passes in the order they should run. std::vector<std::unique_ptr<Pass>> _passes; }; } // end namespace lld diff --git a/contrib/llvm/tools/lld/include/lld/Core/Reader.h b/contrib/llvm/tools/lld/include/lld/Core/Reader.h index c7baf86af61f..6cf6282ff39c 100644 --- a/contrib/llvm/tools/lld/include/lld/Core/Reader.h +++ b/contrib/llvm/tools/lld/include/lld/Core/Reader.h @@ -32,7 +32,7 @@ class File; class LinkingContext; class MachOLinkingContext; -/// \brief An abstract class for reading object files, library files, and +/// An abstract class for reading object files, library files, and /// executable files. /// /// Each file format (e.g. mach-o, etc) has a concrete subclass of Reader. @@ -46,14 +46,14 @@ public: /// 2) the whole file content buffer if the above is not enough. virtual bool canParse(llvm::file_magic magic, MemoryBufferRef mb) const = 0; - /// \brief Parse a supplied buffer (already filled with the contents of a + /// Parse a supplied buffer (already filled with the contents of a /// file) and create a File object. /// The resulting File object takes ownership of the MemoryBuffer. virtual ErrorOr<std::unique_ptr<File>> loadFile(std::unique_ptr<MemoryBuffer> mb, const class Registry &) const = 0; }; -/// \brief An abstract class for handling alternate yaml representations +/// An abstract class for handling alternate yaml representations /// of object files. /// /// The YAML syntax allows "tags" which are used to specify the type of diff --git a/contrib/llvm/tools/lld/include/lld/Core/Resolver.h b/contrib/llvm/tools/lld/include/lld/Core/Resolver.h index fb62a779c0a5..5157c9fddc1a 100644 --- a/contrib/llvm/tools/lld/include/lld/Core/Resolver.h +++ b/contrib/llvm/tools/lld/include/lld/Core/Resolver.h @@ -28,7 +28,7 @@ namespace lld { class Atom; class LinkingContext; -/// \brief The Resolver is responsible for merging all input object files +/// The Resolver is responsible for merging all input object files /// and producing a merged graph. class Resolver { public: @@ -50,7 +50,7 @@ public: // Handle a shared library file. llvm::Error handleSharedLibrary(File &); - /// @brief do work of merging and resolving and return list + /// do work of merging and resolving and return list bool resolve(); std::unique_ptr<SimpleFile> resultFile() { return std::move(_result); } @@ -61,7 +61,7 @@ private: bool undefinesAdded(int begin, int end); File *getFile(int &index); - /// \brief The main function that iterates over the files to resolve + /// The main function that iterates over the files to resolve bool resolveUndefines(); void updateReferences(); void deadStripOptimize(); diff --git a/contrib/llvm/tools/lld/include/lld/Core/Simple.h b/contrib/llvm/tools/lld/include/lld/Core/Simple.h index 3aa7abf5d12b..feeed6ae473b 100644 --- a/contrib/llvm/tools/lld/include/lld/Core/Simple.h +++ b/contrib/llvm/tools/lld/include/lld/Core/Simple.h @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief Provide simple implementations for Atoms and File. +/// Provide simple implementations for Atoms and File. /// //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/lld/include/lld/Core/SymbolTable.h b/contrib/llvm/tools/lld/include/lld/Core/SymbolTable.h index 9c39a6ed507c..156c56eafbf7 100644 --- a/contrib/llvm/tools/lld/include/lld/Core/SymbolTable.h +++ b/contrib/llvm/tools/lld/include/lld/Core/SymbolTable.h @@ -12,7 +12,7 @@ #include "lld/Common/LLVM.h" #include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/DJB.h" #include <cstring> #include <map> #include <vector> @@ -27,35 +27,35 @@ class ResolverOptions; class SharedLibraryAtom; class UndefinedAtom; -/// \brief The SymbolTable class is responsible for coalescing atoms. +/// The SymbolTable class is responsible for coalescing atoms. /// /// All atoms coalescable by-name or by-content should be added. /// The method replacement() can be used to find the replacement atom /// if an atom has been coalesced away. class SymbolTable { public: - /// @brief add atom to symbol table + /// add atom to symbol table bool add(const DefinedAtom &); - /// @brief add atom to symbol table + /// add atom to symbol table bool add(const UndefinedAtom &); - /// @brief add atom to symbol table + /// add atom to symbol table bool add(const SharedLibraryAtom &); - /// @brief add atom to symbol table + /// add atom to symbol table bool add(const AbsoluteAtom &); - /// @brief returns atom in symbol table for specified name (or nullptr) + /// returns atom in symbol table for specified name (or nullptr) const Atom *findByName(StringRef sym); - /// @brief returns vector of remaining UndefinedAtoms + /// returns vector of remaining UndefinedAtoms std::vector<const UndefinedAtom *> undefines(); - /// @brief if atom has been coalesced away, return replacement, else return atom + /// if atom has been coalesced away, return replacement, else return atom const Atom *replacement(const Atom *); - /// @brief if atom has been coalesced away, return true + /// if atom has been coalesced away, return true bool isCoalescedAway(const Atom *); private: @@ -65,7 +65,7 @@ private: static StringRef getEmptyKey() { return StringRef(); } static StringRef getTombstoneKey() { return StringRef(" ", 1); } static unsigned getHashValue(StringRef const val) { - return llvm::HashString(val); + return llvm::djbHash(val, 0); } static bool isEqual(StringRef const lhs, StringRef const rhs) { return lhs.equals(rhs); diff --git a/contrib/llvm/tools/lld/include/lld/Core/TODO.txt b/contrib/llvm/tools/lld/include/lld/Core/TODO.txt index 8b523045de75..2aa61ff8612d 100644 --- a/contrib/llvm/tools/lld/include/lld/Core/TODO.txt +++ b/contrib/llvm/tools/lld/include/lld/Core/TODO.txt @@ -6,9 +6,6 @@ include/lld/Core abstraction only works for returning low level OS errors. It does not work for describing formatting issues. -* We need to design a diagnostics interface. It would be nice to share code - with Clang_ where possible. - * We need to add more attributes to File. In particular, we need cpu and OS information (like target triples). We should also provide explicit support for `LLVM IR module flags metadata`__. diff --git a/contrib/llvm/tools/lld/include/lld/Core/Writer.h b/contrib/llvm/tools/lld/include/lld/Core/Writer.h index 1f0ca4cda41f..1cdfabefebd7 100644 --- a/contrib/llvm/tools/lld/include/lld/Core/Writer.h +++ b/contrib/llvm/tools/lld/include/lld/Core/Writer.h @@ -20,17 +20,17 @@ class File; class LinkingContext; class MachOLinkingContext; -/// \brief The Writer is an abstract class for writing object files, shared +/// The Writer is an abstract class for writing object files, shared /// library files, and executable files. Each file format (e.g. mach-o, etc) /// has a concrete subclass of Writer. class Writer { public: virtual ~Writer(); - /// \brief Write a file from the supplied File object + /// Write a file from the supplied File object virtual llvm::Error writeFile(const File &linkedFile, StringRef path) = 0; - /// \brief This method is called by Core Linking to give the Writer a chance + /// This method is called by Core Linking to give the Writer a chance /// to add file format specific "files" to set of files to be linked. This is /// how file format specific atoms can be added to the link. virtual void createImplicitFiles(std::vector<std::unique_ptr<File>> &) {} diff --git a/contrib/llvm/tools/lld/include/lld/ReaderWriter/MachOLinkingContext.h b/contrib/llvm/tools/lld/include/lld/ReaderWriter/MachOLinkingContext.h index 9eefa8c4d944..fde65880c3e3 100644 --- a/contrib/llvm/tools/lld/include/lld/ReaderWriter/MachOLinkingContext.h +++ b/contrib/llvm/tools/lld/include/lld/ReaderWriter/MachOLinkingContext.h @@ -89,7 +89,7 @@ public: bool exportDynamicSymbols); void addPasses(PassManager &pm) override; - bool validateImpl(raw_ostream &diagnostics) override; + bool validateImpl() override; std::string demangle(StringRef symbolName) const override; void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; @@ -201,7 +201,7 @@ public: uint32_t swiftVersion() const { return _swiftVersion; } - /// \brief Checks whether a given path on the filesystem exists. + /// Checks whether a given path on the filesystem exists. /// /// When running in -test_file_usage mode, this method consults an /// internally maintained list of files that exist (provided by -path_exists) @@ -211,7 +211,7 @@ public: /// Like pathExists() but only used on files - not directories. bool fileExists(StringRef path) const; - /// \brief Adds any library search paths derived from the given base, possibly + /// Adds any library search paths derived from the given base, possibly /// modified by -syslibroots. /// /// The set of paths added consists of approximately all syslibroot-prepended @@ -219,7 +219,7 @@ public: /// for whatever reason. With various edge-cases for compatibility. void addModifiedSearchDir(StringRef libPath, bool isSystemPath = false); - /// \brief Determine whether -lFoo can be resolve within the given path, and + /// Determine whether -lFoo can be resolve within the given path, and /// return the filename if so. /// /// The -lFoo option is documented to search for libFoo.dylib and libFoo.a in @@ -228,7 +228,7 @@ public: llvm::Optional<StringRef> searchDirForLibrary(StringRef path, StringRef libName) const; - /// \brief Iterates through all search path entries looking for libName (as + /// Iterates through all search path entries looking for libName (as /// specified by -lFoo). llvm::Optional<StringRef> searchLibrary(StringRef libName) const; @@ -236,11 +236,11 @@ public: /// the path with syslibroot. void addFrameworkSearchDir(StringRef fwPath, bool isSystemPath = false); - /// \brief Iterates through all framework directories looking for + /// Iterates through all framework directories looking for /// Foo.framework/Foo (when fwName = "Foo"). llvm::Optional<StringRef> findPathForFramework(StringRef fwName) const; - /// \brief The dylib's binary compatibility version, in the raw uint32 format. + /// The dylib's binary compatibility version, in the raw uint32 format. /// /// When building a dynamic library, this is the compatibility version that /// gets embedded into the result. Other Mach-O binaries that link against @@ -249,28 +249,28 @@ public: /// installed dynamic library. uint32_t compatibilityVersion() const { return _compatibilityVersion; } - /// \brief The dylib's current version, in the the raw uint32 format. + /// The dylib's current version, in the the raw uint32 format. /// /// When building a dynamic library, this is the current version that gets /// embedded into the result. Other Mach-O binaries that link against /// this library will store the compatibility version in its load command. uint32_t currentVersion() const { return _currentVersion; } - /// \brief The dylib's install name. + /// The dylib's install name. /// /// Binaries that link against the dylib will embed this path into the dylib /// load command. When loading the binaries at runtime, this is the location /// on disk that the loader will look for the dylib. StringRef installName() const { return _installName; } - /// \brief Whether or not the dylib has side effects during initialization. + /// Whether or not the dylib has side effects during initialization. /// /// Dylibs marked as being dead strippable provide the guarantee that loading /// the dylib has no side effects, allowing the linker to strip out the dylib /// when linking a binary that does not use any of its symbols. bool deadStrippableDylib() const { return _deadStrippableDylib; } - /// \brief Whether or not to use flat namespace. + /// Whether or not to use flat namespace. /// /// MachO usually uses a two-level namespace, where each external symbol /// referenced by the target is associated with the dylib that will provide @@ -282,7 +282,7 @@ public: /// loaded flat_namespace dylibs must be resolvable at build time. bool useFlatNamespace() const { return _flatNamespace; } - /// \brief How to handle undefined symbols. + /// How to handle undefined symbols. /// /// Options are: /// * error: Report an error and terminate linking. @@ -294,7 +294,7 @@ public: /// runtime. UndefinedMode undefinedMode() const { return _undefinedMode; } - /// \brief The path to the executable that will load the bundle at runtime. + /// The path to the executable that will load the bundle at runtime. /// /// When building a Mach-O bundle, this executable will be examined if there /// are undefined symbols after the main link phase. It is expected that this @@ -331,7 +331,7 @@ public: /// Add section alignment constraint on final layout. void addSectionAlignment(StringRef seg, StringRef sect, uint16_t align); - /// \brief Add a section based on a command-line sectcreate option. + /// Add a section based on a command-line sectcreate option. void addSectCreateSection(StringRef seg, StringRef sect, std::unique_ptr<MemoryBuffer> content); diff --git a/contrib/llvm/tools/lld/lib/Core/LinkingContext.cpp b/contrib/llvm/tools/lld/lib/Core/LinkingContext.cpp index 5de863aa7f37..0f225c322122 100644 --- a/contrib/llvm/tools/lld/lib/Core/LinkingContext.cpp +++ b/contrib/llvm/tools/lld/lib/Core/LinkingContext.cpp @@ -20,8 +20,8 @@ LinkingContext::LinkingContext() = default; LinkingContext::~LinkingContext() = default; -bool LinkingContext::validate(raw_ostream &diagnostics) { - return validateImpl(diagnostics); +bool LinkingContext::validate() { + return validateImpl(); } llvm::Error LinkingContext::writeFile(const File &linkedFile) const { diff --git a/contrib/llvm/tools/lld/lib/Driver/CMakeLists.txt b/contrib/llvm/tools/lld/lib/Driver/CMakeLists.txt index 097a8177ea1f..ff67c282f47e 100644 --- a/contrib/llvm/tools/lld/lib/Driver/CMakeLists.txt +++ b/contrib/llvm/tools/lld/lib/Driver/CMakeLists.txt @@ -13,6 +13,7 @@ add_lld_library(lldDriver Support LINK_LIBS + lldCommon lldCore lldMachO lldReaderWriter diff --git a/contrib/llvm/tools/lld/lib/Driver/DarwinLdDriver.cpp b/contrib/llvm/tools/lld/lib/Driver/DarwinLdDriver.cpp index a019e9c32800..ad22845207e1 100644 --- a/contrib/llvm/tools/lld/lib/Driver/DarwinLdDriver.cpp +++ b/contrib/llvm/tools/lld/lib/Driver/DarwinLdDriver.cpp @@ -13,6 +13,8 @@ /// //===----------------------------------------------------------------------===// +#include "lld/Common/Args.h" +#include "lld/Common/ErrorHandler.h" #include "lld/Common/LLVM.h" #include "lld/Core/ArchiveLibraryFile.h" #include "lld/Core/Error.h" @@ -110,11 +112,11 @@ parseMemberFiles(std::unique_ptr<File> file) { return members; } -std::vector<std::unique_ptr<File>> -loadFile(MachOLinkingContext &ctx, StringRef path, - raw_ostream &diag, bool wholeArchive, bool upwardDylib) { +std::vector<std::unique_ptr<File>> loadFile(MachOLinkingContext &ctx, + StringRef path, bool wholeArchive, + bool upwardDylib) { if (ctx.logInputFiles()) - diag << path << "\n"; + message(path); ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr = ctx.getMemoryBuffer(path); if (std::error_code ec = mbOrErr.getError()) @@ -155,10 +157,9 @@ static std::string canonicalizePath(StringRef path) { } static void addFile(StringRef path, MachOLinkingContext &ctx, - bool loadWholeArchive, - bool upwardDylib, raw_ostream &diag) { + bool loadWholeArchive, bool upwardDylib) { std::vector<std::unique_ptr<File>> files = - loadFile(ctx, path, diag, loadWholeArchive, upwardDylib); + loadFile(ctx, path, loadWholeArchive, upwardDylib); for (std::unique_ptr<File> &file : files) ctx.getNodes().push_back(llvm::make_unique<FileNode>(std::move(file))); } @@ -166,8 +167,7 @@ static void addFile(StringRef path, MachOLinkingContext &ctx, // Export lists are one symbol per line. Blank lines are ignored. // Trailing comments start with #. static std::error_code parseExportsList(StringRef exportFilePath, - MachOLinkingContext &ctx, - raw_ostream &diagnostics) { + MachOLinkingContext &ctx) { // Map in export list file. ErrorOr<std::unique_ptr<MemoryBuffer>> mb = MemoryBuffer::getFileOrSTDIN(exportFilePath); @@ -197,8 +197,7 @@ static std::error_code parseExportsList(StringRef exportFilePath, /// libfrob.a(bar.o):_bar /// x86_64:_foo64 static std::error_code parseOrderFile(StringRef orderFilePath, - MachOLinkingContext &ctx, - raw_ostream &diagnostics) { + MachOLinkingContext &ctx) { // Map in order file. ErrorOr<std::unique_ptr<MemoryBuffer>> mb = MemoryBuffer::getFileOrSTDIN(orderFilePath); @@ -252,8 +251,7 @@ static std::error_code parseOrderFile(StringRef orderFilePath, // per line. The <dir> prefix is prepended to each partial path. // static llvm::Error loadFileList(StringRef fileListPath, - MachOLinkingContext &ctx, bool forceLoad, - raw_ostream &diagnostics) { + MachOLinkingContext &ctx, bool forceLoad) { // If there is a comma, split off <dir>. std::pair<StringRef, StringRef> opt = fileListPath.split(','); StringRef filePath = opt.first; @@ -286,9 +284,9 @@ static llvm::Error loadFileList(StringRef fileListPath, + "'"); } if (ctx.testingFileUsage()) { - diagnostics << "Found filelist entry " << canonicalizePath(path) << '\n'; + message("Found filelist entry " + canonicalizePath(path)); } - addFile(path, ctx, forceLoad, false, diagnostics); + addFile(path, ctx, forceLoad, false); buffer = lineAndRest.second; } return llvm::Error::success(); @@ -317,8 +315,7 @@ static void parseLLVMOptions(const LinkingContext &ctx) { namespace lld { namespace mach_o { -bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, - raw_ostream &diagnostics) { +bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx) { // Parse command line options using DarwinLdOptions.td DarwinLdOptTable table; unsigned missingIndex; @@ -326,17 +323,20 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, llvm::opt::InputArgList parsedArgs = table.ParseArgs(args.slice(1), missingIndex, missingCount); if (missingCount) { - diagnostics << "error: missing arg value for '" - << parsedArgs.getArgString(missingIndex) << "' expected " - << missingCount << " argument(s).\n"; + error("missing arg value for '" + + Twine(parsedArgs.getArgString(missingIndex)) + "' expected " + + Twine(missingCount) + " argument(s)."); return false; } for (auto unknownArg : parsedArgs.filtered(OPT_UNKNOWN)) { - diagnostics << "warning: ignoring unknown argument: " - << unknownArg->getAsString(parsedArgs) << "\n"; + warn("ignoring unknown argument: " + + Twine(unknownArg->getAsString(parsedArgs))); } + errorHandler().Verbose = parsedArgs.hasArg(OPT_v); + errorHandler().ErrorLimit = args::getInteger(parsedArgs, OPT_error_limit, 20); + // Figure out output kind ( -dylib, -r, -bundle, -preload, or -static ) llvm::MachO::HeaderFileType fileType = llvm::MachO::MH_EXECUTE; bool isStaticExecutable = false; @@ -367,8 +367,7 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, if (llvm::opt::Arg *archStr = parsedArgs.getLastArg(OPT_arch)) { arch = MachOLinkingContext::archFromName(archStr->getValue()); if (arch == MachOLinkingContext::arch_unknown) { - diagnostics << "error: unknown arch named '" << archStr->getValue() - << "'\n"; + error("unknown arch named '" + Twine(archStr->getValue()) + "'"); return false; } } @@ -386,7 +385,7 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, if (parsedArgs.size() == 0) table.PrintHelp(llvm::outs(), args[0], "LLVM Linker", false); else - diagnostics << "error: -arch not specified and could not be inferred\n"; + error("-arch not specified and could not be inferred"); return false; } } @@ -402,7 +401,7 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, os = MachOLinkingContext::OS::macOSX; if (MachOLinkingContext::parsePackedVersion(minOS->getValue(), minOSVersion)) { - diagnostics << "error: malformed macosx_version_min value\n"; + error("malformed macosx_version_min value"); return false; } break; @@ -410,7 +409,7 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, os = MachOLinkingContext::OS::iOS; if (MachOLinkingContext::parsePackedVersion(minOS->getValue(), minOSVersion)) { - diagnostics << "error: malformed ios_version_min value\n"; + error("malformed ios_version_min value"); return false; } break; @@ -418,7 +417,7 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, os = MachOLinkingContext::OS::iOS_simulator; if (MachOLinkingContext::parsePackedVersion(minOS->getValue(), minOSVersion)) { - diagnostics << "error: malformed ios_simulator_version_min value\n"; + error("malformed ios_simulator_version_min value"); return false; } break; @@ -452,14 +451,14 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, if (llvm::opt::Arg *imageBase = parsedArgs.getLastArg(OPT_image_base)) { uint64_t baseAddress; if (parseNumberBase16(imageBase->getValue(), baseAddress)) { - diagnostics << "error: image_base expects a hex number\n"; + error("image_base expects a hex number"); return false; } else if (baseAddress < ctx.pageZeroSize()) { - diagnostics << "error: image_base overlaps with __PAGEZERO\n"; + error("image_base overlaps with __PAGEZERO"); return false; } else if (baseAddress % ctx.pageSize()) { - diagnostics << "error: image_base must be a multiple of page size (" - << "0x" << llvm::utohexstr(ctx.pageSize()) << ")\n"; + error("image_base must be a multiple of page size (0x" + + llvm::utohexstr(ctx.pageSize()) + ")"); return false; } @@ -488,13 +487,12 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, // Handle -compatibility_version and -current_version if (llvm::opt::Arg *vers = parsedArgs.getLastArg(OPT_compatibility_version)) { if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) { - diagnostics - << "error: -compatibility_version can only be used with -dylib\n"; + error("-compatibility_version can only be used with -dylib"); return false; } uint32_t parsedVers; if (MachOLinkingContext::parsePackedVersion(vers->getValue(), parsedVers)) { - diagnostics << "error: -compatibility_version value is malformed\n"; + error("-compatibility_version value is malformed"); return false; } ctx.setCompatibilityVersion(parsedVers); @@ -502,12 +500,12 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, if (llvm::opt::Arg *vers = parsedArgs.getLastArg(OPT_current_version)) { if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) { - diagnostics << "-current_version can only be used with -dylib\n"; + error("-current_version can only be used with -dylib"); return false; } uint32_t parsedVers; if (MachOLinkingContext::parsePackedVersion(vers->getValue(), parsedVers)) { - diagnostics << "error: -current_version value is malformed\n"; + error("-current_version value is malformed"); return false; } ctx.setCurrentVersion(parsedVers); @@ -526,17 +524,19 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, alignStr += 2; unsigned long long alignValue; if (llvm::getAsUnsignedInteger(alignStr, 16, alignValue)) { - diagnostics << "error: -sectalign alignment value '" - << alignStr << "' not a valid number\n"; + error("-sectalign alignment value '" + Twine(alignStr) + + "' not a valid number"); return false; } uint16_t align = 1 << llvm::countTrailingZeros(alignValue); if (!llvm::isPowerOf2_64(alignValue)) { - diagnostics << "warning: alignment for '-sectalign " - << segName << " " << sectName - << llvm::format(" 0x%llX", alignValue) - << "' is not a power of two, using " - << llvm::format("0x%08X", align) << "\n"; + std::string Msg; + llvm::raw_string_ostream OS(Msg); + OS << "alignment for '-sectalign " << segName << " " << sectName + << llvm::format(" 0x%llX", alignValue) + << "' is not a power of two, using " << llvm::format("0x%08X", align); + OS.flush(); + warn(Msg); } ctx.addSectionAlignment(segName, sectName, align); } @@ -562,18 +562,14 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, if (parsedArgs.getLastArg(OPT_keep_private_externs)) { ctx.setKeepPrivateExterns(true); if (ctx.outputMachOType() != llvm::MachO::MH_OBJECT) - diagnostics << "warning: -keep_private_externs only used in -r mode\n"; + warn("-keep_private_externs only used in -r mode"); } // Handle -dependency_info <path> used by Xcode. - if (llvm::opt::Arg *depInfo = parsedArgs.getLastArg(OPT_dependency_info)) { - if (std::error_code ec = ctx.createDependencyFile(depInfo->getValue())) { - diagnostics << "warning: " << ec.message() - << ", processing '-dependency_info " - << depInfo->getValue() - << "'\n"; - } - } + if (llvm::opt::Arg *depInfo = parsedArgs.getLastArg(OPT_dependency_info)) + if (std::error_code ec = ctx.createDependencyFile(depInfo->getValue())) + warn(ec.message() + ", processing '-dependency_info " + + depInfo->getValue()); // In -test_file_usage mode, we'll be given an explicit list of paths that // exist. We'll also be expected to print out information about how we located @@ -639,31 +635,28 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, // Now that we've constructed the final set of search paths, print out those // search paths in verbose mode. - if (parsedArgs.getLastArg(OPT_v)) { - diagnostics << "Library search paths:\n"; + if (errorHandler().Verbose) { + message("Library search paths:"); for (auto path : ctx.searchDirs()) { - diagnostics << " " << path << '\n'; + message(" " + path); } - diagnostics << "Framework search paths:\n"; + message("Framework search paths:"); for (auto path : ctx.frameworkDirs()) { - diagnostics << " " << path << '\n'; + message(" " + path); } } // Handle -exported_symbols_list <file> for (auto expFile : parsedArgs.filtered(OPT_exported_symbols_list)) { if (ctx.exportMode() == MachOLinkingContext::ExportMode::blackList) { - diagnostics << "error: -exported_symbols_list cannot be combined " - << "with -unexported_symbol[s_list]\n"; - return false; + error("-exported_symbols_list cannot be combined with " + "-unexported_symbol[s_list]"); + return false; } ctx.setExportMode(MachOLinkingContext::ExportMode::whiteList); - if (std::error_code ec = parseExportsList(expFile->getValue(), ctx, - diagnostics)) { - diagnostics << "error: " << ec.message() - << ", processing '-exported_symbols_list " - << expFile->getValue() - << "'\n"; + if (std::error_code ec = parseExportsList(expFile->getValue(), ctx)) { + error(ec.message() + ", processing '-exported_symbols_list " + + expFile->getValue()); return false; } } @@ -671,9 +664,9 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, // Handle -exported_symbol <symbol> for (auto symbol : parsedArgs.filtered(OPT_exported_symbol)) { if (ctx.exportMode() == MachOLinkingContext::ExportMode::blackList) { - diagnostics << "error: -exported_symbol cannot be combined " - << "with -unexported_symbol[s_list]\n"; - return false; + error("-exported_symbol cannot be combined with " + "-unexported_symbol[s_list]"); + return false; } ctx.setExportMode(MachOLinkingContext::ExportMode::whiteList); ctx.addExportSymbol(symbol->getValue()); @@ -682,17 +675,14 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, // Handle -unexported_symbols_list <file> for (auto expFile : parsedArgs.filtered(OPT_unexported_symbols_list)) { if (ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) { - diagnostics << "error: -unexported_symbols_list cannot be combined " - << "with -exported_symbol[s_list]\n"; - return false; + error("-unexported_symbols_list cannot be combined with " + "-exported_symbol[s_list]"); + return false; } ctx.setExportMode(MachOLinkingContext::ExportMode::blackList); - if (std::error_code ec = parseExportsList(expFile->getValue(), ctx, - diagnostics)) { - diagnostics << "error: " << ec.message() - << ", processing '-unexported_symbols_list " - << expFile->getValue() - << "'\n"; + if (std::error_code ec = parseExportsList(expFile->getValue(), ctx)) { + error(ec.message() + ", processing '-unexported_symbols_list " + + expFile->getValue()); return false; } } @@ -700,9 +690,9 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, // Handle -unexported_symbol <symbol> for (auto symbol : parsedArgs.filtered(OPT_unexported_symbol)) { if (ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) { - diagnostics << "error: -unexported_symbol cannot be combined " - << "with -exported_symbol[s_list]\n"; - return false; + error("-unexported_symbol cannot be combined with " + "-exported_symbol[s_list]"); + return false; } ctx.setExportMode(MachOLinkingContext::ExportMode::blackList); ctx.addExportSymbol(symbol->getValue()); @@ -711,30 +701,26 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, // Handle obosolete -multi_module and -single_module if (llvm::opt::Arg *mod = parsedArgs.getLastArg(OPT_multi_module, OPT_single_module)) { - if (mod->getOption().getID() == OPT_multi_module) { - diagnostics << "warning: -multi_module is obsolete and being ignored\n"; - } - else { - if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) { - diagnostics << "warning: -single_module being ignored. " - "It is only for use when producing a dylib\n"; - } - } + if (mod->getOption().getID() == OPT_multi_module) + warn("-multi_module is obsolete and being ignored"); + else if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) + warn("-single_module being ignored. It is only for use when producing a " + "dylib"); } // Handle obsolete ObjC options: -objc_gc_compaction, -objc_gc, -objc_gc_only if (parsedArgs.getLastArg(OPT_objc_gc_compaction)) { - diagnostics << "error: -objc_gc_compaction is not supported\n"; + error("-objc_gc_compaction is not supported"); return false; } if (parsedArgs.getLastArg(OPT_objc_gc)) { - diagnostics << "error: -objc_gc is not supported\n"; + error("-objc_gc is not supported"); return false; } if (parsedArgs.getLastArg(OPT_objc_gc_only)) { - diagnostics << "error: -objc_gc_only is not supported\n"; + error("-objc_gc_only is not supported"); return false; } @@ -746,22 +732,20 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, case MachOLinkingContext::OS::macOSX: if ((minOSVersion < 0x000A0500) && (pie->getOption().getID() == OPT_pie)) { - diagnostics << "-pie can only be used when targeting " - "Mac OS X 10.5 or later\n"; + error("-pie can only be used when targeting Mac OS X 10.5 or later"); return false; } break; case MachOLinkingContext::OS::iOS: if ((minOSVersion < 0x00040200) && (pie->getOption().getID() == OPT_pie)) { - diagnostics << "-pie can only be used when targeting " - "iOS 4.2 or later\n"; + error("-pie can only be used when targeting iOS 4.2 or later"); return false; } break; case MachOLinkingContext::OS::iOS_simulator: if (pie->getOption().getID() == OPT_no_pie) { - diagnostics << "iOS simulator programs must be built PIE\n"; + error("iOS simulator programs must be built PIE"); return false; } break; @@ -774,12 +758,12 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, break; case llvm::MachO::MH_DYLIB: case llvm::MachO::MH_BUNDLE: - diagnostics << "warning: " << pie->getSpelling() << " being ignored. " - << "It is only used when linking main executables\n"; + warn(pie->getSpelling() + + " being ignored. It is only used when linking main executables"); break; default: - diagnostics << pie->getSpelling() - << " can only used when linking main executables\n"; + error(pie->getSpelling() + + " can only used when linking main executables"); return false; } } @@ -934,7 +918,7 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, uint32_t sdkVersion = 0; if (MachOLinkingContext::parsePackedVersion(arg->getValue(), sdkVersion)) { - diagnostics << "error: malformed sdkVersion value\n"; + error("malformed sdkVersion value"); return false; } ctx.setSdkVersion(sdkVersion); @@ -943,9 +927,8 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, // with min_version, then we need to give an warning as we have no sdk // version to put in that command. // FIXME: We need to decide whether to make this an error. - diagnostics << "warning: -sdk_version is required when emitting " - "min version load command. " - "Setting sdk version to match provided min version\n"; + warn("-sdk_version is required when emitting min version load command. " + "Setting sdk version to match provided min version"); ctx.setSdkVersion(ctx.osMinVersion()); } @@ -954,7 +937,7 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, uint64_t version = 0; if (MachOLinkingContext::parsePackedVersion(arg->getValue(), version)) { - diagnostics << "error: malformed source_version value\n"; + error("malformed source_version value"); return false; } ctx.setSourceVersion(version); @@ -964,12 +947,12 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, if (llvm::opt::Arg *stackSize = parsedArgs.getLastArg(OPT_stack_size)) { uint64_t stackSizeVal; if (parseNumberBase16(stackSize->getValue(), stackSizeVal)) { - diagnostics << "error: stack_size expects a hex number\n"; + error("stack_size expects a hex number"); return false; } if ((stackSizeVal % ctx.pageSize()) != 0) { - diagnostics << "error: stack_size must be a multiple of page size (" - << "0x" << llvm::utohexstr(ctx.pageSize()) << ")\n"; + error("stack_size must be a multiple of page size (0x" + + llvm::utohexstr(ctx.pageSize()) + ")"); return false; } @@ -982,12 +965,9 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, // Handle -order_file <file> for (auto orderFile : parsedArgs.filtered(OPT_order_file)) { - if (std::error_code ec = parseOrderFile(orderFile->getValue(), ctx, - diagnostics)) { - diagnostics << "error: " << ec.message() - << ", processing '-order_file " - << orderFile->getValue() - << "'\n"; + if (std::error_code ec = parseOrderFile(orderFile->getValue(), ctx)) { + error(ec.message() + ", processing '-order_file " + orderFile->getValue() + + "'"); return false; } } @@ -1011,8 +991,8 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, else if (StringRef(undef->getValue()).equals("dynamic_lookup")) UndefMode = MachOLinkingContext::UndefinedMode::dynamicLookup; else { - diagnostics << "error: invalid option to -undefined " - "[ warning | error | suppress | dynamic_lookup ]\n"; + error("invalid option to -undefined [ warning | error | suppress | " + "dynamic_lookup ]"); return false; } @@ -1026,8 +1006,8 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, // illegal. Emit a diagnostic if they've been (mis)used. if (UndefMode == MachOLinkingContext::UndefinedMode::warning || UndefMode == MachOLinkingContext::UndefinedMode::suppress) { - diagnostics << "error: can't use -undefined warning or suppress with " - "-twolevel_namespace\n"; + error("can't use -undefined warning or suppress with " + "-twolevel_namespace"); return false; } } @@ -1046,19 +1026,16 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, case llvm::MachO::MH_DYLIB: case llvm::MachO::MH_BUNDLE: if (!ctx.minOS("10.5", "2.0")) { - if (ctx.os() == MachOLinkingContext::OS::macOSX) { - diagnostics << "error: -rpath can only be used when targeting " - "OS X 10.5 or later\n"; - } else { - diagnostics << "error: -rpath can only be used when targeting " - "iOS 2.0 or later\n"; - } + if (ctx.os() == MachOLinkingContext::OS::macOSX) + error("-rpath can only be used when targeting OS X 10.5 or later"); + else + error("-rpath can only be used when targeting iOS 2.0 or later"); return false; } break; default: - diagnostics << "error: -rpath can only be used when creating " - "a dynamic final linked image\n"; + error("-rpath can only be used when creating a dynamic final linked " + "image"); return false; } @@ -1068,7 +1045,7 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, } // Parse the LLVM options before we process files in case the file handling - // makes use of things like DEBUG(). + // makes use of things like LLVM_DEBUG(). parseLLVMOptions(ctx); // Handle input files and sectcreate. @@ -1079,52 +1056,46 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, default: continue; case OPT_INPUT: - addFile(arg->getValue(), ctx, globalWholeArchive, false, diagnostics); + addFile(arg->getValue(), ctx, globalWholeArchive, false); break; case OPT_upward_library: - addFile(arg->getValue(), ctx, false, true, diagnostics); + addFile(arg->getValue(), ctx, false, true); break; case OPT_force_load: - addFile(arg->getValue(), ctx, true, false, diagnostics); + addFile(arg->getValue(), ctx, true, false); break; case OPT_l: case OPT_upward_l: upward = (arg->getOption().getID() == OPT_upward_l); resolvedPath = ctx.searchLibrary(arg->getValue()); if (!resolvedPath) { - diagnostics << "Unable to find library for " << arg->getSpelling() - << arg->getValue() << "\n"; + error("Unable to find library for " + arg->getSpelling() + + arg->getValue()); return false; } else if (ctx.testingFileUsage()) { - diagnostics << "Found " << (upward ? "upward " : " ") << "library " - << canonicalizePath(resolvedPath.getValue()) << '\n'; + message(Twine("Found ") + (upward ? "upward " : " ") + "library " + + canonicalizePath(resolvedPath.getValue())); } - addFile(resolvedPath.getValue(), ctx, globalWholeArchive, - upward, diagnostics); + addFile(resolvedPath.getValue(), ctx, globalWholeArchive, upward); break; case OPT_framework: case OPT_upward_framework: upward = (arg->getOption().getID() == OPT_upward_framework); resolvedPath = ctx.findPathForFramework(arg->getValue()); if (!resolvedPath) { - diagnostics << "Unable to find framework for " - << arg->getSpelling() << " " << arg->getValue() << "\n"; + error("Unable to find framework for " + arg->getSpelling() + " " + + arg->getValue()); return false; } else if (ctx.testingFileUsage()) { - diagnostics << "Found " << (upward ? "upward " : " ") << "framework " - << canonicalizePath(resolvedPath.getValue()) << '\n'; + message(Twine("Found ") + (upward ? "upward " : " ") + "framework " + + canonicalizePath(resolvedPath.getValue())); } - addFile(resolvedPath.getValue(), ctx, globalWholeArchive, - upward, diagnostics); + addFile(resolvedPath.getValue(), ctx, globalWholeArchive, upward); break; case OPT_filelist: - if (auto ec = loadFileList(arg->getValue(), - ctx, globalWholeArchive, - diagnostics)) { + if (auto ec = loadFileList(arg->getValue(), ctx, globalWholeArchive)) { handleAllErrors(std::move(ec), [&](const llvm::ErrorInfoBase &EI) { - diagnostics << "error: "; - EI.log(diagnostics); - diagnostics << ", processing '-filelist " << arg->getValue() << "'\n"; + error(EI.message() + ", processing '-filelist " + arg->getValue()); }); return false; } @@ -1138,7 +1109,7 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, MemoryBuffer::getFile(fileName); if (!contentOrErr) { - diagnostics << "error: can't open -sectcreate file " << fileName << "\n"; + error("can't open -sectcreate file " + Twine(fileName)); return false; } @@ -1149,12 +1120,12 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx, } if (ctx.getNodes().empty()) { - diagnostics << "No input files\n"; + error("No input files"); return false; } // Validate the combination of options used. - return ctx.validate(diagnostics); + return ctx.validate(); } static void createFiles(MachOLinkingContext &ctx, bool Implicit) { @@ -1170,9 +1141,18 @@ static void createFiles(MachOLinkingContext &ctx, bool Implicit) { } /// This is where the link is actually performed. -bool link(llvm::ArrayRef<const char *> args, raw_ostream &diagnostics) { +bool link(llvm::ArrayRef<const char *> args, bool CanExitEarly, + raw_ostream &Error) { + errorHandler().LogName = llvm::sys::path::filename(args[0]); + errorHandler().ErrorLimitExceededMsg = + "too many errors emitted, stopping now (use " + "'-error-limit 0' to see all errors)"; + errorHandler().ErrorOS = &Error; + errorHandler().ExitEarly = CanExitEarly; + errorHandler().ColorDiagnostics = Error.has_colors(); + MachOLinkingContext ctx; - if (!parse(args, ctx, diagnostics)) + if (!parse(args, ctx)) return false; if (ctx.doNothing()) return true; @@ -1214,9 +1194,10 @@ bool link(llvm::ArrayRef<const char *> args, raw_ostream &diagnostics) { if (auto ec = pm.runOnFile(*merged)) { // FIXME: This should be passed to logAllUnhandledErrors but it needs // to be passed a Twine instead of a string. - diagnostics << "Failed to run passes on file '" << ctx.outputPath() - << "': "; - logAllUnhandledErrors(std::move(ec), diagnostics, std::string()); + *errorHandler().ErrorOS << "Failed to run passes on file '" + << ctx.outputPath() << "': "; + logAllUnhandledErrors(std::move(ec), *errorHandler().ErrorOS, + std::string()); return false; } @@ -1227,11 +1208,18 @@ bool link(llvm::ArrayRef<const char *> args, raw_ostream &diagnostics) { if (auto ec = ctx.writeFile(*merged)) { // FIXME: This should be passed to logAllUnhandledErrors but it needs // to be passed a Twine instead of a string. - diagnostics << "Failed to write file '" << ctx.outputPath() << "': "; - logAllUnhandledErrors(std::move(ec), diagnostics, std::string()); + *errorHandler().ErrorOS << "Failed to write file '" << ctx.outputPath() + << "': "; + logAllUnhandledErrors(std::move(ec), *errorHandler().ErrorOS, + std::string()); return false; } + // Call exit() if we can to avoid calling destructors. + if (CanExitEarly) + exitLld(errorCount() ? 1 : 0); + + return true; } diff --git a/contrib/llvm/tools/lld/lib/Driver/DarwinLdOptions.td b/contrib/llvm/tools/lld/lib/Driver/DarwinLdOptions.td index fa07f33646e7..3bbde8bf1c1c 100644 --- a/contrib/llvm/tools/lld/lib/Driver/DarwinLdOptions.td +++ b/contrib/llvm/tools/lld/lib/Driver/DarwinLdOptions.td @@ -227,6 +227,14 @@ def t : Flag<["-"], "t">, HelpText<"Print the names of the input files as ld processes them">; def v : Flag<["-"], "v">, HelpText<"Print linker information">; +def error_limit : Separate<["-", "--"], "error-limit">, + MetaVarName<"<number>">, + HelpText<"Maximum number of errors to emit before stopping (0 = no limit)">; + +// Ignored options +def lto_library : Separate<["-"], "lto_library">, + MetaVarName<"<path>">, + HelpText<"Ignored for compatibility with other linkers">; // Obsolete options def grp_obsolete : OptionGroup<"obsolete">, HelpText<"OBSOLETE OPTIONS">; diff --git a/contrib/llvm/tools/lld/lib/ReaderWriter/FileArchive.cpp b/contrib/llvm/tools/lld/lib/ReaderWriter/FileArchive.cpp index 04c0bee76989..2f52d9d34312 100644 --- a/contrib/llvm/tools/lld/lib/ReaderWriter/FileArchive.cpp +++ b/contrib/llvm/tools/lld/lib/ReaderWriter/FileArchive.cpp @@ -38,7 +38,7 @@ namespace lld { namespace { -/// \brief The FileArchive class represents an Archive Library file +/// The FileArchive class represents an Archive Library file class FileArchive : public lld::ArchiveLibraryFile { public: FileArchive(std::unique_ptr<MemoryBuffer> mb, const Registry ®, @@ -46,7 +46,7 @@ public: : ArchiveLibraryFile(path), _mb(std::shared_ptr<MemoryBuffer>(mb.release())), _registry(reg), _logLoading(logLoading) {} - /// \brief Check if any member of the archive contains an Atom with the + /// Check if any member of the archive contains an Atom with the /// specified name and return the File object for that member, or nullptr. File *find(StringRef name) override { auto member = _symbolMemberMap.find(name); @@ -77,7 +77,7 @@ public: return file; } - /// \brief parse each member + /// parse each member std::error_code parseAllMembers(std::vector<std::unique_ptr<File>> &result) override { if (std::error_code ec = parse()) diff --git a/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp b/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp index aee9959ca6b8..8cb6710857e3 100644 --- a/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp +++ b/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp @@ -674,7 +674,7 @@ void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref, *loc32 = ref.addend() + inAtomAddress - fixupAddress; return; case delta32Anon: - // The value we write here should be the the delta to the target + // The value we write here should be the delta to the target // after taking in to account the difference from the fixup back to the // last defined label // ie, if we have: @@ -690,7 +690,7 @@ void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref, *loc64 = ref.addend() + inAtomAddress - fixupAddress; return; case delta64Anon: - // The value we write here should be the the delta to the target + // The value we write here should be the delta to the target // after taking in to account the difference from the fixup back to the // last defined label // ie, if we have: diff --git a/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/CMakeLists.txt b/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/CMakeLists.txt index f2fc34772496..37d1de432c0f 100644 --- a/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/CMakeLists.txt +++ b/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/CMakeLists.txt @@ -26,6 +26,7 @@ add_lld_library(lldMachO Support LINK_LIBS + lldCommon lldCore lldYAML ${LLVM_PTHREAD_LIB} diff --git a/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/CompactUnwindPass.cpp b/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/CompactUnwindPass.cpp index 1e210409237f..fa0aaa103eeb 100644 --- a/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/CompactUnwindPass.cpp +++ b/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/CompactUnwindPass.cpp @@ -282,7 +282,7 @@ public: private: llvm::Error perform(SimpleFile &mergedFile) override { - DEBUG(llvm::dbgs() << "MachO Compact Unwind pass\n"); + LLVM_DEBUG(llvm::dbgs() << "MachO Compact Unwind pass\n"); std::map<const Atom *, CompactUnwindEntry> unwindLocs; std::map<const Atom *, const Atom *> dwarfFrames; @@ -319,7 +319,7 @@ private: // Finally, we can start creating pages based on these entries. - DEBUG(llvm::dbgs() << " Splitting entries into pages\n"); + LLVM_DEBUG(llvm::dbgs() << " Splitting entries into pages\n"); // FIXME: we split the entries into pages naively: lots of 4k pages followed // by a small one. ld64 tried to minimize space and align them to real 4k // boundaries. That might be worth doing, or perhaps we could perform some @@ -336,11 +336,13 @@ private: pages.back().entries = remainingInfos.slice(0, entriesInPage); remainingInfos = remainingInfos.slice(entriesInPage); - DEBUG(llvm::dbgs() - << " Page from " << pages.back().entries[0].rangeStart->name() - << " to " << pages.back().entries.back().rangeStart->name() << " + " - << llvm::format("0x%x", pages.back().entries.back().rangeLength) - << " has " << entriesInPage << " entries\n"); + LLVM_DEBUG(llvm::dbgs() + << " Page from " + << pages.back().entries[0].rangeStart->name() << " to " + << pages.back().entries.back().rangeStart->name() << " + " + << llvm::format("0x%x", + pages.back().entries.back().rangeLength) + << " has " << entriesInPage << " entries\n"); } while (!remainingInfos.empty()); auto *unwind = new (_file.allocator()) @@ -360,7 +362,7 @@ private: const SimpleFile &mergedFile, std::map<const Atom *, CompactUnwindEntry> &unwindLocs, std::vector<const Atom *> &personalities, uint32_t &numLSDAs) { - DEBUG(llvm::dbgs() << " Collecting __compact_unwind entries\n"); + LLVM_DEBUG(llvm::dbgs() << " Collecting __compact_unwind entries\n"); for (const DefinedAtom *atom : mergedFile.defined()) { if (atom->contentType() != DefinedAtom::typeCompactUnwindInfo) @@ -369,14 +371,15 @@ private: auto unwindEntry = extractCompactUnwindEntry(atom); unwindLocs.insert(std::make_pair(unwindEntry.rangeStart, unwindEntry)); - DEBUG(llvm::dbgs() << " Entry for " << unwindEntry.rangeStart->name() - << ", encoding=" - << llvm::format("0x%08x", unwindEntry.encoding)); + LLVM_DEBUG(llvm::dbgs() << " Entry for " + << unwindEntry.rangeStart->name() << ", encoding=" + << llvm::format("0x%08x", unwindEntry.encoding)); if (unwindEntry.personalityFunction) - DEBUG(llvm::dbgs() << ", personality=" - << unwindEntry.personalityFunction->name() - << ", lsdaLoc=" << unwindEntry.lsdaLocation->name()); - DEBUG(llvm::dbgs() << '\n'); + LLVM_DEBUG(llvm::dbgs() + << ", personality=" + << unwindEntry.personalityFunction->name() + << ", lsdaLoc=" << unwindEntry.lsdaLocation->name()); + LLVM_DEBUG(llvm::dbgs() << '\n'); // Count number of LSDAs we see, since we need to know how big the index // will be while laying out the section. @@ -454,7 +457,7 @@ private: const std::map<const Atom *, const Atom *> &dwarfFrames) { std::vector<CompactUnwindEntry> unwindInfos; - DEBUG(llvm::dbgs() << " Creating __unwind_info entries\n"); + LLVM_DEBUG(llvm::dbgs() << " Creating __unwind_info entries\n"); // The final order in the __unwind_info section must be derived from the // order of typeCode atoms, since that's how they'll be put into the object // file eventually (yuck!). @@ -465,10 +468,10 @@ private: unwindInfos.push_back(finalizeUnwindInfoEntryForAtom( atom, unwindLocs, personalities, dwarfFrames)); - DEBUG(llvm::dbgs() << " Entry for " << atom->name() - << ", final encoding=" - << llvm::format("0x%08x", unwindInfos.back().encoding) - << '\n'); + LLVM_DEBUG(llvm::dbgs() + << " Entry for " << atom->name() << ", final encoding=" + << llvm::format("0x%08x", unwindInfos.back().encoding) + << '\n'); } return unwindInfos; diff --git a/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/LayoutPass.cpp b/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/LayoutPass.cpp index 7bca07eb16d6..9058e4f562e2 100644 --- a/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/LayoutPass.cpp +++ b/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/LayoutPass.cpp @@ -191,7 +191,7 @@ static bool compareAtomsSub(const LayoutPass::SortKey &lc, // Sort atoms by their ordinal overrides only if they fall in the same // chain. if (leftRoot == rightRoot) { - DEBUG(reason = formatReason("override", lc._override, rc._override)); + LLVM_DEBUG(reason = formatReason("override", lc._override, rc._override)); return lc._override < rc._override; } @@ -200,8 +200,8 @@ static bool compareAtomsSub(const LayoutPass::SortKey &lc, DefinedAtom::ContentPermissions rightPerms = rightRoot->permissions(); if (leftPerms != rightPerms) { - DEBUG(reason = - formatReason("contentPerms", (int)leftPerms, (int)rightPerms)); + LLVM_DEBUG( + reason = formatReason("contentPerms", (int)leftPerms, (int)rightPerms)); return leftPerms < rightPerms; } @@ -210,7 +210,8 @@ static bool compareAtomsSub(const LayoutPass::SortKey &lc, DefinedAtom::ContentType rightType = rightRoot->contentType(); if (leftType != rightType) { - DEBUG(reason = formatReason("contentType", (int)leftType, (int)rightType)); + LLVM_DEBUG(reason = + formatReason("contentType", (int)leftType, (int)rightType)); return leftType < rightType; } @@ -226,8 +227,8 @@ static bool compareAtomsSub(const LayoutPass::SortKey &lc, const File *rightFile = &rightRoot->file(); if (leftFile != rightFile) { - DEBUG(reason = formatReason(".o order", (int)leftFile->ordinal(), - (int)rightFile->ordinal())); + LLVM_DEBUG(reason = formatReason(".o order", (int)leftFile->ordinal(), + (int)rightFile->ordinal())); return leftFile->ordinal() < rightFile->ordinal(); } @@ -236,8 +237,8 @@ static bool compareAtomsSub(const LayoutPass::SortKey &lc, uint64_t rightOrdinal = rightRoot->ordinal(); if (leftOrdinal != rightOrdinal) { - DEBUG(reason = formatReason("ordinal", (int)leftRoot->ordinal(), - (int)rightRoot->ordinal())); + LLVM_DEBUG(reason = formatReason("ordinal", (int)leftRoot->ordinal(), + (int)rightRoot->ordinal())); return leftOrdinal < rightOrdinal; } @@ -251,7 +252,7 @@ static bool compareAtoms(const LayoutPass::SortKey &lc, LayoutPass::SortOverride customSorter) { std::string reason; bool result = compareAtomsSub(lc, rc, customSorter, reason); - DEBUG({ + LLVM_DEBUG({ StringRef comp = result ? "<" : ">="; llvm::dbgs() << "Layout: '" << lc._atom.get()->name() << "' " << comp << " '" @@ -441,7 +442,7 @@ void LayoutPass::undecorate(File::AtomRange<DefinedAtom> &atomRange, /// Perform the actual pass llvm::Error LayoutPass::perform(SimpleFile &mergedFile) { - DEBUG(llvm::dbgs() << "******** Laying out atoms:\n"); + LLVM_DEBUG(llvm::dbgs() << "******** Laying out atoms:\n"); // sort the atoms ScopedTask task(getDefaultDomain(), "LayoutPass"); File::AtomRange<DefinedAtom> atomRange = mergedFile.defined(); @@ -450,12 +451,12 @@ llvm::Error LayoutPass::perform(SimpleFile &mergedFile) { buildFollowOnTable(atomRange); // Check the structure of followon graph if running in debug mode. - DEBUG(checkFollowonChain(atomRange)); + LLVM_DEBUG(checkFollowonChain(atomRange)); // Build override maps buildOrdinalOverrideMap(atomRange); - DEBUG({ + LLVM_DEBUG({ llvm::dbgs() << "unsorted atoms:\n"; printDefinedAtoms(atomRange); }); @@ -465,15 +466,15 @@ llvm::Error LayoutPass::perform(SimpleFile &mergedFile) { [&](const LayoutPass::SortKey &l, const LayoutPass::SortKey &r) -> bool { return compareAtoms(l, r, _customSorter); }); - DEBUG(checkTransitivity(vec, _customSorter)); + LLVM_DEBUG(checkTransitivity(vec, _customSorter)); undecorate(atomRange, vec); - DEBUG({ + LLVM_DEBUG({ llvm::dbgs() << "sorted atoms:\n"; printDefinedAtoms(atomRange); }); - DEBUG(llvm::dbgs() << "******** Finished laying out atoms\n"); + LLVM_DEBUG(llvm::dbgs() << "******** Finished laying out atoms\n"); return llvm::Error::success(); } diff --git a/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp b/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp index 4ef7a62a8297..ce423d03aae3 100644 --- a/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp +++ b/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "lld/Common/ErrorHandler.h" #include "lld/ReaderWriter/MachOLinkingContext.h" #include "ArchHandler.h" #include "File.h" @@ -579,29 +580,26 @@ MachOLinkingContext::findPathForFramework(StringRef fwName) const{ return llvm::None; } -bool MachOLinkingContext::validateImpl(raw_ostream &diagnostics) { +bool MachOLinkingContext::validateImpl() { // TODO: if -arch not specified, look at arch of first .o file. if (_currentVersion && _outputMachOType != MH_DYLIB) { - diagnostics << "error: -current_version can only be used with dylibs\n"; + error("-current_version can only be used with dylibs"); return false; } if (_compatibilityVersion && _outputMachOType != MH_DYLIB) { - diagnostics - << "error: -compatibility_version can only be used with dylibs\n"; + error("-compatibility_version can only be used with dylibs"); return false; } if (_deadStrippableDylib && _outputMachOType != MH_DYLIB) { - diagnostics - << "error: -mark_dead_strippable_dylib can only be used with dylibs.\n"; + error("-mark_dead_strippable_dylib can only be used with dylibs"); return false; } if (!_bundleLoader.empty() && outputMachOType() != MH_BUNDLE) { - diagnostics - << "error: -bundle_loader can only be used with Mach-O bundles\n"; + error("-bundle_loader can only be used with Mach-O bundles"); return false; } diff --git a/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp b/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp index 3b07a40f9bf2..473de894894e 100644 --- a/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp +++ b/contrib/llvm/tools/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp @@ -906,7 +906,7 @@ readCompUnit(const NormalizedFile &normalizedFile, abbrevData.getU8(&abbrevOffset); uint32_t name; llvm::dwarf::Form form; - llvm::DWARFFormParams formParams = {version, addrSize, Format}; + llvm::dwarf::FormParams formParams = {version, addrSize, Format}; TranslationUnitSource tu; while ((name = abbrevData.getULEB128(&abbrevOffset)) | (form = static_cast<llvm::dwarf::Form>( @@ -1431,8 +1431,8 @@ llvm::Error normalizedObjectToAtoms(MachOFile *file, const NormalizedFile &normalizedFile, bool copyRefs) { - DEBUG(llvm::dbgs() << "******** Normalizing file to atoms: " - << file->path() << "\n"); + LLVM_DEBUG(llvm::dbgs() << "******** Normalizing file to atoms: " + << file->path() << "\n"); bool scatterable = ((normalizedFile.flags & MH_SUBSECTIONS_VIA_SYMBOLS) != 0); // Create atoms from each section. diff --git a/contrib/llvm/tools/lld/tools/lld/lld.cpp b/contrib/llvm/tools/lld/tools/lld/lld.cpp index fa4bf6f64328..65a9a800ec7a 100644 --- a/contrib/llvm/tools/lld/tools/lld/lld.cpp +++ b/contrib/llvm/tools/lld/tools/lld/lld.cpp @@ -7,12 +7,22 @@ // //===----------------------------------------------------------------------===// // -// This is the entry point to the lld driver. This is a thin wrapper which -// dispatches to the given platform specific driver. +// This file contains the main function of the lld executable. The main +// function is a thin wrapper which dispatches to the platform specific +// driver. // -// If there is -flavor option, it is dispatched according to the arguments. -// If the flavor parameter is not present, then it is dispatched according -// to argv[0]. +// lld is a single executable that contains four different linkers for ELF, +// COFF, WebAssembly and Mach-O. The main function dispatches according to +// argv[0] (i.e. command name). The most common name for each target is shown +// below: +// +// - ld.lld: ELF (Unix) +// - ld64: Mach-O (macOS) +// - lld-link: COFF (Windows) +// - ld-wasm: WebAssembly +// +// lld can be invoked as "lld" along with "-flavor" option. This is for +// backward compatibility and not recommended. // //===----------------------------------------------------------------------===// @@ -20,10 +30,9 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Twine.h" -#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/InitLLVM.h" #include "llvm/Support/Path.h" -#include "llvm/Support/PrettyStackTrace.h" -#include "llvm/Support/Signals.h" +#include <cstdlib> using namespace lld; using namespace llvm; @@ -102,13 +111,18 @@ static Flavor parseFlavor(std::vector<const char *> &V) { return parseProgname(Arg0); } +// If this function returns true, lld calls _exit() so that it quickly +// exits without invoking destructors of globally allocated objects. +// +// We don't want to do that if we are running tests though, because +// doing that breaks leak sanitizer. So, lit sets this environment variable, +// and we use it to detect whether we are running tests or not. +static bool canExitEarly() { return StringRef(getenv("LLD_IN_TEST")) != "1"; } + /// Universal linker main(). This linker emulates the gnu, darwin, or /// windows linker based on the argv[0] or -flavor option. int main(int Argc, const char **Argv) { - // Standard set up, so program fails gracefully. - sys::PrintStackTraceOnErrorSignal(Argv[0]); - PrettyStackTraceProgram StackPrinter(Argc, Argv); - llvm_shutdown_obj Shutdown; + InitLLVM X(Argc, Argv); std::vector<const char *> Args(Argv, Argv + Argc); #ifdef __FreeBSD__ @@ -118,16 +132,17 @@ int main(int Argc, const char **Argv) { case Gnu: if (isPETarget(Args)) return !mingw::link(Args); - return !elf::link(Args, true); + return !elf::link(Args, canExitEarly()); case WinLink: - return !coff::link(Args, true); + return !coff::link(Args, canExitEarly()); case Darwin: - return !mach_o::link(Args); + return !mach_o::link(Args, canExitEarly()); case Wasm: - return !wasm::link(Args, true); + return !wasm::link(Args, canExitEarly()); default: die("lld is a generic driver.\n" - "Invoke ld.lld (Unix), ld (macOS) or lld-link (Windows) instead."); + "Invoke ld.lld (Unix), ld64.lld (macOS), lld-link (Windows), wasm-lld" + " (WebAssembly) instead"); } #endif } |