diff options
711 files changed, 19884 insertions, 5608 deletions
diff --git a/COFF/Chunks.cpp b/COFF/Chunks.cpp index 412ff783222b..29131d7eb8db 100644 --- a/COFF/Chunks.cpp +++ b/COFF/Chunks.cpp @@ -11,6 +11,7 @@ #include "InputFiles.h" #include "Symbols.h" #include "Writer.h" +#include "SymbolTable.h" #include "lld/Common/ErrorHandler.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/COFF.h" @@ -44,6 +45,22 @@ SectionChunk::SectionChunk(ObjFile *F, const coff_section *H) Live = !Config->DoGC || !isCOMDAT(); } +// Initialize the RelocTargets vector, to allow redirecting certain relocations +// to a thunk instead of the actual symbol the relocation's symbol table index +// indicates. +void SectionChunk::readRelocTargets() { + assert(RelocTargets.empty()); + RelocTargets.reserve(Relocs.size()); + for (const coff_relocation &Rel : Relocs) + RelocTargets.push_back(File->getSymbol(Rel.SymbolTableIndex)); +} + +// Reset RelocTargets to their original targets before thunks were added. +void SectionChunk::resetRelocTargets() { + for (size_t I = 0, E = Relocs.size(); I < E; ++I) + RelocTargets[I] = File->getSymbol(Relocs[I].SymbolTableIndex); +} + static void add16(uint8_t *P, int16_t V) { write16le(P, read16le(P) + V); } static void add32(uint8_t *P, int32_t V) { write32le(P, read32le(P) + V); } static void add64(uint8_t *P, int64_t V) { write64le(P, read64le(P) + V); } @@ -58,7 +75,8 @@ static bool checkSecRel(const SectionChunk *Sec, OutputSection *OS) { return true; if (Sec->isCodeView()) return false; - fatal("SECREL relocation cannot be applied to absolute symbols"); + error("SECREL relocation cannot be applied to absolute symbols"); + return false; } static void applySecRel(const SectionChunk *Sec, uint8_t *Off, @@ -98,7 +116,7 @@ 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) + " in " + + error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " + toString(File)); } } @@ -113,7 +131,7 @@ 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) + " in " + + error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " + toString(File)); } } @@ -123,16 +141,22 @@ static void applyMOV(uint8_t *Off, uint16_t V) { write16le(Off + 2, (read16le(Off + 2) & 0x8f00) | ((V & 0x700) << 4) | (V & 0xff)); } -static uint16_t readMOV(uint8_t *Off) { +static uint16_t readMOV(uint8_t *Off, bool MOVT) { uint16_t Op1 = read16le(Off); + if ((Op1 & 0xfbf0) != (MOVT ? 0xf2c0 : 0xf240)) + error("unexpected instruction in " + Twine(MOVT ? "MOVT" : "MOVW") + + " instruction in MOV32T relocation"); uint16_t Op2 = read16le(Off + 2); + if ((Op2 & 0x8000) != 0) + error("unexpected instruction in " + Twine(MOVT ? "MOVT" : "MOVW") + + " instruction in MOV32T relocation"); return (Op2 & 0x00ff) | ((Op2 >> 4) & 0x0700) | ((Op1 << 1) & 0x0800) | ((Op1 & 0x000f) << 12); } void applyMOV32T(uint8_t *Off, uint32_t V) { - uint16_t ImmW = readMOV(Off); // read MOVW operand - uint16_t ImmT = readMOV(Off + 4); // read MOVT operand + uint16_t ImmW = readMOV(Off, false); // read MOVW operand + uint16_t ImmT = readMOV(Off + 4, true); // read MOVT operand uint32_t Imm = ImmW | (ImmT << 16); V += Imm; // add the immediate offset applyMOV(Off, V); // set MOVW operand @@ -141,7 +165,7 @@ void applyMOV32T(uint8_t *Off, uint32_t V) { static void applyBranch20T(uint8_t *Off, int32_t V) { if (!isInt<21>(V)) - fatal("relocation out of range"); + error("relocation out of range"); uint32_t S = V < 0 ? 1 : 0; uint32_t J1 = (V >> 19) & 1; uint32_t J2 = (V >> 18) & 1; @@ -151,7 +175,7 @@ static void applyBranch20T(uint8_t *Off, int32_t V) { void applyBranch24T(uint8_t *Off, int32_t V) { if (!isInt<25>(V)) - fatal("relocation out of range"); + error("relocation out of range"); uint32_t S = V < 0 ? 1 : 0; uint32_t J1 = ((~V >> 23) & 1) ^ S; uint32_t J2 = ((~V >> 22) & 1) ^ S; @@ -176,7 +200,7 @@ 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) + " in " + + error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " + toString(File)); } } @@ -184,7 +208,7 @@ void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS, // 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, int Shift) { +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; @@ -198,7 +222,7 @@ static void applyArm64Addr(uint8_t *Off, uint64_t S, uint64_t P, int Shift) { // Update the immediate field in a AARCH64 ldr, str, and add instruction. // Optionally limit the range of the written immediate by one or more bits // (RangeLimit). -static void applyArm64Imm(uint8_t *Off, uint64_t Imm, uint32_t RangeLimit) { +void applyArm64Imm(uint8_t *Off, uint64_t Imm, uint32_t RangeLimit) { uint32_t Orig = read32le(Off); Imm += (Orig >> 10) & 0xFFF; Orig &= ~(0xFFF << 10); @@ -221,7 +245,7 @@ static void applyArm64Ldr(uint8_t *Off, uint64_t Imm) { if ((Orig & 0x4800000) == 0x4800000) Size += 4; if ((Imm & ((1 << Size) - 1)) != 0) - fatal("misaligned ldr/str offset"); + error("misaligned ldr/str offset"); applyArm64Imm(Off, Imm >> Size, Size); } @@ -250,21 +274,21 @@ static void applySecRelLdr(const SectionChunk *Sec, uint8_t *Off, applyArm64Ldr(Off, (S - OS->getRVA()) & 0xfff); } -static void applyArm64Branch26(uint8_t *Off, int64_t V) { +void applyArm64Branch26(uint8_t *Off, int64_t V) { if (!isInt<28>(V)) - fatal("relocation out of range"); + error("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"); + error("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"); + error("relocation out of range"); or32(Off, (V & 0x0000FFFC) << 3); } @@ -287,11 +311,37 @@ void SectionChunk::applyRelARM64(uint8_t *Off, uint16_t Type, OutputSection *OS, 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) + " in " + + error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " + toString(File)); } } +static void maybeReportRelocationToDiscarded(const SectionChunk *FromChunk, + Defined *Sym, + const coff_relocation &Rel) { + // Don't report these errors when the relocation comes from a debug info + // section or in mingw mode. MinGW mode object files (built by GCC) can + // have leftover sections with relocations against discarded comdat + // sections. Such sections are left as is, with relocations untouched. + if (FromChunk->isCodeView() || FromChunk->isDWARF() || Config->MinGW) + return; + + // Get the name of the symbol. If it's null, it was discarded early, so we + // have to go back to the object file. + ObjFile *File = FromChunk->File; + StringRef Name; + if (Sym) { + Name = Sym->getName(); + } else { + COFFSymbolRef COFFSym = + check(File->getCOFFObj()->getSymbol(Rel.SymbolTableIndex)); + File->getCOFFObj()->getSymbolName(COFFSym, Name); + } + + error("relocation against symbol in discarded section: " + Name + + getSymbolLocations(File, Rel.SymbolTableIndex)); +} + void SectionChunk::writeTo(uint8_t *Buf) const { if (!hasData()) return; @@ -302,46 +352,40 @@ void SectionChunk::writeTo(uint8_t *Buf) const { // Apply relocations. size_t InputSize = getSize(); - for (const coff_relocation &Rel : Relocs) { + for (size_t I = 0, E = Relocs.size(); I < E; I++) { + const coff_relocation &Rel = Relocs[I]; + // Check for an invalid relocation offset. This check isn't perfect, because // we don't have the relocation size, which is only known after checking the // machine and relocation type. As a result, a relocation may overwrite the // beginning of the following input section. - if (Rel.VirtualAddress >= InputSize) - fatal("relocation points beyond the end of its parent section"); + if (Rel.VirtualAddress >= InputSize) { + error("relocation points beyond the end of its parent section"); + continue; + } uint8_t *Off = Buf + OutputSectionOff + Rel.VirtualAddress; + // Use the potentially remapped Symbol instead of the one that the + // relocation points to. + auto *Sym = dyn_cast_or_null<Defined>(RelocTargets[I]); + // Get the output section of the symbol for this relocation. The output // section is needed to compute SECREL and SECTION relocations used in debug // info. - auto *Sym = - dyn_cast_or_null<Defined>(File->getSymbol(Rel.SymbolTableIndex)); - if (!Sym) { - if (isCodeView() || isDWARF()) - continue; - // Symbols in early discarded sections are represented using null pointers, - // so we need to retrieve the name from the object file. - COFFSymbolRef Sym = - check(File->getCOFFObj()->getSymbol(Rel.SymbolTableIndex)); - StringRef Name; - File->getCOFFObj()->getSymbolName(Sym, Name); - fatal("relocation against symbol in discarded section: " + Name); - } - Chunk *C = Sym->getChunk(); + Chunk *C = Sym ? Sym->getChunk() : nullptr; OutputSection *OS = C ? C->getOutputSection() : nullptr; - // Only absolute and __ImageBase symbols lack an output section. For any - // other symbol, this indicates that the chunk was discarded. Normally - // relocations against discarded sections are an error. However, debug info - // sections are not GC roots and can end up with these kinds of relocations. - // Skip these relocations. - if (!OS && !isa<DefinedAbsolute>(Sym) && !isa<DefinedSynthetic>(Sym)) { - if (isCodeView() || isDWARF()) - continue; - fatal("relocation against symbol in discarded section: " + - Sym->getName()); + // Skip the relocation if it refers to a discarded section, and diagnose it + // as an error if appropriate. If a symbol was discarded early, it may be + // null. If it was discarded late, the output section will be null, unless + // it was an absolute or synthetic symbol. + if (!Sym || + (!OS && !isa<DefinedAbsolute>(Sym) && !isa<DefinedSynthetic>(Sym))) { + maybeReportRelocationToDiscarded(this, Sym, Rel); + continue; } + uint64_t S = Sym->getRVA(); // Compute the RVA of the relocation for relative relocations. @@ -399,17 +443,125 @@ static uint8_t getBaserelType(const coff_relocation &Rel) { // fixed by the loader if load-time relocation is needed. // Only called when base relocation is enabled. void SectionChunk::getBaserels(std::vector<Baserel> *Res) { - for (const coff_relocation &Rel : Relocs) { + for (size_t I = 0, E = Relocs.size(); I < E; I++) { + const coff_relocation &Rel = Relocs[I]; uint8_t Ty = getBaserelType(Rel); if (Ty == IMAGE_REL_BASED_ABSOLUTE) continue; - Symbol *Target = File->getSymbol(Rel.SymbolTableIndex); + // Use the potentially remapped Symbol instead of the one that the + // relocation points to. + Symbol *Target = RelocTargets[I]; if (!Target || isa<DefinedAbsolute>(Target)) continue; Res->emplace_back(RVA + Rel.VirtualAddress, Ty); } } +// MinGW specific. +// Check whether a static relocation of type Type can be deferred and +// handled at runtime as a pseudo relocation (for references to a module +// local variable, which turned out to actually need to be imported from +// another DLL) This returns the size the relocation is supposed to update, +// in bits, or 0 if the relocation cannot be handled as a runtime pseudo +// relocation. +static int getRuntimePseudoRelocSize(uint16_t Type) { + // Relocations that either contain an absolute address, or a plain + // relative offset, since the runtime pseudo reloc implementation + // adds 8/16/32/64 bit values to a memory address. + // + // Given a pseudo relocation entry, + // + // typedef struct { + // DWORD sym; + // DWORD target; + // DWORD flags; + // } runtime_pseudo_reloc_item_v2; + // + // the runtime relocation performs this adjustment: + // *(base + .target) += *(base + .sym) - (base + .sym) + // + // This works for both absolute addresses (IMAGE_REL_*_ADDR32/64, + // IMAGE_REL_I386_DIR32, where the memory location initially contains + // the address of the IAT slot, and for relative addresses (IMAGE_REL*_REL32), + // where the memory location originally contains the relative offset to the + // IAT slot. + // + // This requires the target address to be writable, either directly out of + // the image, or temporarily changed at runtime with VirtualProtect. + // Since this only operates on direct address values, it doesn't work for + // ARM/ARM64 relocations, other than the plain ADDR32/ADDR64 relocations. + switch (Config->Machine) { + case AMD64: + switch (Type) { + case IMAGE_REL_AMD64_ADDR64: + return 64; + case IMAGE_REL_AMD64_ADDR32: + case IMAGE_REL_AMD64_REL32: + case IMAGE_REL_AMD64_REL32_1: + case IMAGE_REL_AMD64_REL32_2: + case IMAGE_REL_AMD64_REL32_3: + case IMAGE_REL_AMD64_REL32_4: + case IMAGE_REL_AMD64_REL32_5: + return 32; + default: + return 0; + } + case I386: + switch (Type) { + case IMAGE_REL_I386_DIR32: + case IMAGE_REL_I386_REL32: + return 32; + default: + return 0; + } + case ARMNT: + switch (Type) { + case IMAGE_REL_ARM_ADDR32: + return 32; + default: + return 0; + } + case ARM64: + switch (Type) { + case IMAGE_REL_ARM64_ADDR64: + return 64; + case IMAGE_REL_ARM64_ADDR32: + return 32; + default: + return 0; + } + default: + llvm_unreachable("unknown machine type"); + } +} + +// MinGW specific. +// Append information to the provided vector about all relocations that +// need to be handled at runtime as runtime pseudo relocations (references +// to a module local variable, which turned out to actually need to be +// imported from another DLL). +void SectionChunk::getRuntimePseudoRelocs( + std::vector<RuntimePseudoReloc> &Res) { + for (const coff_relocation &Rel : Relocs) { + auto *Target = + dyn_cast_or_null<Defined>(File->getSymbol(Rel.SymbolTableIndex)); + if (!Target || !Target->IsRuntimePseudoReloc) + continue; + int SizeInBits = getRuntimePseudoRelocSize(Rel.Type); + if (SizeInBits == 0) { + error("unable to automatically import from " + Target->getName() + + " with relocation type " + + File->getCOFFObj()->getRelocationTypeName(Rel.Type) + " in " + + toString(File)); + continue; + } + // SizeInBits is used to initialize the Flags field; currently no + // other flags are defined. + Res.emplace_back( + RuntimePseudoReloc(Target, this, Rel.VirtualAddress, SizeInBits)); + } +} + bool SectionChunk::hasData() const { return !(Header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA); } @@ -447,6 +599,13 @@ void SectionChunk::replace(SectionChunk *Other) { Other->Live = false; } +uint32_t SectionChunk::getSectionNumber() const { + DataRefImpl R; + R.p = reinterpret_cast<uintptr_t>(Header); + SectionRef S(R, File->getCOFFObj()); + return S.getIndex() + 1; +} + CommonChunk::CommonChunk(const COFFSymbolRef S) : Sym(S) { // Common symbols are aligned on natural boundaries up to 32 bytes. // This is what MSVC link.exe does. @@ -460,6 +619,7 @@ uint32_t CommonChunk::getOutputCharacteristics() const { void StringChunk::writeTo(uint8_t *Buf) const { memcpy(Buf + OutputSectionOff, Str.data(), Str.size()); + Buf[OutputSectionOff + Str.size()] = '\0'; } ImportThunkChunkX64::ImportThunkChunkX64(Defined *S) : ImpSymbol(S) { @@ -502,13 +662,30 @@ void ImportThunkChunkARM64::writeTo(uint8_t *Buf) const { applyArm64Ldr(Buf + OutputSectionOff + 4, Off); } +// A Thumb2, PIC, non-interworking range extension thunk. +const uint8_t ArmThunk[] = { + 0x40, 0xf2, 0x00, 0x0c, // P: movw ip,:lower16:S - (P + (L1-P) + 4) + 0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P) + 4) + 0xe7, 0x44, // L1: add pc, ip +}; + +size_t RangeExtensionThunk::getSize() const { + assert(Config->Machine == ARMNT); + return sizeof(ArmThunk); +} + +void RangeExtensionThunk::writeTo(uint8_t *Buf) const { + assert(Config->Machine == ARMNT); + uint64_t Offset = Target->getRVA() - RVA - 12; + memcpy(Buf + OutputSectionOff, ArmThunk, sizeof(ArmThunk)); + applyMOV32T(Buf + OutputSectionOff, uint32_t(Offset)); +} + void LocalImportChunk::getBaserels(std::vector<Baserel> *Res) { Res->emplace_back(getRVA()); } -size_t LocalImportChunk::getSize() const { - return Config->is64() ? 8 : 4; -} +size_t LocalImportChunk::getSize() const { return Config->Wordsize; } void LocalImportChunk::writeTo(uint8_t *Buf) const { if (Config->is64()) { @@ -528,6 +705,34 @@ void RVATableChunk::writeTo(uint8_t *Buf) const { "RVA tables should be de-duplicated"); } +// MinGW specific, for the "automatic import of variables from DLLs" feature. +size_t PseudoRelocTableChunk::getSize() const { + if (Relocs.empty()) + return 0; + return 12 + 12 * Relocs.size(); +} + +// MinGW specific. +void PseudoRelocTableChunk::writeTo(uint8_t *Buf) const { + if (Relocs.empty()) + return; + + ulittle32_t *Table = reinterpret_cast<ulittle32_t *>(Buf + OutputSectionOff); + // This is the list header, to signal the runtime pseudo relocation v2 + // format. + Table[0] = 0; + Table[1] = 0; + Table[2] = 1; + + size_t Idx = 3; + for (const RuntimePseudoReloc &RPR : Relocs) { + Table[Idx + 0] = RPR.Sym->getRVA(); + Table[Idx + 1] = RPR.Target->getRVA() + RPR.TargetOffset; + Table[Idx + 2] = RPR.Flags; + Idx += 3; + } +} + // Windows-specific. This class represents a block in .reloc section. // The format is described here. // @@ -613,13 +818,16 @@ void MergeChunk::addSection(SectionChunk *C) { } void MergeChunk::finalizeContents() { - for (SectionChunk *C : Sections) - if (C->isLive()) - Builder.add(toStringRef(C->getContents())); - Builder.finalize(); + if (!Finalized) { + for (SectionChunk *C : Sections) + if (C->Live) + Builder.add(toStringRef(C->getContents())); + Builder.finalize(); + Finalized = true; + } for (SectionChunk *C : Sections) { - if (!C->isLive()) + if (!C->Live) continue; size_t Off = Builder.getOffset(toStringRef(C->getContents())); C->setOutputSection(Out); @@ -640,5 +848,16 @@ void MergeChunk::writeTo(uint8_t *Buf) const { Builder.write(Buf + OutputSectionOff); } +// MinGW specific. +size_t AbsolutePointerChunk::getSize() const { return Config->Wordsize; } + +void AbsolutePointerChunk::writeTo(uint8_t *Buf) const { + if (Config->is64()) { + write64le(Buf + OutputSectionOff, Value); + } else { + write32le(Buf + OutputSectionOff, Value); + } +} + } // namespace coff } // namespace lld diff --git a/COFF/Chunks.h b/COFF/Chunks.h index 9e896531bd9a..f8a0ddd8ef3b 100644 --- a/COFF/Chunks.h +++ b/COFF/Chunks.h @@ -36,6 +36,7 @@ class DefinedImportData; class DefinedRegular; class ObjFile; class OutputSection; +class RuntimePseudoReloc; class Symbol; // Mask for permissions (discardable, writable, readable, executable, etc). @@ -63,6 +64,13 @@ public: // before calling this function. virtual void writeTo(uint8_t *Buf) const {} + // Called by the writer once before assigning addresses and writing + // the output. + virtual void readRelocTargets() {} + + // Called if restarting thunk addition. + virtual void resetRelocTargets() {} + // Called by the writer after an RVA is assigned, but before calling // getSize(). virtual void finalizeContents() {} @@ -114,6 +122,10 @@ protected: public: // The offset from beginning of the output section. The writer sets a value. uint64_t OutputSectionOff = 0; + + // Whether this section needs to be kept distinct from other sections during + // ICF. This is set by the driver using address-significance tables. + bool KeepUnique = false; }; // A chunk corresponding a section of an input file. @@ -140,6 +152,8 @@ public: SectionChunk(ObjFile *File, const coff_section *Header); static bool classof(const Chunk *C) { return C->kind() == SectionKind; } + void readRelocTargets() override; + void resetRelocTargets() override; size_t getSize() const override { return Header->SizeOfRawData; } ArrayRef<uint8_t> getContents() const; void writeTo(uint8_t *Buf) const override; @@ -157,6 +171,8 @@ public: void applyRelARM64(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S, uint64_t P) const; + void getRuntimePseudoRelocs(std::vector<RuntimePseudoReloc> &Res); + // Called if the garbage collector decides to not include this chunk // in a final output. It's supposed to print out a log message to stdout. void printDiscardedMessage() const; @@ -167,16 +183,6 @@ public: StringRef getDebugName() override; - // Returns true if the chunk was not dropped by GC. - bool isLive() { return Live; } - - // Used by the garbage collector. - void markLive() { - assert(Config->DoGC && "should only mark things live from GC"); - assert(!isLive() && "Cannot mark an already live section!"); - Live = true; - } - // True if this is a codeview debug info chunk. These will not be laid out in // the image. Instead they will end up in the PDB, if one is requested. bool isCodeView() const { @@ -197,10 +203,13 @@ public: // Allow iteration over the associated child chunks for this section. ArrayRef<SectionChunk *> children() const { return AssocChildren; } + // The section ID this chunk belongs to in its Obj. + uint32_t getSectionNumber() const; + // A pointer pointing to a replacement for this chunk. // Initially it points to "this" object. If this chunk is merged // with other chunk by ICF, it points to another chunk, - // and this chunk is considrered as dead. + // and this chunk is considered as dead. SectionChunk *Repl; // The CRC of the contents as described in the COFF spec 4.5.5. @@ -217,13 +226,17 @@ public: ArrayRef<coff_relocation> Relocs; + // Used by the garbage collector. + bool Live; + + // When inserting a thunk, we need to adjust a relocation to point to + // the thunk instead of the actual original target Symbol. + std::vector<Symbol *> RelocTargets; + private: StringRef SectionName; std::vector<SectionChunk *> AssocChildren; - // Used by the garbage collector. - bool Live; - // Used for ICF (Identical COMDAT Folding) void replace(SectionChunk *Other); uint32_t Class[2] = {0, 0}; @@ -254,6 +267,7 @@ public: private: llvm::StringTableBuilder Builder; + bool Finalized = false; }; // A chunk for common symbols. Common chunks don't have actual data. @@ -297,7 +311,7 @@ static const uint8_t ImportThunkARM64[] = { }; // Windows-specific. -// A chunk for DLL import jump table entry. In a final output, it's +// A chunk for DLL import jump table entry. In a final output, its // contents will be a JMP instruction to some __imp_ symbol. class ImportThunkChunkX64 : public Chunk { public: @@ -341,11 +355,22 @@ private: Defined *ImpSymbol; }; +class RangeExtensionThunk : public Chunk { +public: + explicit RangeExtensionThunk(Defined *T) : Target(T) {} + size_t getSize() const override; + void writeTo(uint8_t *Buf) const override; + + Defined *Target; +}; + // Windows-specific. // See comments for DefinedLocalImport class. class LocalImportChunk : public Chunk { public: - explicit LocalImportChunk(Defined *S) : Sym(S) {} + explicit LocalImportChunk(Defined *S) : Sym(S) { + Alignment = Config->Wordsize; + } size_t getSize() const override; void getBaserels(std::vector<Baserel> *Res) override; void writeTo(uint8_t *Buf) const override; @@ -414,9 +439,73 @@ public: uint8_t Type; }; +// This is a placeholder Chunk, to allow attaching a DefinedSynthetic to a +// specific place in a section, without any data. This is used for the MinGW +// specific symbol __RUNTIME_PSEUDO_RELOC_LIST_END__, even though the concept +// of an empty chunk isn't MinGW specific. +class EmptyChunk : public Chunk { +public: + EmptyChunk() {} + size_t getSize() const override { return 0; } + void writeTo(uint8_t *Buf) const override {} +}; + +// MinGW specific, for the "automatic import of variables from DLLs" feature. +// This provides the table of runtime pseudo relocations, for variable +// references that turned out to need to be imported from a DLL even though +// the reference didn't use the dllimport attribute. The MinGW runtime will +// process this table after loading, before handling control over to user +// code. +class PseudoRelocTableChunk : public Chunk { +public: + PseudoRelocTableChunk(std::vector<RuntimePseudoReloc> &Relocs) + : Relocs(std::move(Relocs)) { + Alignment = 4; + } + size_t getSize() const override; + void writeTo(uint8_t *Buf) const override; + +private: + std::vector<RuntimePseudoReloc> Relocs; +}; + +// MinGW specific; information about one individual location in the image +// that needs to be fixed up at runtime after loading. This represents +// one individual element in the PseudoRelocTableChunk table. +class RuntimePseudoReloc { +public: + RuntimePseudoReloc(Defined *Sym, SectionChunk *Target, uint32_t TargetOffset, + int Flags) + : Sym(Sym), Target(Target), TargetOffset(TargetOffset), Flags(Flags) {} + + Defined *Sym; + SectionChunk *Target; + uint32_t TargetOffset; + // The Flags field contains the size of the relocation, in bits. No other + // flags are currently defined. + int Flags; +}; + +// MinGW specific. A Chunk that contains one pointer-sized absolute value. +class AbsolutePointerChunk : public Chunk { +public: + AbsolutePointerChunk(uint64_t Value) : Value(Value) { + Alignment = getSize(); + } + size_t getSize() const override; + void writeTo(uint8_t *Buf) const override; + +private: + uint64_t Value; +}; + void applyMOV32T(uint8_t *Off, uint32_t V); void applyBranch24T(uint8_t *Off, int32_t V); +void applyArm64Addr(uint8_t *Off, uint64_t S, uint64_t P, int Shift); +void applyArm64Imm(uint8_t *Off, uint64_t Imm, uint32_t RangeLimit); +void applyArm64Branch26(uint8_t *Off, int64_t V); + } // namespace coff } // namespace lld diff --git a/COFF/Config.h b/COFF/Config.h index 3ae50b868333..8915b6a3bdd8 100644 --- a/COFF/Config.h +++ b/COFF/Config.h @@ -84,6 +84,7 @@ struct Configuration { bool is64() { return Machine == AMD64 || Machine == ARM64; } llvm::COFF::MachineTypes Machine = IMAGE_FILE_MACHINE_UNKNOWN; + size_t Wordsize; bool Verbose = false; WindowsSubsystem Subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN; Symbol *Entry = nullptr; @@ -94,7 +95,8 @@ struct Configuration { bool DoICF = true; bool TailMerge; bool Relocatable = true; - bool Force = false; + bool ForceMultiple = false; + bool ForceUnresolved = false; bool Debug = false; bool DebugDwarf = false; bool DebugGHashes = false; @@ -195,6 +197,7 @@ struct Configuration { bool MinGW = false; bool WarnMissingOrderSymbol = true; bool WarnLocallyDefinedImported = true; + bool WarnDebugInfoUnusable = true; bool Incremental = true; bool IntegrityCheck = false; bool KillAt = false; diff --git a/COFF/DLL.cpp b/COFF/DLL.cpp index 464abe8e0894..599cc5892a16 100644 --- a/COFF/DLL.cpp +++ b/COFF/DLL.cpp @@ -35,8 +35,6 @@ namespace { // Import table -static int ptrSize() { return Config->is64() ? 8 : 4; } - // A chunk for the import descriptor table. class HintNameChunk : public Chunk { public: @@ -61,8 +59,8 @@ private: // A chunk for the import descriptor table. class LookupChunk : public Chunk { public: - explicit LookupChunk(Chunk *C) : HintName(C) { Alignment = ptrSize(); } - size_t getSize() const override { return ptrSize(); } + explicit LookupChunk(Chunk *C) : HintName(C) { Alignment = Config->Wordsize; } + size_t getSize() const override { return Config->Wordsize; } void writeTo(uint8_t *Buf) const override { write32le(Buf + OutputSectionOff, HintName->getRVA()); @@ -76,8 +74,10 @@ public: // See Microsoft PE/COFF spec 7.1. Import Header for details. class OrdinalOnlyChunk : public Chunk { public: - explicit OrdinalOnlyChunk(uint16_t V) : Ordinal(V) { Alignment = ptrSize(); } - size_t getSize() const override { return ptrSize(); } + explicit OrdinalOnlyChunk(uint16_t V) : Ordinal(V) { + Alignment = Config->Wordsize; + } + size_t getSize() const override { return Config->Wordsize; } void writeTo(uint8_t *Buf) const override { // An import-by-ordinal slot has MSB 1 to indicate that @@ -230,6 +230,36 @@ static const uint8_t ThunkARM[] = { 0x60, 0x47, // bx ip }; +static const uint8_t ThunkARM64[] = { + 0x11, 0x00, 0x00, 0x90, // adrp x17, #0 __imp_<FUNCNAME> + 0x31, 0x02, 0x00, 0x91, // add x17, x17, #0 :lo12:__imp_<FUNCNAME> + 0xfd, 0x7b, 0xb3, 0xa9, // stp x29, x30, [sp, #-208]! + 0xfd, 0x03, 0x00, 0x91, // mov x29, sp + 0xe0, 0x07, 0x01, 0xa9, // stp x0, x1, [sp, #16] + 0xe2, 0x0f, 0x02, 0xa9, // stp x2, x3, [sp, #32] + 0xe4, 0x17, 0x03, 0xa9, // stp x4, x5, [sp, #48] + 0xe6, 0x1f, 0x04, 0xa9, // stp x6, x7, [sp, #64] + 0xe0, 0x87, 0x02, 0xad, // stp q0, q1, [sp, #80] + 0xe2, 0x8f, 0x03, 0xad, // stp q2, q3, [sp, #112] + 0xe4, 0x97, 0x04, 0xad, // stp q4, q5, [sp, #144] + 0xe6, 0x9f, 0x05, 0xad, // stp q6, q7, [sp, #176] + 0xe1, 0x03, 0x11, 0xaa, // mov x1, x17 + 0x00, 0x00, 0x00, 0x90, // adrp x0, #0 DELAY_IMPORT_DESCRIPTOR + 0x00, 0x00, 0x00, 0x91, // add x0, x0, #0 :lo12:DELAY_IMPORT_DESCRIPTOR + 0x00, 0x00, 0x00, 0x94, // bl #0 __delayLoadHelper2 + 0xf0, 0x03, 0x00, 0xaa, // mov x16, x0 + 0xe6, 0x9f, 0x45, 0xad, // ldp q6, q7, [sp, #176] + 0xe4, 0x97, 0x44, 0xad, // ldp q4, q5, [sp, #144] + 0xe2, 0x8f, 0x43, 0xad, // ldp q2, q3, [sp, #112] + 0xe0, 0x87, 0x42, 0xad, // ldp q0, q1, [sp, #80] + 0xe6, 0x1f, 0x44, 0xa9, // ldp x6, x7, [sp, #64] + 0xe4, 0x17, 0x43, 0xa9, // ldp x4, x5, [sp, #48] + 0xe2, 0x0f, 0x42, 0xa9, // ldp x2, x3, [sp, #32] + 0xe0, 0x07, 0x41, 0xa9, // ldp x0, x1, [sp, #16] + 0xfd, 0x7b, 0xcd, 0xa8, // ldp x29, x30, [sp], #208 + 0x00, 0x02, 0x1f, 0xd6, // br x16 +}; + // A chunk for the delay import thunk. class ThunkChunkX64 : public Chunk { public: @@ -298,11 +328,35 @@ public: Defined *Helper = nullptr; }; +class ThunkChunkARM64 : public Chunk { +public: + ThunkChunkARM64(Defined *I, Chunk *D, Defined *H) + : Imp(I), Desc(D), Helper(H) {} + + size_t getSize() const override { return sizeof(ThunkARM64); } + + void writeTo(uint8_t *Buf) const override { + memcpy(Buf + OutputSectionOff, ThunkARM64, sizeof(ThunkARM64)); + applyArm64Addr(Buf + OutputSectionOff + 0, Imp->getRVA(), RVA + 0, 12); + applyArm64Imm(Buf + OutputSectionOff + 4, Imp->getRVA() & 0xfff, 0); + applyArm64Addr(Buf + OutputSectionOff + 52, Desc->getRVA(), RVA + 52, 12); + applyArm64Imm(Buf + OutputSectionOff + 56, Desc->getRVA() & 0xfff, 0); + applyArm64Branch26(Buf + OutputSectionOff + 60, + Helper->getRVA() - RVA - 60); + } + + Defined *Imp = nullptr; + Chunk *Desc = nullptr; + Defined *Helper = nullptr; +}; + // A chunk for the import descriptor table. class DelayAddressChunk : public Chunk { public: - explicit DelayAddressChunk(Chunk *C) : Thunk(C) { Alignment = ptrSize(); } - size_t getSize() const override { return ptrSize(); } + explicit DelayAddressChunk(Chunk *C) : Thunk(C) { + Alignment = Config->Wordsize; + } + size_t getSize() const override { return Config->Wordsize; } void writeTo(uint8_t *Buf) const override { if (Config->is64()) { @@ -362,6 +416,8 @@ public: size_t getSize() const override { return Size * 4; } void writeTo(uint8_t *Buf) const override { + memset(Buf + OutputSectionOff, 0, getSize()); + for (const Export &E : Config->Exports) { uint8_t *P = Buf + OutputSectionOff + E.Ordinal * 4; uint32_t Bit = 0; @@ -418,30 +474,6 @@ private: } // anonymous namespace -uint64_t IdataContents::getDirSize() { - return Dirs.size() * sizeof(ImportDirectoryTableEntry); -} - -uint64_t IdataContents::getIATSize() { - return Addresses.size() * ptrSize(); -} - -// Returns a list of .idata contents. -// See Microsoft PE/COFF spec 5.4 for details. -std::vector<Chunk *> IdataContents::getChunks() { - create(); - - // The loader assumes a specific order of data. - // Add each type in the correct order. - std::vector<Chunk *> V; - V.insert(V.end(), Dirs.begin(), Dirs.end()); - V.insert(V.end(), Lookups.begin(), Lookups.end()); - V.insert(V.end(), Addresses.begin(), Addresses.end()); - V.insert(V.end(), Hints.begin(), Hints.end()); - V.insert(V.end(), DLLNames.begin(), DLLNames.end()); - return V; -} - void IdataContents::create() { std::vector<std::vector<DefinedImportData *>> V = binImports(Imports); @@ -465,8 +497,8 @@ void IdataContents::create() { Hints.push_back(C); } // Terminate with null values. - Lookups.push_back(make<NullChunk>(ptrSize())); - Addresses.push_back(make<NullChunk>(ptrSize())); + Lookups.push_back(make<NullChunk>(Config->Wordsize)); + Addresses.push_back(make<NullChunk>(Config->Wordsize)); for (int I = 0, E = Syms.size(); I < E; ++I) Syms[I]->setLocation(Addresses[Base + I]); @@ -555,6 +587,8 @@ Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *S, Chunk *Dir) { return make<ThunkChunkX86>(S, Dir, Helper); case ARMNT: return make<ThunkChunkARM>(S, Dir, Helper); + case ARM64: + return make<ThunkChunkARM64>(S, Dir, Helper); default: llvm_unreachable("unsupported machine type"); } diff --git a/COFF/DLL.h b/COFF/DLL.h index c5d6e7c93abf..a298271e2c0d 100644 --- a/COFF/DLL.h +++ b/COFF/DLL.h @@ -19,19 +19,12 @@ namespace coff { // Windows-specific. // IdataContents creates all chunks for the DLL import table. // You are supposed to call add() to add symbols and then -// call getChunks() to get a list of chunks. +// call create() to populate the chunk vectors. class IdataContents { public: void add(DefinedImportData *Sym) { Imports.push_back(Sym); } bool empty() { return Imports.empty(); } - std::vector<Chunk *> getChunks(); - uint64_t getDirRVA() { return Dirs[0]->getRVA(); } - uint64_t getDirSize(); - uint64_t getIATRVA() { return Addresses[0]->getRVA(); } - uint64_t getIATSize(); - -private: void create(); std::vector<DefinedImportData *> Imports; diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp index eefdb48beadd..2e4b1e6d3147 100644 --- a/COFF/Driver.cpp +++ b/COFF/Driver.cpp @@ -32,6 +32,7 @@ #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/LEB128.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/TarWriter.h" @@ -56,7 +57,7 @@ Configuration *Config; LinkerDriver *Driver; bool link(ArrayRef<const char *> Args, bool CanExitEarly, raw_ostream &Diag) { - errorHandler().LogName = sys::path::filename(Args[0]); + errorHandler().LogName = args::getFilenameWithoutExe(Args[0]); errorHandler().ErrorOS = &Diag; errorHandler().ColorDiagnostics = Diag.has_colors(); errorHandler().ErrorLimitExceededMsg = @@ -116,6 +117,19 @@ static std::future<MBErrPair> createFutureForFile(std::string Path) { }); } +// Symbol names are mangled by prepending "_" on x86. +static StringRef mangle(StringRef Sym) { + assert(Config->Machine != IMAGE_FILE_MACHINE_UNKNOWN); + if (Config->Machine == I386) + return Saver.save("_" + Sym); + return Sym; +} + +static bool findUnderscoreMangle(StringRef Sym) { + StringRef Entry = Symtab->findMangle(mangle(Sym)); + return !Entry.empty() && !isa<Undefined>(Symtab->find(Entry)); +} + MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> MB) { MemoryBufferRef MBRef = *MB; make<std::unique_ptr<MemoryBuffer>>(std::move(MB)); // take ownership @@ -357,13 +371,30 @@ Optional<StringRef> LinkerDriver::findFile(StringRef Filename) { return Path; } +// MinGW specific. If an embedded directive specified to link to +// foo.lib, but it isn't found, try libfoo.a instead. +StringRef LinkerDriver::doFindLibMinGW(StringRef Filename) { + if (Filename.contains('/') || Filename.contains('\\')) + return Filename; + + SmallString<128> S = Filename; + sys::path::replace_extension(S, ".a"); + StringRef LibName = Saver.save("lib" + S.str()); + return doFindFile(LibName); +} + // Find library file from search path. StringRef LinkerDriver::doFindLib(StringRef Filename) { // Add ".lib" to Filename if that has no file extension. bool HasExt = Filename.contains('.'); if (!HasExt) Filename = Saver.save(Filename + ".lib"); - return doFindFile(Filename); + StringRef Ret = doFindFile(Filename); + // For MinGW, if the find above didn't turn up anything, try + // looking for a MinGW formatted library name. + if (Config->MinGW && Ret == Filename) + return doFindLibMinGW(Filename); + return Ret; } // Resolves a library path. /nodefaultlib options are taken into @@ -407,54 +438,57 @@ Symbol *LinkerDriver::addUndefined(StringRef Name) { return B; } -// Symbol names are mangled by appending "_" prefix on x86. -StringRef LinkerDriver::mangle(StringRef Sym) { - assert(Config->Machine != IMAGE_FILE_MACHINE_UNKNOWN); - if (Config->Machine == I386) - return Saver.save("_" + Sym); - return 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); + assert(Config->Subsystem != IMAGE_SUBSYSTEM_UNKNOWN && + "must handle /subsystem before calling this"); + + if (Config->MinGW) + return mangle(Config->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI + ? "WinMainCRTStartup" + : "mainCRTStartup"); + + if (Config->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) { + if (findUnderscoreMangle("wWinMain")) { + if (!findUnderscoreMangle("WinMain")) + return mangle("wWinMainCRTStartup"); + warn("found both wWinMain and WinMain; using latter"); } - return ""; + return mangle("WinMainCRTStartup"); } - - // User-defined main functions and their corresponding entry points. - static const char *Entries[][2] = { - {"main", "mainCRTStartup"}, - {"wmain", "wmainCRTStartup"}, - {"WinMain", "WinMainCRTStartup"}, - {"wWinMain", "wWinMainCRTStartup"}, - }; - for (auto E : Entries) { - StringRef Entry = Symtab->findMangle(mangle(E[0])); - if (!Entry.empty() && !isa<Undefined>(Symtab->find(Entry))) - return mangle(E[1]); + if (findUnderscoreMangle("wmain")) { + if (!findUnderscoreMangle("main")) + return mangle("wmainCRTStartup"); + warn("found both wmain and main; using latter"); } - return ""; + return mangle("mainCRTStartup"); } WindowsSubsystem LinkerDriver::inferSubsystem() { if (Config->DLL) return IMAGE_SUBSYSTEM_WINDOWS_GUI; - if (Symtab->findUnderscore("main") || Symtab->findUnderscore("wmain")) + if (Config->MinGW) return IMAGE_SUBSYSTEM_WINDOWS_CUI; - if (Symtab->findUnderscore("WinMain") || Symtab->findUnderscore("wWinMain")) + // Note that link.exe infers the subsystem from the presence of these + // functions even if /entry: or /nodefaultlib are passed which causes them + // to not be called. + bool HaveMain = findUnderscoreMangle("main"); + bool HaveWMain = findUnderscoreMangle("wmain"); + bool HaveWinMain = findUnderscoreMangle("WinMain"); + bool HaveWWinMain = findUnderscoreMangle("wWinMain"); + if (HaveMain || HaveWMain) { + if (HaveWinMain || HaveWWinMain) { + warn(std::string("found ") + (HaveMain ? "main" : "wmain") + " and " + + (HaveWinMain ? "WinMain" : "wWinMain") + + "; defaulting to /subsystem:console"); + } + return IMAGE_SUBSYSTEM_WINDOWS_CUI; + } + if (HaveWinMain || HaveWWinMain) return IMAGE_SUBSYSTEM_WINDOWS_GUI; return IMAGE_SUBSYSTEM_UNKNOWN; } @@ -500,26 +534,65 @@ static std::string createResponseFile(const opt::InputArgList &Args, return Data.str(); } -static unsigned getDefaultDebugType(const opt::InputArgList &Args) { - unsigned DebugTypes = static_cast<unsigned>(DebugType::CV); +enum class DebugKind { Unknown, None, Full, FastLink, GHash, Dwarf, Symtab }; + +static DebugKind parseDebugKind(const opt::InputArgList &Args) { + auto *A = Args.getLastArg(OPT_debug, OPT_debug_opt); + if (!A) + return DebugKind::None; + if (A->getNumValues() == 0) + return DebugKind::Full; + + DebugKind Debug = StringSwitch<DebugKind>(A->getValue()) + .CaseLower("none", DebugKind::None) + .CaseLower("full", DebugKind::Full) + .CaseLower("fastlink", DebugKind::FastLink) + // LLD extensions + .CaseLower("ghash", DebugKind::GHash) + .CaseLower("dwarf", DebugKind::Dwarf) + .CaseLower("symtab", DebugKind::Symtab) + .Default(DebugKind::Unknown); + + if (Debug == DebugKind::FastLink) { + warn("/debug:fastlink unsupported; using /debug:full"); + return DebugKind::Full; + } + if (Debug == DebugKind::Unknown) { + error("/debug: unknown option: " + Twine(A->getValue())); + return DebugKind::None; + } + return Debug; +} + +static unsigned parseDebugTypes(const opt::InputArgList &Args) { + unsigned DebugTypes = static_cast<unsigned>(DebugType::None); + + if (auto *A = Args.getLastArg(OPT_debugtype)) { + SmallVector<StringRef, 3> Types; + A->getSpelling().split(Types, ',', /*KeepEmpty=*/false); + + for (StringRef Type : Types) { + unsigned V = StringSwitch<unsigned>(Type.lower()) + .Case("cv", static_cast<unsigned>(DebugType::CV)) + .Case("pdata", static_cast<unsigned>(DebugType::PData)) + .Case("fixup", static_cast<unsigned>(DebugType::Fixup)) + .Default(0); + if (V == 0) { + warn("/debugtype: unknown option: " + Twine(A->getValue())); + continue; + } + DebugTypes |= V; + } + return DebugTypes; + } + + // Default debug types + DebugTypes = static_cast<unsigned>(DebugType::CV); if (Args.hasArg(OPT_driver)) DebugTypes |= static_cast<unsigned>(DebugType::PData); if (Args.hasArg(OPT_profile)) DebugTypes |= static_cast<unsigned>(DebugType::Fixup); - return DebugTypes; -} -static unsigned parseDebugType(StringRef Arg) { - SmallVector<StringRef, 3> Types; - Arg.split(Types, ',', /*KeepEmpty=*/false); - - unsigned DebugTypes = static_cast<unsigned>(DebugType::None); - for (StringRef Type : Types) - DebugTypes |= StringSwitch<unsigned>(Type.lower()) - .Case("cv", static_cast<unsigned>(DebugType::CV)) - .Case("pdata", static_cast<unsigned>(DebugType::PData)) - .Case("fixup", static_cast<unsigned>(DebugType::Fixup)) - .Default(0); return DebugTypes; } @@ -679,131 +752,6 @@ static void parseModuleDefs(StringRef Path) { } } -// A helper function for filterBitcodeFiles. -static bool needsRebuilding(MemoryBufferRef MB) { - // The MSVC linker doesn't support thin archives, so if it's a thin - // archive, we always need to rebuild it. - std::unique_ptr<Archive> File = - CHECK(Archive::create(MB), "Failed to read " + MB.getBufferIdentifier()); - if (File->isThin()) - return true; - - // Returns true if the archive contains at least one bitcode file. - for (MemoryBufferRef Member : getArchiveMembers(File.get())) - if (identify_magic(Member.getBuffer()) == file_magic::bitcode) - return true; - return false; -} - -// Opens a given path as an archive file and removes bitcode files -// from them if exists. This function is to appease the MSVC linker as -// their linker doesn't like archive files containing non-native -// object files. -// -// If a given archive doesn't contain bitcode files, the archive path -// is returned as-is. Otherwise, a new temporary file is created and -// its path is returned. -static Optional<std::string> -filterBitcodeFiles(StringRef Path, std::vector<std::string> &TemporaryFiles) { - std::unique_ptr<MemoryBuffer> MB = CHECK( - MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path); - MemoryBufferRef MBRef = MB->getMemBufferRef(); - file_magic Magic = identify_magic(MBRef.getBuffer()); - - if (Magic == file_magic::bitcode) - return None; - if (Magic != file_magic::archive) - return Path.str(); - if (!needsRebuilding(MBRef)) - return Path.str(); - - std::unique_ptr<Archive> File = - CHECK(Archive::create(MBRef), - MBRef.getBufferIdentifier() + ": failed to parse archive"); - - std::vector<NewArchiveMember> New; - for (MemoryBufferRef Member : getArchiveMembers(File.get())) - if (identify_magic(Member.getBuffer()) != file_magic::bitcode) - New.emplace_back(Member); - - if (New.empty()) - return None; - - log("Creating a temporary archive for " + Path + " to remove bitcode files"); - - SmallString<128> 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); - - Error E = - llvm::writeArchive(Temp, New, /*WriteSymtab=*/true, Archive::Kind::K_GNU, - /*Deterministics=*/true, - /*Thin=*/false); - handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) { - error("failed to create a new archive " + S.str() + ": " + EI.message()); - }); - return Temp; -} - -// Create response file contents and invoke the MSVC linker. -void LinkerDriver::invokeMSVC(opt::InputArgList &Args) { - std::string Rsp = "/nologo\n"; - std::vector<std::string> Temps; - - // Write out archive members that we used in symbol resolution and pass these - // to MSVC before any archives, so that MSVC uses the same objects to satisfy - // references. - for (ObjFile *Obj : ObjFile::Instances) { - if (Obj->ParentName.empty()) - continue; - SmallString<128> S; - int Fd; - if (auto EC = sys::fs::createTemporaryFile( - "lld-" + sys::path::filename(Obj->ParentName), ".obj", Fd, S)) - fatal("cannot create a temporary file: " + EC.message()); - raw_fd_ostream OS(Fd, /*shouldClose*/ true); - OS << Obj->MB.getBuffer(); - Temps.push_back(S.str()); - Rsp += quote(S) + "\n"; - } - - for (auto *Arg : Args) { - switch (Arg->getOption().getID()) { - case OPT_linkrepro: - case OPT_lldmap: - case OPT_lldmap_file: - case OPT_lldsavetemps: - case OPT_msvclto: - // LLD-specific options are stripped. - break; - case OPT_opt: - if (!StringRef(Arg->getValue()).startswith("lld")) - Rsp += toString(*Arg) + " "; - break; - case OPT_INPUT: { - if (Optional<StringRef> Path = doFindFile(Arg->getValue())) { - if (Optional<std::string> S = filterBitcodeFiles(*Path, Temps)) - Rsp += quote(*S) + "\n"; - continue; - } - Rsp += quote(Arg->getValue()) + "\n"; - break; - } - default: - Rsp += toString(*Arg) + "\n"; - } - } - - std::vector<StringRef> ObjFiles = Symtab->compileBitcodeFiles(); - runMSVCLinker(Rsp, ObjFiles); - - for (StringRef Path : Temps) - sys::fs::remove(Path); -} - void LinkerDriver::enqueueTask(std::function<void()> Task) { TaskQueue.push_back(std::move(Task)); } @@ -859,6 +807,97 @@ static void parseOrderFile(StringRef Arg) { } } +static void markAddrsig(Symbol *S) { + if (auto *D = dyn_cast_or_null<Defined>(S)) + if (Chunk *C = D->getChunk()) + C->KeepUnique = true; +} + +static void findKeepUniqueSections() { + // Exported symbols could be address-significant in other executables or DSOs, + // so we conservatively mark them as address-significant. + for (Export &R : Config->Exports) + markAddrsig(R.Sym); + + // Visit the address-significance table in each object file and mark each + // referenced symbol as address-significant. + for (ObjFile *Obj : ObjFile::Instances) { + ArrayRef<Symbol *> Syms = Obj->getSymbols(); + if (Obj->AddrsigSec) { + ArrayRef<uint8_t> Contents; + Obj->getCOFFObj()->getSectionContents(Obj->AddrsigSec, Contents); + 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(Obj) + ": could not decode addrsig section: " + Err); + if (SymIndex >= Syms.size()) + fatal(toString(Obj) + ": invalid symbol index in addrsig section"); + 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); + } + } +} + +// link.exe replaces each %foo% in AltPath with the contents of environment +// variable foo, and adds the two magic env vars _PDB (expands to the basename +// of pdb's output path) and _EXT (expands to the extension of the output +// binary). +// lld only supports %_PDB% and %_EXT% and warns on references to all other env +// vars. +static void parsePDBAltPath(StringRef AltPath) { + SmallString<128> Buf; + StringRef PDBBasename = + sys::path::filename(Config->PDBPath, sys::path::Style::windows); + StringRef BinaryExtension = + sys::path::extension(Config->OutputFile, sys::path::Style::windows); + if (!BinaryExtension.empty()) + BinaryExtension = BinaryExtension.substr(1); // %_EXT% does not include '.'. + + // Invariant: + // +--------- Cursor ('a...' might be the empty string). + // | +----- FirstMark + // | | +- SecondMark + // v v v + // a...%...%... + size_t Cursor = 0; + while (Cursor < AltPath.size()) { + size_t FirstMark, SecondMark; + if ((FirstMark = AltPath.find('%', Cursor)) == StringRef::npos || + (SecondMark = AltPath.find('%', FirstMark + 1)) == StringRef::npos) { + // Didn't find another full fragment, treat rest of string as literal. + Buf.append(AltPath.substr(Cursor)); + break; + } + + // Found a full fragment. Append text in front of first %, and interpret + // text between first and second % as variable name. + Buf.append(AltPath.substr(Cursor, FirstMark - Cursor)); + StringRef Var = AltPath.substr(FirstMark, SecondMark - FirstMark + 1); + if (Var.equals_lower("%_pdb%")) + Buf.append(PDBBasename); + else if (Var.equals_lower("%_ext%")) + Buf.append(BinaryExtension); + else { + warn("only %_PDB% and %_EXT% supported in /pdbaltpath:, keeping " + + Var + " as literal"); + Buf.append(Var); + } + + Cursor = SecondMark + 1; + } + + Config->PDBAltPath = Buf; +} + 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. @@ -947,11 +986,17 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // Handle /ignore for (auto *Arg : Args.filtered(OPT_ignore)) { - if (StringRef(Arg->getValue()) == "4037") - Config->WarnMissingOrderSymbol = false; - else if (StringRef(Arg->getValue()) == "4217") - Config->WarnLocallyDefinedImported = false; - // Other warning numbers are ignored. + SmallVector<StringRef, 8> Vec; + StringRef(Arg->getValue()).split(Vec, ','); + for (StringRef S : Vec) { + if (S == "4037") + Config->WarnMissingOrderSymbol = false; + else if (S == "4099") + Config->WarnDebugInfoUnusable = false; + else if (S == "4217") + Config->WarnLocallyDefinedImported = false; + // Other warning numbers are ignored. + } } // Handle /out @@ -965,20 +1010,26 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // Handle /force or /force:unresolved if (Args.hasArg(OPT_force, OPT_force_unresolved)) - Config->Force = true; + Config->ForceUnresolved = true; + + // Handle /force or /force:multiple + if (Args.hasArg(OPT_force, OPT_force_multiple)) + Config->ForceMultiple = true; // Handle /debug - if (Args.hasArg(OPT_debug, OPT_debug_dwarf, OPT_debug_ghash)) { + DebugKind Debug = parseDebugKind(Args); + if (Debug == DebugKind::Full || Debug == DebugKind::Dwarf || + Debug == DebugKind::GHash) { Config->Debug = true; Config->Incremental = true; - if (auto *Arg = Args.getLastArg(OPT_debugtype)) - Config->DebugTypes = parseDebugType(Arg->getValue()); - else - Config->DebugTypes = getDefaultDebugType(Args); } + // Handle /debugtype + Config->DebugTypes = parseDebugTypes(Args); + // Handle /pdb - bool ShouldCreatePDB = Args.hasArg(OPT_debug, OPT_debug_ghash); + bool ShouldCreatePDB = + (Debug == DebugKind::Full || Debug == DebugKind::GHash); if (ShouldCreatePDB) { if (auto *Arg = Args.getLastArg(OPT_pdb)) Config->PDBPath = Arg->getValue(); @@ -1099,7 +1150,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { Config->Implib = Arg->getValue(); // Handle /opt. - bool DoGC = !Args.hasArg(OPT_debug) || Args.hasArg(OPT_profile); + bool DoGC = Debug == DebugKind::None || Args.hasArg(OPT_profile); unsigned ICFLevel = Args.hasArg(OPT_profile) ? 0 : 1; // 0: off, 1: limited, 2: on unsigned TailMerge = 1; @@ -1184,6 +1235,12 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { parseMerge(".xdata=.rdata"); parseMerge(".bss=.data"); + if (Config->MinGW) { + parseMerge(".ctors=.rdata"); + parseMerge(".dtors=.rdata"); + parseMerge(".CRT=.rdata"); + } + // Handle /section for (auto *Arg : Args.filtered(OPT_section)) parseSection(Arg->getValue()); @@ -1237,9 +1294,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { Config->NxCompat = Args.hasFlag(OPT_nxcompat, OPT_nxcompat_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->DebugDwarf = Debug == DebugKind::Dwarf; + Config->DebugGHashes = Debug == DebugKind::GHash; + Config->DebugSymtab = Debug == DebugKind::Symtab; Config->MapFile = getMapFile(Args); @@ -1269,10 +1326,14 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { return; std::set<sys::fs::UniqueID> WholeArchives; - for (auto *Arg : Args.filtered(OPT_wholearchive_file)) - if (Optional<StringRef> Path = doFindFile(Arg->getValue())) + AutoExporter Exporter; + 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); + Exporter.addWholeArchive(*Path); + } + } // A predicate returning true if a given path is an argument for // /wholearchive:, or /wholearchive is enabled globally. @@ -1303,12 +1364,16 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // Read all input files given via the command line. run(); + if (errorCount()) + return; + // We should have inferred a machine type by now from the input files, but if // not we assume x64. if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) { warn("/machine is not specified. x64 is assumed"); Config->Machine = AMD64; } + Config->Wordsize = Config->is64() ? 8 : 4; // Input files can be Windows resource files (.res files). We use // WindowsResource to convert resource files to a regular COFF file, @@ -1335,25 +1400,6 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { error("/dynamicbase:no is not compatible with " + machineToStr(Config->Machine)); - // Handle /entry and /dll - if (auto *Arg = Args.getLastArg(OPT_entry)) { - Config->Entry = addUndefined(mangle(Arg->getValue())); - } else if (!Config->Entry && !Config->NoEntry) { - if (Args.hasArg(OPT_dll)) { - StringRef S = (Config->Machine == I386) ? "__DllMainCRTStartup@12" - : "_DllMainCRTStartup"; - Config->Entry = addUndefined(S); - } else { - // Windows specific -- If entry point name is not given, we need to - // infer that from user-defined entry name. - StringRef S = findDefaultEntry(); - if (S.empty()) - fatal("entry point must be defined"); - Config->Entry = addUndefined(S); - log("Entry name inferred: " + S); - } - } - // Handle /export for (auto *Arg : Args.filtered(OPT_export)) { Export E = parseExport(Arg->getValue()); @@ -1379,6 +1425,34 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { return; } + // Windows specific -- if no /subsystem is given, we need to infer + // that from entry point name. Must happen before /entry handling, + // and after the early return when just writing an import library. + if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) { + Config->Subsystem = inferSubsystem(); + if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) + fatal("subsystem must be defined"); + } + + // Handle /entry and /dll + if (auto *Arg = Args.getLastArg(OPT_entry)) { + Config->Entry = addUndefined(mangle(Arg->getValue())); + } else if (!Config->Entry && !Config->NoEntry) { + if (Args.hasArg(OPT_dll)) { + StringRef S = (Config->Machine == I386) ? "__DllMainCRTStartup@12" + : "_DllMainCRTStartup"; + Config->Entry = addUndefined(S); + } else { + // Windows specific -- If entry point name is not given, we need to + // infer that from user-defined entry name. + StringRef S = findDefaultEntry(); + if (S.empty()) + fatal("entry point must be defined"); + Config->Entry = addUndefined(S); + log("Entry name inferred: " + S); + } + } + // Handle /delayload for (auto *Arg : Args.filtered(OPT_delayload)) { Config->DelayLoads.insert(StringRef(Arg->getValue()).lower()); @@ -1412,6 +1486,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // tools won't work correctly if these assumptions are not held. sys::fs::make_absolute(Config->PDBAltPath); sys::path::remove_dots(Config->PDBAltPath); + } else { + // Don't do this earlier, so that Config->OutputFile is ready. + parsePDBAltPath(Config->PDBAltPath); } } @@ -1435,6 +1512,13 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // Needed for MSVC 2017 15.5 CRT. Symtab->addAbsolute(mangle("__enclave_config"), 0); + if (Config->MinGW) { + Symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0); + Symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0); + Symtab->addAbsolute(mangle("__CTOR_LIST__"), 0); + Symtab->addAbsolute(mangle("__DTOR_LIST__"), 0); + } + // This code may add new undefined symbols to the link, which may enqueue more // symbol resolution tasks, so we need to continue executing tasks until we // converge. @@ -1474,31 +1558,34 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { if (errorCount()) return; - // If /msvclto is given, we use the MSVC linker to link LTO output files. - // This is useful because MSVC link.exe can generate complete PDBs. - if (Args.hasArg(OPT_msvclto)) { - invokeMSVC(Args); - return; - } - // Do LTO by compiling bitcode input files to a set of native COFF files then // link those files. Symtab->addCombinedLTOObjects(); run(); + if (Config->MinGW) { + // Load any further object files that might be needed for doing automatic + // imports. + // + // For cases with no automatically imported symbols, this iterates once + // over the symbol table and doesn't do anything. + // + // For the normal case with a few automatically imported symbols, this + // should only need to be run once, since each new object file imported + // is an import library and wouldn't add any new undefined references, + // but there's nothing stopping the __imp_ symbols from coming from a + // normal object file as well (although that won't be used for the + // actual autoimport later on). If this pass adds new undefined references, + // we won't iterate further to resolve them. + Symtab->loadMinGWAutomaticImports(); + run(); + } + // Make sure we have resolved all symbols. Symtab->reportRemainingUndefines(); if (errorCount()) return; - // Windows specific -- if no /subsystem is given, we need to infer - // that from entry point name. - if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) { - Config->Subsystem = inferSubsystem(); - if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) - fatal("subsystem must be defined"); - } - // Handle /safeseh. if (Args.hasFlag(OPT_safeseh, OPT_safeseh_no, false)) { for (ObjFile *File : ObjFile::Instances) @@ -1512,7 +1599,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // are chosen to be exported. if (Config->DLL && ((Config->MinGW && Config->Exports.empty()) || Args.hasArg(OPT_export_all_symbols))) { - AutoExporter Exporter; + Exporter.initSymbolExcludes(); Symtab->forEachSymbol([=](Symbol *S) { auto *Def = dyn_cast<Defined>(S); @@ -1551,11 +1638,11 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { continue; } + // If the symbol isn't common, it must have been replaced with a regular + // symbol, which will carry its own alignment. auto *DC = dyn_cast<DefinedCommon>(Sym); - if (!DC) { - warn("/aligncomm symbol " + Name + " of wrong kind"); + if (!DC) continue; - } CommonChunk *C = DC->getChunk(); C->Alignment = std::max(C->Alignment, Alignment); @@ -1576,8 +1663,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { markLive(Symtab->getChunks()); // Identify identical COMDAT sections to merge them. - if (Config->DoICF) + if (Config->DoICF) { + findKeepUniqueSections(); doICF(Symtab->getChunks()); + } // Write the result. writeResult(); diff --git a/COFF/Driver.h b/COFF/Driver.h index 627e991a9028..e779721ab75d 100644 --- a/COFF/Driver.h +++ b/COFF/Driver.h @@ -89,6 +89,7 @@ private: Optional<StringRef> findLib(StringRef Filename); StringRef doFindFile(StringRef Filename); StringRef doFindLib(StringRef Filename); + StringRef doFindLibMinGW(StringRef Filename); // Parses LIB environment which contains a list of search paths. void addLibSearchPaths(); @@ -103,7 +104,6 @@ private: std::set<std::string> VisitedLibs; Symbol *addUndefined(StringRef Sym); - StringRef mangle(StringRef Sym); // Windows specific -- "main" is not the only main function in Windows. // You can choose one from these four -- {w,}{WinMain,main}. @@ -115,8 +115,6 @@ private: StringRef findDefaultEntry(); WindowsSubsystem inferSubsystem(); - void invokeMSVC(llvm::opt::InputArgList &Args); - void addBuffer(std::unique_ptr<MemoryBuffer> MB, bool WholeArchive); void addArchiveBuffer(MemoryBufferRef MBRef, StringRef SymName, StringRef ParentName); diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp index c12e791f9507..3a11895497a4 100644 --- a/COFF/DriverUtils.cpp +++ b/COFF/DriverUtils.cpp @@ -713,26 +713,6 @@ MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> MBs) { return MBRef; } -// Run MSVC link.exe for given in-memory object files. -// Command line options are copied from those given to LLD. -// This is for the /msvclto option. -void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects) { - // Write the in-memory object files to disk. - std::vector<TemporaryFile> Temps; - for (StringRef S : Objects) { - Temps.emplace_back("lto", "obj", S); - Rsp += quote(Temps.back().Path) + "\n"; - } - - log("link.exe " + Rsp); - - // Run MSVC link.exe. - Temps.emplace_back("lto", "rsp", Rsp); - Executor E("link.exe"); - E.add(Twine("@" + Temps.back().Path)); - E.run(); -} - // Create OptTable // Create prefix string literals used in Options.td @@ -883,7 +863,9 @@ std::vector<const char *> ArgParser::tokenize(StringRef S) { } void printHelp(const char *Argv0) { - COFFOptTable().PrintHelp(outs(), Argv0, "LLVM Linker", false); + COFFOptTable().PrintHelp(outs(), + (std::string(Argv0) + " [options] file...").c_str(), + "LLVM Linker", false); } } // namespace coff diff --git a/COFF/ICF.cpp b/COFF/ICF.cpp index 7feb3c4e0b0c..34ea360fa925 100644 --- a/COFF/ICF.cpp +++ b/COFF/ICF.cpp @@ -22,6 +22,7 @@ #include "Chunks.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" +#include "lld/Common/Threads.h" #include "lld/Common/Timer.h" #include "llvm/ADT/Hashing.h" #include "llvm/Support/Debug.h" @@ -80,7 +81,7 @@ private: bool ICF::isEligible(SectionChunk *C) { // Non-comdat chunks, dead chunks, and writable chunks are not elegible. bool Writable = C->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE; - if (!C->isCOMDAT() || !C->isLive() || Writable) + if (!C->isCOMDAT() || !C->Live || Writable) return false; // Code sections are eligible. @@ -93,7 +94,11 @@ bool ICF::isEligible(SectionChunk *C) { return true; // So are vtables. - return C->Sym && C->Sym->getName().startswith("??_7"); + if (C->Sym && C->Sym->getName().startswith("??_7")) + return true; + + // Anything else not in an address-significance table is eligible. + return !C->KeepUnique; } // Split an equivalence class into smaller classes. @@ -222,10 +227,10 @@ void ICF::forEachClass(std::function<void(size_t, size_t)> 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) { + parallelForEachN(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) { + parallelForEachN(1, NumShards + 1, [&](size_t I) { if (Boundaries[I - 1] < Boundaries[I]) { forEachClassRange(Boundaries[I - 1], Boundaries[I], Fn); } @@ -257,9 +262,19 @@ void ICF::run(ArrayRef<Chunk *> Vec) { SC->Class[0] = NextId++; // Initially, we use hash values to partition sections. - for_each(parallel::par, Chunks.begin(), Chunks.end(), [&](SectionChunk *SC) { + parallelForEach(Chunks, [&](SectionChunk *SC) { + SC->Class[1] = xxHash64(SC->getContents()); + }); + + // Combine the hashes of the sections referenced by each section into its + // hash. + parallelForEach(Chunks, [&](SectionChunk *SC) { + uint32_t Hash = SC->Class[1]; + for (Symbol *B : SC->symbols()) + if (auto *Sym = dyn_cast_or_null<DefinedRegular>(B)) + Hash ^= Sym->getChunk()->Class[1]; // Set MSB to 1 to avoid collisions with non-hash classs. - SC->Class[0] = xxHash64(SC->getContents()) | (1 << 31); + SC->Class[0] = Hash | (1U << 31); }); // From now on, sections in Chunks are ordered so that sections in diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp index 2b3e65fae04b..236c90ef0388 100644 --- a/COFF/InputFiles.cpp +++ b/COFF/InputFiles.cpp @@ -54,8 +54,16 @@ std::vector<BitcodeFile *> BitcodeFile::Instances; static void checkAndSetWeakAlias(SymbolTable *Symtab, InputFile *F, Symbol *Source, Symbol *Target) { if (auto *U = dyn_cast<Undefined>(Source)) { - if (U->WeakAlias && U->WeakAlias != Target) + if (U->WeakAlias && U->WeakAlias != Target) { + // Weak aliases as produced by GCC are named in the form + // .weak.<weaksymbol>.<othersymbol>, where <othersymbol> is the name + // of another symbol emitted near the weak symbol. + // Just use the definition from the first object file that defined + // this weak symbol. + if (Config->MinGW) + return; Symtab->reportDuplicate(Source, F); + } U->WeakAlias = Target; } } @@ -147,9 +155,10 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber, const coff_aux_section_definition *Def, StringRef LeaderName) { const coff_section *Sec; - StringRef Name; if (auto EC = COFFObj->getSection(SectionNumber, Sec)) fatal("getSection failed: #" + Twine(SectionNumber) + ": " + EC.message()); + + StringRef Name; if (auto EC = COFFObj->getSectionName(Sec, Name)) fatal("getSectionName failed: #" + Twine(SectionNumber) + ": " + EC.message()); @@ -161,6 +170,11 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber, return nullptr; } + if (Name == ".llvm_addrsig") { + AddrsigSec = Sec; + return nullptr; + } + // Object files may have DWARF debug info or MS CodeView debug info // (or both). // @@ -168,8 +182,8 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber, // of the linker; they are just a data section containing relocations. // We can just link them to complete debug info. // - // CodeView needs a linker support. We need to interpret and debug - // info, and then write it to a separate .pdb file. + // CodeView needs linker support. We need to interpret debug info, + // and then write it to a separate .pdb file. // Ignore DWARF debug info unless /debug is given. if (!Config->Debug && Name.startswith(".debug_")) @@ -205,7 +219,13 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber, void ObjFile::readAssociativeDefinition( COFFSymbolRef Sym, const coff_aux_section_definition *Def) { - SectionChunk *Parent = SparseChunks[Def->getNumber(Sym.isBigObj())]; + readAssociativeDefinition(Sym, Def, Def->getNumber(Sym.isBigObj())); +} + +void ObjFile::readAssociativeDefinition(COFFSymbolRef Sym, + const coff_aux_section_definition *Def, + uint32_t ParentSection) { + SectionChunk *Parent = SparseChunks[ParentSection]; // If the parent is pending, it probably means that its section definition // appears after us in the symbol table. Leave the associated section as @@ -225,6 +245,35 @@ void ObjFile::readAssociativeDefinition( } } +void ObjFile::recordPrevailingSymbolForMingw( + COFFSymbolRef Sym, DenseMap<StringRef, uint32_t> &PrevailingSectionMap) { + // For comdat symbols in executable sections, where this is the copy + // of the section chunk we actually include instead of discarding it, + // add the symbol to a map to allow using it for implicitly + // associating .[px]data$<func> sections to it. + int32_t SectionNumber = Sym.getSectionNumber(); + SectionChunk *SC = SparseChunks[SectionNumber]; + if (SC && SC->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) { + StringRef Name; + COFFObj->getSymbolName(Sym, Name); + PrevailingSectionMap[Name] = SectionNumber; + } +} + +void ObjFile::maybeAssociateSEHForMingw( + COFFSymbolRef Sym, const coff_aux_section_definition *Def, + const DenseMap<StringRef, uint32_t> &PrevailingSectionMap) { + StringRef Name; + COFFObj->getSymbolName(Sym, Name); + if (Name.consume_front(".pdata$") || Name.consume_front(".xdata$")) { + // For MinGW, treat .[px]data$<func> as implicitly associative to + // the symbol <func>. + auto ParentSym = PrevailingSectionMap.find(Name); + if (ParentSym != PrevailingSectionMap.end()) + readAssociativeDefinition(Sym, Def, ParentSym->second); + } +} + Symbol *ObjFile::createRegular(COFFSymbolRef Sym) { SectionChunk *SC = SparseChunks[Sym.getSectionNumber()]; if (Sym.isExternal()) { @@ -232,10 +281,17 @@ Symbol *ObjFile::createRegular(COFFSymbolRef Sym) { COFFObj->getSymbolName(Sym, Name); if (SC) return Symtab->addRegular(this, Name, Sym.getGeneric(), SC); + // For MinGW symbols named .weak.* that point to a discarded section, + // don't create an Undefined symbol. If nothing ever refers to the symbol, + // everything should be fine. If something actually refers to the symbol + // (e.g. the undefined weak alias), linking will fail due to undefined + // references at the end. + if (Config->MinGW && Name.startswith(".weak.")) + return nullptr; return Symtab->addUndefined(Name, this, false); } if (SC) - return make<DefinedRegular>(this, /*Name*/ "", false, + return make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false, /*IsExternal*/ false, Sym.getGeneric(), SC); return nullptr; } @@ -248,19 +304,24 @@ void ObjFile::initializeSymbols() { std::vector<uint32_t> PendingIndexes; PendingIndexes.reserve(NumSymbols); + DenseMap<StringRef, uint32_t> PrevailingSectionMap; std::vector<const coff_aux_section_definition *> ComdatDefs( COFFObj->getNumberOfSections() + 1); for (uint32_t I = 0; I < NumSymbols; ++I) { COFFSymbolRef COFFSym = check(COFFObj->getSymbol(I)); + bool PrevailingComdat; if (COFFSym.isUndefined()) { Symbols[I] = createUndefined(COFFSym); } else if (COFFSym.isWeakExternal()) { Symbols[I] = createUndefined(COFFSym); uint32_t TagIndex = COFFSym.getAux<coff_aux_weak_external>()->TagIndex; WeakAliases.emplace_back(Symbols[I], TagIndex); - } else if (Optional<Symbol *> OptSym = createDefined(COFFSym, ComdatDefs)) { + } else if (Optional<Symbol *> OptSym = + createDefined(COFFSym, ComdatDefs, PrevailingComdat)) { Symbols[I] = *OptSym; + if (Config->MinGW && PrevailingComdat) + recordPrevailingSymbolForMingw(COFFSym, PrevailingSectionMap); } else { // createDefined() returns None if a symbol belongs to a section that // was pending at the point when the symbol was read. This can happen in @@ -278,9 +339,12 @@ void ObjFile::initializeSymbols() { for (uint32_t I : PendingIndexes) { COFFSymbolRef Sym = check(COFFObj->getSymbol(I)); - if (auto *Def = Sym.getSectionDefinition()) + if (const coff_aux_section_definition *Def = Sym.getSectionDefinition()) { if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) readAssociativeDefinition(Sym, Def); + else if (Config->MinGW) + maybeAssociateSEHForMingw(Sym, Def, PrevailingSectionMap); + } if (SparseChunks[Sym.getSectionNumber()] == PendingComdat) { StringRef Name; COFFObj->getSymbolName(Sym, Name); @@ -306,7 +370,9 @@ Symbol *ObjFile::createUndefined(COFFSymbolRef Sym) { Optional<Symbol *> ObjFile::createDefined( COFFSymbolRef Sym, - std::vector<const coff_aux_section_definition *> &ComdatDefs) { + std::vector<const coff_aux_section_definition *> &ComdatDefs, + bool &Prevailing) { + Prevailing = false; auto GetName = [&]() { StringRef S; COFFObj->getSymbolName(Sym, S); @@ -352,12 +418,11 @@ Optional<Symbol *> ObjFile::createDefined( if (const coff_aux_section_definition *Def = ComdatDefs[SectionNumber]) { ComdatDefs[SectionNumber] = nullptr; Symbol *Leader; - bool Prevailing; if (Sym.isExternal()) { std::tie(Leader, Prevailing) = Symtab->addComdat(this, GetName(), Sym.getGeneric()); } else { - Leader = make<DefinedRegular>(this, /*Name*/ "", false, + Leader = make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false, /*IsExternal*/ false, Sym.getGeneric()); Prevailing = true; } @@ -377,7 +442,7 @@ Optional<Symbol *> ObjFile::createDefined( // leader symbol by setting the section's ComdatDefs pointer if we encounter a // non-associative comdat. if (SparseChunks[SectionNumber] == PendingComdat) { - if (auto *Def = Sym.getSectionDefinition()) { + if (const coff_aux_section_definition *Def = Sym.getSectionDefinition()) { if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) readAssociativeDefinition(Sym, Def); else @@ -385,8 +450,10 @@ Optional<Symbol *> ObjFile::createDefined( } } + // readAssociativeDefinition() writes to SparseChunks, so need to check again. if (SparseChunks[SectionNumber] == PendingComdat) return None; + return createRegular(Sym); } @@ -437,6 +504,10 @@ void ImportFile::parse() { ExternalName = ExtName; ImpSym = Symtab->addImportData(ImpName, this); + // If this was a duplicate, we logged an error but may continue; + // in this case, ImpSym is nullptr. + if (!ImpSym) + return; if (Hdr->getType() == llvm::COFF::IMPORT_CONST) static_cast<void>(Symtab->addImportData(Name, this)); diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h index 4ee4b363886f..ec802f2d0300 100644 --- a/COFF/InputFiles.h +++ b/COFF/InputFiles.h @@ -13,7 +13,9 @@ #include "Config.h" #include "lld/Common/LLVM.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/LTO/LTO.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" @@ -121,9 +123,12 @@ public: return Symbols[SymbolIndex]; } - // Returns the underying COFF file. + // Returns the underlying COFF file. COFFObjectFile *getCOFFObj() { return COFFObj.get(); } + // Whether the object was already merged into the final PDB or not + bool wasProcessedForPDB() const { return !!ModuleDBI; } + static std::vector<ObjFile *> Instances; // Flags in the absolute @feat.00 symbol if it is present. These usually @@ -144,6 +149,13 @@ public: // if we are not producing a PDB. llvm::pdb::DbiModuleDescriptorBuilder *ModuleDBI = nullptr; + const coff_section *AddrsigSec = nullptr; + + // When using Microsoft precompiled headers, this is the PCH's key. + // The same key is used by both the precompiled object, and objects using the + // precompiled object. Any difference indicates out-of-date objects. + llvm::Optional<uint32_t> PCHSignature; + private: void initializeChunks(); void initializeSymbols(); @@ -157,10 +169,24 @@ private: COFFSymbolRef COFFSym, const llvm::object::coff_aux_section_definition *Def); + void readAssociativeDefinition( + COFFSymbolRef COFFSym, + const llvm::object::coff_aux_section_definition *Def, + uint32_t ParentSection); + + void recordPrevailingSymbolForMingw( + COFFSymbolRef COFFSym, + llvm::DenseMap<StringRef, uint32_t> &PrevailingSectionMap); + + void maybeAssociateSEHForMingw( + COFFSymbolRef Sym, const llvm::object::coff_aux_section_definition *Def, + const llvm::DenseMap<StringRef, uint32_t> &PrevailingSectionMap); + llvm::Optional<Symbol *> createDefined(COFFSymbolRef Sym, std::vector<const llvm::object::coff_aux_section_definition *> - &ComdatDefs); + &ComdatDefs, + bool &PrevailingComdat); Symbol *createRegular(COFFSymbolRef Sym); Symbol *createUndefined(COFFSymbolRef Sym); diff --git a/COFF/LTO.cpp b/COFF/LTO.cpp index 93f7ba3f9e4c..92d9ff0937c0 100644 --- a/COFF/LTO.cpp +++ b/COFF/LTO.cpp @@ -60,6 +60,9 @@ static std::unique_ptr<lto::LTO> createLTO() { C.DisableVerify = true; C.DiagHandler = diagnosticHandler; C.OptLevel = Config->LTOO; + C.CPU = GetCPUStr(); + C.MAttrs = GetMAttrs(); + if (Config->SaveTemps) checkError(C.addSaveTemps(std::string(Config->OutputFile) + ".", /*UseInputModulePath*/ true)); diff --git a/COFF/MapFile.cpp b/COFF/MapFile.cpp index 6ca1b6647bd7..fd4894250223 100644 --- a/COFF/MapFile.cpp +++ b/COFF/MapFile.cpp @@ -110,7 +110,7 @@ void coff::writeMapFile(ArrayRef<OutputSection *> OutputSections) { writeHeader(OS, Sec->getRVA(), Sec->getVirtualSize(), /*Align=*/PageSize); OS << Sec->Name << '\n'; - for (Chunk *C : Sec->getChunks()) { + for (Chunk *C : Sec->Chunks) { auto *SC = dyn_cast<SectionChunk>(C); if (!SC) continue; diff --git a/COFF/MarkLive.cpp b/COFF/MarkLive.cpp index 57ae450a9138..18b1c9c2529f 100644 --- a/COFF/MarkLive.cpp +++ b/COFF/MarkLive.cpp @@ -32,13 +32,13 @@ void markLive(ArrayRef<Chunk *> Chunks) { // COMDAT section chunks are dead by default. Add non-COMDAT chunks. for (Chunk *C : Chunks) if (auto *SC = dyn_cast<SectionChunk>(C)) - if (SC->isLive()) + if (SC->Live) Worklist.push_back(SC); auto Enqueue = [&](SectionChunk *C) { - if (C->isLive()) + if (C->Live) return; - C->markLive(); + C->Live = true; Worklist.push_back(C); }; @@ -57,7 +57,7 @@ void markLive(ArrayRef<Chunk *> Chunks) { while (!Worklist.empty()) { SectionChunk *SC = Worklist.pop_back_val(); - assert(SC->isLive() && "We mark as live when pushing onto the worklist!"); + assert(SC->Live && "We mark as live when pushing onto the worklist!"); // Mark all symbols listed in the relocation table for this section. for (Symbol *B : SC->symbols()) diff --git a/COFF/MinGW.cpp b/COFF/MinGW.cpp index 2ca00587331f..b2c8c4eadca4 100644 --- a/COFF/MinGW.cpp +++ b/COFF/MinGW.cpp @@ -19,7 +19,23 @@ using namespace lld::coff; using namespace llvm; using namespace llvm::COFF; -AutoExporter::AutoExporter() { +void AutoExporter::initSymbolExcludes() { + ExcludeSymbolPrefixes = { + // Import symbols + "__imp_", + "__IMPORT_DESCRIPTOR_", + // Extra import symbols from GNU import libraries + "__nm_", + // C++ symbols + "__rtti_", + "__builtin_", + // Artifical symbols such as .refptr + ".", + }; + ExcludeSymbolSuffixes = { + "_iname", + "_NULL_THUNK_DATA", + }; if (Config->Machine == I386) { ExcludeSymbols = { "__NULL_IMPORT_DESCRIPTOR", @@ -36,9 +52,10 @@ AutoExporter::AutoExporter() { "_DllEntryPoint@12", "_DllMainCRTStartup@12", }; + ExcludeSymbolPrefixes.insert("__head_"); } else { ExcludeSymbols = { - "_NULL_IMPORT_DESCRIPTOR", + "__NULL_IMPORT_DESCRIPTOR", "_pei386_runtime_relocator", "do_pseudo_reloc", "impure_ptr", @@ -52,8 +69,11 @@ AutoExporter::AutoExporter() { "DllEntryPoint", "DllMainCRTStartup", }; + ExcludeSymbolPrefixes.insert("_head_"); } +} +AutoExporter::AutoExporter() { ExcludeLibs = { "libgcc", "libgcc_s", @@ -64,6 +84,7 @@ AutoExporter::AutoExporter() { "libsupc++", "libobjc", "libgcj", + "libclang_rt.builtins", "libclang_rt.builtins-aarch64", "libclang_rt.builtins-arm", "libclang_rt.builtins-i386", @@ -90,6 +111,13 @@ AutoExporter::AutoExporter() { }; } +void AutoExporter::addWholeArchive(StringRef Path) { + StringRef LibName = sys::path::filename(Path); + // Drop the file extension, to match the processing below. + LibName = LibName.substr(0, LibName.rfind('.')); + ExcludeLibs.erase(LibName); +} + bool AutoExporter::shouldExport(Defined *Sym) const { if (!Sym || !Sym->isLive() || !Sym->getChunk()) return false; @@ -101,10 +129,12 @@ bool AutoExporter::shouldExport(Defined *Sym) const { if (ExcludeSymbols.count(Sym->getName())) return false; - // Don't export anything that looks like an import symbol (which also can be - // a manually defined data symbol with such a name). - if (Sym->getName().startswith("__imp_")) - return false; + for (StringRef Prefix : ExcludeSymbolPrefixes.keys()) + if (Sym->getName().startswith(Prefix)) + return false; + for (StringRef Suffix : ExcludeSymbolSuffixes.keys()) + if (Sym->getName().endswith(Suffix)) + return false; // If a corresponding __imp_ symbol exists and is defined, don't export it. if (Symtab->find(("__imp_" + Sym->getName()).str())) diff --git a/COFF/MinGW.h b/COFF/MinGW.h index fe6cc5588ebc..f9c5e3e5c2cc 100644 --- a/COFF/MinGW.h +++ b/COFF/MinGW.h @@ -23,7 +23,13 @@ class AutoExporter { public: AutoExporter(); + void initSymbolExcludes(); + + void addWholeArchive(StringRef Path); + llvm::StringSet<> ExcludeSymbols; + llvm::StringSet<> ExcludeSymbolPrefixes; + llvm::StringSet<> ExcludeSymbolSuffixes; llvm::StringSet<> ExcludeLibs; llvm::StringSet<> ExcludeObjects; diff --git a/COFF/Options.td b/COFF/Options.td index 871bad8bd655..acf1bc5c8b1d 100644 --- a/COFF/Options.td +++ b/COFF/Options.td @@ -66,13 +66,18 @@ def wholearchive_file : P<"wholearchive", "Include all object files from this ar def disallowlib : Joined<["/", "-", "-?"], "disallowlib:">, Alias<nodefaultlib>; -def manifest : F<"manifest">; -def manifest_colon : P<"manifest", "Create manifest file">; +def manifest : F<"manifest">, HelpText<"Create .manifest file">; +def manifest_colon : P< + "manifest", + "NO disables manifest output; EMBED[,ID=#] embeds manifest as resource in the image">; def manifestuac : P<"manifestuac", "User access control">; -def manifestfile : P<"manifestfile", "Manifest file path">; -def manifestdependency : P<"manifestdependency", - "Attributes for <dependency> in manifest file">; -def manifestinput : P<"manifestinput", "Specify manifest file">; +def manifestfile : P<"manifestfile", "Manifest output path, with /manifest">; +def manifestdependency : P< + "manifestdependency", + "Attributes for <dependency> element in manifest file; implies /manifest">; +def manifestinput : P< + "manifestinput", + "Additional manifest inputs; only valid with /manifest:embed">; // We cannot use multiclass P because class name "incl" is different // from its command line option name. We do this because "include" is @@ -85,22 +90,28 @@ 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 debug_opt : P<"debug", "Embed a symbol table in the image with option">; 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 nodefaultlib_all : F<"nodefaultlib">, + HelpText<"Remove all default libraries">; +def noentry : F<"noentry">, + HelpText<"Don't add reference to DllMainCRTStartup; only valid with /dll">; def profile : F<"profile">; -def repro : F<"Brepro">, HelpText<"Use a hash of the executable as the PE header timestamp">; +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">; def wholearchive_flag : F<"wholearchive">; def force : F<"force">, + HelpText<"Allow undefined and multiply defined symbols when creating executables">; +def force_unresolved : F<"force:unresolved">, HelpText<"Allow undefined symbols when creating executables">; -def force_unresolved : F<"force:unresolved">; +def force_multiple : F<"force:multiple">, + HelpText<"Allow multiply defined symbols when creating executables">; defm WX : B<"WX", "Treat warnings as errors", "Don't treat warnings as errors">; defm allowbind : B<"allowbind", "Enable DLL binding (default)", @@ -139,13 +150,9 @@ def help : F<"help">; 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">; diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp index 766bf3f6b456..7862b6ce4cc5 100644 --- a/COFF/PDB.cpp +++ b/COFF/PDB.cpp @@ -16,12 +16,14 @@ #include "Writer.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Timer.h" +#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" #include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" #include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" #include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/RecordName.h" #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h" #include "llvm/DebugInfo/CodeView/SymbolSerializer.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" #include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h" @@ -48,8 +50,10 @@ #include "llvm/Object/CVDebugRecord.h" #include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/Endian.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/JamCRC.h" +#include "llvm/Support/Parallel.h" #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" #include <memory> @@ -79,9 +83,14 @@ struct CVIndexMap { SmallVector<TypeIndex, 0> TPIMap; SmallVector<TypeIndex, 0> IPIMap; bool IsTypeServerMap = false; + bool IsPrecompiledTypeMap = false; }; +class DebugSHandler; + class PDBLinker { + friend DebugSHandler; + public: PDBLinker(SymbolTable *Symtab) : Alloc(), Symtab(Symtab), Builder(Alloc), TypeTable(Alloc), @@ -93,7 +102,7 @@ public: } /// Emit the basic PDB structure: initial streams, headers, etc. - void initialize(const llvm::codeview::DebugInfo &BuildId); + void initialize(llvm::codeview::DebugInfo *BuildId); /// Add natvis files specified on the command line. void addNatvisFiles(); @@ -101,8 +110,10 @@ public: /// Link CodeView from each object file in the symbol table into the PDB. void addObjectsToPDB(); - /// Link CodeView from a single object file into the PDB. - void addObjFile(ObjFile *File); + /// Link CodeView from a single object file into the target (output) PDB. + /// When a precompiled headers object is linked, its TPI map might be provided + /// externally. + void addObjFile(ObjFile *File, CVIndexMap *ExternIndexMap = nullptr); /// Produce a mapping from the type and item indices used in the object /// file to those in the destination PDB. @@ -115,18 +126,60 @@ public: /// If the object does not use a type server PDB (compiled with /Z7), we merge /// all the type and item records from the .debug$S stream and fill in the /// caller-provided ObjectIndexMap. - Expected<const CVIndexMap&> mergeDebugT(ObjFile *File, - CVIndexMap &ObjectIndexMap); + Expected<const CVIndexMap &> mergeDebugT(ObjFile *File, + CVIndexMap *ObjectIndexMap); + + /// Reads and makes available a PDB. + Expected<const CVIndexMap &> maybeMergeTypeServerPDB(ObjFile *File, + const CVType &FirstType); + + /// Merges a precompiled headers TPI map into the current TPI map. The + /// precompiled headers object will also be loaded and remapped in the + /// process. + Expected<const CVIndexMap &> + mergeInPrecompHeaderObj(ObjFile *File, const CVType &FirstType, + CVIndexMap *ObjectIndexMap); + + /// Reads and makes available a precompiled headers object. + /// + /// This is a requirement for objects compiled with cl.exe /Yu. In that + /// case, the referenced object (which was compiled with /Yc) has to be loaded + /// first. This is mainly because the current object's TPI stream has external + /// references to the precompiled headers object. + /// + /// If the precompiled headers object was already loaded, this function will + /// simply return its (remapped) TPI map. + Expected<const CVIndexMap &> aquirePrecompObj(ObjFile *File, + PrecompRecord Precomp); + + /// Adds a precompiled headers object signature -> TPI mapping. + std::pair<CVIndexMap &, bool /*already there*/> + registerPrecompiledHeaders(uint32_t Signature); - Expected<const CVIndexMap&> maybeMergeTypeServerPDB(ObjFile *File, - TypeServer2Record &TS); + void mergeSymbolRecords(ObjFile *File, const CVIndexMap &IndexMap, + std::vector<ulittle32_t *> &StringTableRefs, + BinaryStreamRef SymData); /// Add the section map and section contributions to the PDB. void addSections(ArrayRef<OutputSection *> OutputSections, ArrayRef<uint8_t> SectionTable); - /// Write the PDB to disk. - void commit(); + /// Get the type table or the global type table if /DEBUG:GHASH is enabled. + TypeCollection &getTypeTable() { + if (Config->DebugGHashes) + return GlobalTypeTable; + return TypeTable; + } + + /// Get the ID table or the global ID table if /DEBUG:GHASH is enabled. + TypeCollection &getIDTable() { + if (Config->DebugGHashes) + return GlobalIDTable; + return IDTable; + } + + /// Write the PDB to disk and store the Guid generated for it in *Guid. + void commit(codeview::GUID *Guid); private: BumpPtrAllocator Alloc; @@ -161,14 +214,95 @@ private: std::vector<pdb::SecMapEntry> SectionMap; /// Type index mappings of type server PDBs that we've loaded so far. - std::map<GUID, CVIndexMap> TypeServerIndexMappings; + std::map<codeview::GUID, CVIndexMap> TypeServerIndexMappings; + + /// Type index mappings of precompiled objects type map that we've loaded so + /// far. + std::map<uint32_t, CVIndexMap> PrecompTypeIndexMappings; /// List of TypeServer PDBs which cannot be loaded. /// Cached to prevent repeated load attempts. - std::set<GUID> MissingTypeServerPDBs; + std::map<codeview::GUID, std::string> MissingTypeServerPDBs; +}; + +class DebugSHandler { + PDBLinker &Linker; + + /// The object file whose .debug$S sections we're processing. + ObjFile &File; + + /// The result of merging type indices. + const CVIndexMap &IndexMap; + + /// The DEBUG_S_STRINGTABLE subsection. These strings are referred to by + /// index from other records in the .debug$S section. All of these strings + /// need to be added to the global PDB string table, and all references to + /// these strings need to have their indices re-written to refer to the + /// global PDB string table. + DebugStringTableSubsectionRef CVStrTab; + + /// The DEBUG_S_FILECHKSMS subsection. As above, these are referred to + /// by other records in the .debug$S section and need to be merged into the + /// PDB. + DebugChecksumsSubsectionRef Checksums; + + /// The DEBUG_S_FRAMEDATA subsection(s). There can be more than one of + /// these and they need not appear in any specific order. However, they + /// contain string table references which need to be re-written, so we + /// collect them all here and re-write them after all subsections have been + /// discovered and processed. + std::vector<DebugFrameDataSubsectionRef> NewFpoFrames; + + /// Pointers to raw memory that we determine have string table references + /// that need to be re-written. We first process all .debug$S subsections + /// to ensure that we can handle subsections written in any order, building + /// up this list as we go. At the end, we use the string table (which must + /// have been discovered by now else it is an error) to re-write these + /// references. + std::vector<ulittle32_t *> StringTableReferences; + +public: + DebugSHandler(PDBLinker &Linker, ObjFile &File, const CVIndexMap &IndexMap) + : Linker(Linker), File(File), IndexMap(IndexMap) {} + + void handleDebugS(lld::coff::SectionChunk &DebugS); + void finish(); }; } +// Visual Studio's debugger requires absolute paths in various places in the +// PDB to work without additional configuration: +// https://docs.microsoft.com/en-us/visualstudio/debugger/debug-source-files-common-properties-solution-property-pages-dialog-box +static void pdbMakeAbsolute(SmallVectorImpl<char> &FileName) { + // The default behavior is to produce paths that are valid within the context + // of the machine that you perform the link on. If the linker is running on + // a POSIX system, we will output absolute POSIX paths. If the linker is + // running on a Windows system, we will output absolute Windows paths. If the + // user desires any other kind of behavior, they should explicitly pass + // /pdbsourcepath, in which case we will treat the exact string the user + // passed in as the gospel and not normalize, canonicalize it. + if (sys::path::is_absolute(FileName, sys::path::Style::windows) || + sys::path::is_absolute(FileName, sys::path::Style::posix)) + return; + + // It's not absolute in any path syntax. Relative paths necessarily refer to + // the local file system, so we can make it native without ending up with a + // nonsensical path. + sys::path::native(FileName); + if (Config->PDBSourcePath.empty()) { + sys::fs::make_absolute(FileName); + return; + } + // Only apply native and dot removal to the relative file path. We want to + // leave the path the user specified untouched since we assume they specified + // it for a reason. + sys::path::remove_dots(FileName, /*remove_dot_dots=*/true); + + SmallString<128> AbsoluteFileName = Config->PDBSourcePath; + sys::path::append(AbsoluteFileName, FileName); + FileName = std::move(AbsoluteFileName); +} + static SectionChunk *findByName(ArrayRef<SectionChunk *> Sections, StringRef Name) { for (SectionChunk *C : Sections) @@ -242,27 +376,79 @@ static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder, }); } -static Optional<TypeServer2Record> -maybeReadTypeServerRecord(CVTypeArray &Types) { - auto I = Types.begin(); - if (I == Types.end()) - return None; - const CVType &Type = *I; - if (Type.kind() != LF_TYPESERVER2) - return None; - TypeServer2Record TS; - if (auto EC = TypeDeserializer::deserializeAs(const_cast<CVType &>(Type), TS)) - fatal("error reading type server record: " + toString(std::move(EC))); - return std::move(TS); +// OBJs usually start their symbol stream with a S_OBJNAME record. This record +// also contains the signature/key of the current PCH session. The signature +// must be same for all objects which depend on the precompiled object. +// Recompiling the precompiled headers will generate a new PCH key and thus +// invalidate all the dependent objects. +static uint32_t extractPCHSignature(ObjFile *File) { + auto DbgIt = find_if(File->getDebugChunks(), [](SectionChunk *C) { + return C->getSectionName() == ".debug$S"; + }); + if (!DbgIt) + return 0; + + ArrayRef<uint8_t> Contents = + consumeDebugMagic((*DbgIt)->getContents(), ".debug$S"); + DebugSubsectionArray Subsections; + BinaryStreamReader Reader(Contents, support::little); + ExitOnErr(Reader.readArray(Subsections, Contents.size())); + + for (const DebugSubsectionRecord &SS : Subsections) { + if (SS.kind() != DebugSubsectionKind::Symbols) + continue; + + // If it's there, the S_OBJNAME record shall come first in the stream. + Expected<CVSymbol> Sym = readSymbolFromStream(SS.getRecordData(), 0); + if (!Sym) { + consumeError(Sym.takeError()); + continue; + } + if (auto ObjName = SymbolDeserializer::deserializeAs<ObjNameSym>(Sym.get())) + return ObjName->Signature; + } + return 0; } -Expected<const CVIndexMap&> PDBLinker::mergeDebugT(ObjFile *File, - CVIndexMap &ObjectIndexMap) { +Expected<const CVIndexMap &> +PDBLinker::mergeDebugT(ObjFile *File, CVIndexMap *ObjectIndexMap) { ScopedTimer T(TypeMergingTimer); + bool IsPrecompiledHeader = false; + ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$T"); + if (Data.empty()) { + // Try again, Microsoft precompiled headers use .debug$P instead of + // .debug$T + Data = getDebugSection(File, ".debug$P"); + IsPrecompiledHeader = true; + } if (Data.empty()) - return ObjectIndexMap; + return *ObjectIndexMap; // no debug info + + // Precompiled headers objects need to save the index map for further + // reference by other objects which use the precompiled headers. + if (IsPrecompiledHeader) { + uint32_t PCHSignature = extractPCHSignature(File); + if (PCHSignature == 0) + fatal("No signature found for the precompiled headers OBJ (" + + File->getName() + ")"); + + // When a precompiled headers object comes first on the command-line, we + // update the mapping here. Otherwise, if an object referencing the + // precompiled headers object comes first, the mapping is created in + // aquirePrecompObj(), thus we would skip this block. + if (!ObjectIndexMap->IsPrecompiledTypeMap) { + auto R = registerPrecompiledHeaders(PCHSignature); + if (R.second) + fatal( + "A precompiled headers OBJ with the same signature was already " + "provided! (" + + File->getName() + ")"); + + ObjectIndexMap = &R.first; + } + } BinaryByteStream Stream(Data, support::little); CVTypeArray Types; @@ -270,13 +456,32 @@ Expected<const CVIndexMap&> PDBLinker::mergeDebugT(ObjFile *File, if (auto EC = Reader.readArray(Types, Reader.getLength())) fatal("Reader::readArray failed: " + toString(std::move(EC))); - // Look through type servers. If we've already seen this type server, don't - // merge any type information. - if (Optional<TypeServer2Record> TS = maybeReadTypeServerRecord(Types)) - return maybeMergeTypeServerPDB(File, *TS); + auto FirstType = Types.begin(); + if (FirstType == Types.end()) + return *ObjectIndexMap; + + if (FirstType->kind() == LF_TYPESERVER2) { + // Look through type servers. If we've already seen this type server, + // don't merge any type information. + return maybeMergeTypeServerPDB(File, *FirstType); + } else if (FirstType->kind() == LF_PRECOMP) { + // This object was compiled with /Yu, so process the corresponding + // precompiled headers object (/Yc) first. Some type indices in the current + // object are referencing data in the precompiled headers object, so we need + // both to be loaded. + auto E = mergeInPrecompHeaderObj(File, *FirstType, ObjectIndexMap); + if (!E) + return E.takeError(); + + // Drop LF_PRECOMP record from the input stream, as it needs to be replaced + // with the precompiled headers object type stream. + // Note that we can't just call Types.drop_front(), as we explicitly want to + // rebase the stream. + Types.setUnderlyingStream( + Types.getUnderlyingStream().drop_front(FirstType->RecordData.size())); + } - // This is a /Z7 object. Fill in the temporary, caller-provided - // ObjectIndexMap. + // Fill in the temporary, caller-provided ObjectIndexMap. if (Config->DebugGHashes) { ArrayRef<GloballyHashedType> Hashes; std::vector<GloballyHashedType> OwnedHashes; @@ -288,20 +493,28 @@ Expected<const CVIndexMap&> PDBLinker::mergeDebugT(ObjFile *File, } if (auto Err = mergeTypeAndIdRecords(GlobalIDTable, GlobalTypeTable, - ObjectIndexMap.TPIMap, Types, Hashes)) + ObjectIndexMap->TPIMap, Types, Hashes, + File->PCHSignature)) fatal("codeview::mergeTypeAndIdRecords failed: " + toString(std::move(Err))); } else { - if (auto Err = mergeTypeAndIdRecords(IDTable, TypeTable, - ObjectIndexMap.TPIMap, Types)) + if (auto Err = + mergeTypeAndIdRecords(IDTable, TypeTable, ObjectIndexMap->TPIMap, + Types, File->PCHSignature)) fatal("codeview::mergeTypeAndIdRecords failed: " + toString(std::move(Err))); } - return ObjectIndexMap; + return *ObjectIndexMap; } static Expected<std::unique_ptr<pdb::NativeSession>> -tryToLoadPDB(const GUID &GuidFromObj, StringRef TSPath) { +tryToLoadPDB(const codeview::GUID &GuidFromObj, StringRef TSPath) { + // Ensure the file exists before anything else. We want to return ENOENT, + // "file not found", even if the path points to a removable device (in which + // case the return message would be EAGAIN, "resource unavailable try again") + if (!llvm::sys::fs::exists(TSPath)) + return errorCodeToError(std::error_code(ENOENT, std::generic_category())); + ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = MemoryBuffer::getFile( TSPath, /*FileSize=*/-1, /*RequiresNullTerminator=*/false); if (!MBOrErr) @@ -326,21 +539,27 @@ tryToLoadPDB(const GUID &GuidFromObj, StringRef TSPath) { // PDB file doesn't mean it matches. For it to match the InfoStream's GUID // must match the GUID specified in the TypeServer2 record. if (ExpectedInfo->getGuid() != GuidFromObj) - return make_error<pdb::GenericError>( - pdb::generic_error_code::type_server_not_found, TSPath); + return make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date); return std::move(NS); } -Expected<const CVIndexMap&> PDBLinker::maybeMergeTypeServerPDB(ObjFile *File, - TypeServer2Record &TS) { - const GUID& TSId = TS.getGuid(); +Expected<const CVIndexMap &> +PDBLinker::maybeMergeTypeServerPDB(ObjFile *File, const CVType &FirstType) { + TypeServer2Record TS; + if (auto EC = + TypeDeserializer::deserializeAs(const_cast<CVType &>(FirstType), TS)) + fatal("error reading record: " + toString(std::move(EC))); + + const codeview::GUID &TSId = TS.getGuid(); StringRef TSPath = TS.getName(); // First, check if the PDB has previously failed to load. - if (MissingTypeServerPDBs.count(TSId)) - return make_error<pdb::GenericError>( - pdb::generic_error_code::type_server_not_found, TSPath); + auto PrevErr = MissingTypeServerPDBs.find(TSId); + if (PrevErr != MissingTypeServerPDBs.end()) + return createFileError( + TSPath, + make_error<StringError>(PrevErr->second, inconvertibleErrorCode())); // Second, check if we already loaded a PDB with this GUID. Return the type // index mapping if we have it. @@ -355,20 +574,39 @@ Expected<const CVIndexMap&> PDBLinker::maybeMergeTypeServerPDB(ObjFile *File, // Check for a PDB at: // 1. The given file path // 2. Next to the object file or archive file - auto ExpectedSession = tryToLoadPDB(TSId, TSPath); - if (!ExpectedSession) { - consumeError(ExpectedSession.takeError()); - StringRef LocalPath = - !File->ParentName.empty() ? File->ParentName : File->getName(); - SmallString<128> Path = sys::path::parent_path(LocalPath); - sys::path::append( - Path, sys::path::filename(TSPath, sys::path::Style::windows)); - ExpectedSession = tryToLoadPDB(TSId, Path); - } + auto ExpectedSession = handleExpected( + tryToLoadPDB(TSId, TSPath), + [&]() { + StringRef LocalPath = + !File->ParentName.empty() ? File->ParentName : File->getName(); + SmallString<128> Path = sys::path::parent_path(LocalPath); + // Currently, type server PDBs are only created by cl, which only runs + // on Windows, so we can assume type server paths are Windows style. + sys::path::append( + Path, sys::path::filename(TSPath, sys::path::Style::windows)); + return tryToLoadPDB(TSId, Path); + }, + [&](std::unique_ptr<ECError> EC) -> Error { + auto SysErr = EC->convertToErrorCode(); + // Only re-try loading if the previous error was "No such file or + // directory" + if (SysErr.category() == std::generic_category() && + SysErr.value() == ENOENT) + return Error::success(); + return Error(std::move(EC)); + }); + if (auto E = ExpectedSession.takeError()) { TypeServerIndexMappings.erase(TSId); - MissingTypeServerPDBs.emplace(TSId); - return std::move(E); + + // Flatten the error to a string, for later display, if the error occurs + // again on the same PDB. + std::string ErrMsg; + raw_string_ostream S(ErrMsg); + S << E; + MissingTypeServerPDBs.emplace(TSId, S.str()); + + return createFileError(TSPath, std::move(E)); } pdb::NativeSession *Session = ExpectedSession->get(); @@ -394,9 +632,10 @@ Expected<const CVIndexMap&> PDBLinker::maybeMergeTypeServerPDB(ObjFile *File, auto IpiHashes = GloballyHashedType::hashIds(ExpectedIpi->typeArray(), TpiHashes); + Optional<uint32_t> EndPrecomp; // Merge TPI first, because the IPI stream will reference type indices. if (auto Err = mergeTypeRecords(GlobalTypeTable, IndexMap.TPIMap, - ExpectedTpi->typeArray(), TpiHashes)) + ExpectedTpi->typeArray(), TpiHashes, EndPrecomp)) fatal("codeview::mergeTypeRecords failed: " + toString(std::move(Err))); // Merge IPI. @@ -419,6 +658,103 @@ Expected<const CVIndexMap&> PDBLinker::maybeMergeTypeServerPDB(ObjFile *File, return IndexMap; } +Expected<const CVIndexMap &> +PDBLinker::mergeInPrecompHeaderObj(ObjFile *File, const CVType &FirstType, + CVIndexMap *ObjectIndexMap) { + PrecompRecord Precomp; + if (auto EC = TypeDeserializer::deserializeAs(const_cast<CVType &>(FirstType), + Precomp)) + fatal("error reading record: " + toString(std::move(EC))); + + auto E = aquirePrecompObj(File, Precomp); + if (!E) + return E.takeError(); + + const CVIndexMap &PrecompIndexMap = *E; + assert(PrecompIndexMap.IsPrecompiledTypeMap); + + if (PrecompIndexMap.TPIMap.empty()) + return PrecompIndexMap; + + assert(Precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex); + assert(Precomp.getTypesCount() <= PrecompIndexMap.TPIMap.size()); + // Use the previously remapped index map from the precompiled headers. + ObjectIndexMap->TPIMap.append(PrecompIndexMap.TPIMap.begin(), + PrecompIndexMap.TPIMap.begin() + + Precomp.getTypesCount()); + return *ObjectIndexMap; +} + +static bool equals_path(StringRef path1, StringRef path2) { +#if defined(_WIN32) + return path1.equals_lower(path2); +#else + return path1.equals(path2); +#endif +} + +// Find by name an OBJ provided on the command line +static ObjFile *findObjByName(StringRef FileNameOnly) { + SmallString<128> CurrentPath; + + for (ObjFile *F : ObjFile::Instances) { + StringRef CurrentFileName = sys::path::filename(F->getName()); + + // Compare based solely on the file name (link.exe behavior) + if (equals_path(CurrentFileName, FileNameOnly)) + return F; + } + return nullptr; +} + +std::pair<CVIndexMap &, bool /*already there*/> +PDBLinker::registerPrecompiledHeaders(uint32_t Signature) { + auto Insertion = PrecompTypeIndexMappings.insert({Signature, CVIndexMap()}); + CVIndexMap &IndexMap = Insertion.first->second; + if (!Insertion.second) + return {IndexMap, true}; + // Mark this map as a precompiled types map. + IndexMap.IsPrecompiledTypeMap = true; + return {IndexMap, false}; +} + +Expected<const CVIndexMap &> +PDBLinker::aquirePrecompObj(ObjFile *File, PrecompRecord Precomp) { + // First, check if we already loaded the precompiled headers object with this + // signature. Return the type index mapping if we've already seen it. + auto R = registerPrecompiledHeaders(Precomp.getSignature()); + if (R.second) + return R.first; + + CVIndexMap &IndexMap = R.first; + + // Cross-compile warning: given that Clang doesn't generate LF_PRECOMP + // records, we assume the OBJ comes from a Windows build of cl.exe. Thusly, + // the paths embedded in the OBJs are in the Windows format. + SmallString<128> PrecompFileName = sys::path::filename( + Precomp.getPrecompFilePath(), sys::path::Style::windows); + + // link.exe requires that a precompiled headers object must always be provided + // on the command-line, even if that's not necessary. + auto PrecompFile = findObjByName(PrecompFileName); + if (!PrecompFile) + return createFileError( + PrecompFileName.str(), + make_error<pdb::PDBError>(pdb::pdb_error_code::external_cmdline_ref)); + + addObjFile(PrecompFile, &IndexMap); + + if (!PrecompFile->PCHSignature) + fatal(PrecompFile->getName() + " is not a precompiled headers object"); + + if (Precomp.getSignature() != PrecompFile->PCHSignature.getValueOr(0)) + return createFileError( + Precomp.getPrecompFilePath().str(), + make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date)); + + return IndexMap; +} + static bool remapTypeIndex(TypeIndex &TI, ArrayRef<TypeIndex> TypeIndexMap) { if (TI.isSimple()) return true; @@ -429,9 +765,11 @@ static bool remapTypeIndex(TypeIndex &TI, ArrayRef<TypeIndex> TypeIndexMap) { } static void remapTypesInSymbolRecord(ObjFile *File, SymbolKind SymKind, - MutableArrayRef<uint8_t> Contents, + MutableArrayRef<uint8_t> RecordBytes, const CVIndexMap &IndexMap, ArrayRef<TiReference> TypeRefs) { + MutableArrayRef<uint8_t> Contents = + RecordBytes.drop_front(sizeof(RecordPrefix)); for (const TiReference &Ref : TypeRefs) { unsigned ByteSize = Ref.Count * sizeof(TypeIndex); if (Contents.size() < Ref.Offset + ByteSize) @@ -477,7 +815,7 @@ recordStringTableReferences(SymbolKind Kind, MutableArrayRef<uint8_t> Contents, switch (Kind) { case SymbolKind::S_FILESTATIC: // FileStaticSym::ModFileOffset - recordStringTableReferenceAtOffset(Contents, 4, StrTableRefs); + recordStringTableReferenceAtOffset(Contents, 8, StrTableRefs); break; case SymbolKind::S_DEFRANGE: case SymbolKind::S_DEFRANGE_SUBFIELD: @@ -542,58 +880,26 @@ static void translateIdSymbols(MutableArrayRef<uint8_t> &RecordData, /// Copy the symbol record. In a PDB, symbol records must be 4 byte aligned. /// The object file may not be aligned. -static MutableArrayRef<uint8_t> copySymbolForPdb(const CVSymbol &Sym, - BumpPtrAllocator &Alloc) { +static MutableArrayRef<uint8_t> +copyAndAlignSymbol(const CVSymbol &Sym, MutableArrayRef<uint8_t> &AlignedMem) { size_t Size = alignTo(Sym.length(), alignOf(CodeViewContainer::Pdb)); assert(Size >= 4 && "record too short"); assert(Size <= MaxRecordLength && "record too long"); - void *Mem = Alloc.Allocate(Size, 4); + assert(AlignedMem.size() >= Size && "didn't preallocate enough"); // Copy the symbol record and zero out any padding bytes. - MutableArrayRef<uint8_t> NewData(reinterpret_cast<uint8_t *>(Mem), Size); + MutableArrayRef<uint8_t> NewData = AlignedMem.take_front(Size); + AlignedMem = AlignedMem.drop_front(Size); memcpy(NewData.data(), Sym.data().data(), Sym.length()); memset(NewData.data() + Sym.length(), 0, Size - Sym.length()); // Update the record prefix length. It should point to the beginning of the // next record. - auto *Prefix = reinterpret_cast<RecordPrefix *>(Mem); + auto *Prefix = reinterpret_cast<RecordPrefix *>(NewData.data()); Prefix->RecordLen = Size - 2; return NewData; } -/// Return true if this symbol opens a scope. This implies that the symbol has -/// "parent" and "end" fields, which contain the offset of the S_END or -/// S_INLINESITE_END record. -static bool symbolOpensScope(SymbolKind Kind) { - switch (Kind) { - case SymbolKind::S_GPROC32: - case SymbolKind::S_LPROC32: - case SymbolKind::S_LPROC32_ID: - case SymbolKind::S_GPROC32_ID: - case SymbolKind::S_BLOCK32: - case SymbolKind::S_SEPCODE: - case SymbolKind::S_THUNK32: - case SymbolKind::S_INLINESITE: - case SymbolKind::S_INLINESITE2: - return true; - default: - break; - } - return false; -} - -static bool symbolEndsScope(SymbolKind Kind) { - switch (Kind) { - case SymbolKind::S_END: - case SymbolKind::S_PROC_ID_END: - case SymbolKind::S_INLINESITE_END: - return true; - default: - break; - } - return false; -} - struct ScopeRecord { ulittle32_t PtrParent; ulittle32_t PtrEnd; @@ -625,11 +931,10 @@ static void scopeStackClose(SmallVectorImpl<SymbolScope> &Stack, S.OpeningRecord->PtrEnd = CurOffset; } -static bool symbolGoesInModuleStream(const CVSymbol &Sym) { +static bool symbolGoesInModuleStream(const CVSymbol &Sym, bool IsGlobalScope) { switch (Sym.kind()) { case SymbolKind::S_GDATA32: case SymbolKind::S_CONSTANT: - case SymbolKind::S_UDT: // We really should not be seeing S_PROCREF and S_LPROCREF in the first place // since they are synthesized by the linker in response to S_GPROC32 and // S_LPROC32, but if we do see them, don't put them in the module stream I @@ -637,6 +942,9 @@ static bool symbolGoesInModuleStream(const CVSymbol &Sym) { case SymbolKind::S_PROCREF: case SymbolKind::S_LPROCREF: return false; + // S_UDT records go in the module stream if it is not a global S_UDT. + case SymbolKind::S_UDT: + return !IsGlobalScope; // S_GDATA32 does not go in the module stream, but S_LDATA32 does. case SymbolKind::S_LDATA32: default: @@ -644,7 +952,7 @@ static bool symbolGoesInModuleStream(const CVSymbol &Sym) { } } -static bool symbolGoesInGlobalsStream(const CVSymbol &Sym) { +static bool symbolGoesInGlobalsStream(const CVSymbol &Sym, bool IsGlobalScope) { switch (Sym.kind()) { case SymbolKind::S_CONSTANT: case SymbolKind::S_GDATA32: @@ -658,20 +966,16 @@ static bool symbolGoesInGlobalsStream(const CVSymbol &Sym) { case SymbolKind::S_PROCREF: case SymbolKind::S_LPROCREF: return true; - // FIXME: For now, we drop all S_UDT symbols (i.e. they don't go in the - // globals stream or the modules stream). These have special handling which - // needs more investigation before we can get right, but by putting them all - // into the globals stream WinDbg fails to display local variables of class - // types saying that it cannot find the type Foo *. So as a stopgap just to - // keep things working, we drop them. + // S_UDT records go in the globals stream if it is a global S_UDT. case SymbolKind::S_UDT: + return IsGlobalScope; default: return false; } } -static void addGlobalSymbol(pdb::GSIStreamBuilder &Builder, ObjFile &File, - const CVSymbol &Sym) { +static void addGlobalSymbol(pdb::GSIStreamBuilder &Builder, uint16_t ModIndex, + unsigned SymOffset, const CVSymbol &Sym) { switch (Sym.kind()) { case SymbolKind::S_CONSTANT: case SymbolKind::S_UDT: @@ -687,12 +991,12 @@ static void addGlobalSymbol(pdb::GSIStreamBuilder &Builder, ObjFile &File, if (Sym.kind() == SymbolKind::S_LPROC32) K = SymbolRecordKind::LocalProcRef; ProcRefSym PS(K); - PS.Module = static_cast<uint16_t>(File.ModuleDBI->getModuleIndex()); + PS.Module = ModIndex; // For some reason, MSVC seems to add one to this value. ++PS.Module; PS.Name = getSymbolName(Sym); PS.SumName = 0; - PS.SymOffset = File.ModuleDBI->getNextSymbolOffset(); + PS.SymOffset = SymOffset; Builder.addGlobalSymbol(PS); break; } @@ -701,20 +1005,62 @@ static void addGlobalSymbol(pdb::GSIStreamBuilder &Builder, ObjFile &File, } } -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. +void PDBLinker::mergeSymbolRecords(ObjFile *File, const CVIndexMap &IndexMap, + std::vector<ulittle32_t *> &StringTableRefs, + BinaryStreamRef SymData) { ArrayRef<uint8_t> SymsBuffer; cantFail(SymData.readBytes(0, SymData.getLength(), SymsBuffer)); SmallVector<SymbolScope, 4> Scopes; + // Iterate every symbol to check if any need to be realigned, and if so, how + // much space we need to allocate for them. + bool NeedsRealignment = false; + unsigned TotalRealignedSize = 0; auto EC = forEachCodeViewRecord<CVSymbol>( - SymsBuffer, [&](const CVSymbol &Sym) -> llvm::Error { + SymsBuffer, [&](CVSymbol Sym) -> llvm::Error { + unsigned RealignedSize = + alignTo(Sym.length(), alignOf(CodeViewContainer::Pdb)); + NeedsRealignment |= RealignedSize != Sym.length(); + TotalRealignedSize += RealignedSize; + return Error::success(); + }); + + // If any of the symbol record lengths was corrupt, ignore them all, warn + // about it, and move on. + if (EC) { + warn("corrupt symbol records in " + File->getName()); + consumeError(std::move(EC)); + return; + } + + // If any symbol needed realignment, allocate enough contiguous memory for + // them all. Typically symbol subsections are small enough that this will not + // cause fragmentation. + MutableArrayRef<uint8_t> AlignedSymbolMem; + if (NeedsRealignment) { + void *AlignedData = + Alloc.Allocate(TotalRealignedSize, alignOf(CodeViewContainer::Pdb)); + AlignedSymbolMem = makeMutableArrayRef( + reinterpret_cast<uint8_t *>(AlignedData), TotalRealignedSize); + } + + // Iterate again, this time doing the real work. + unsigned CurSymOffset = File->ModuleDBI->getNextSymbolOffset(); + ArrayRef<uint8_t> BulkSymbols; + cantFail(forEachCodeViewRecord<CVSymbol>( + SymsBuffer, [&](CVSymbol Sym) -> llvm::Error { + // Align the record if required. + MutableArrayRef<uint8_t> RecordBytes; + if (NeedsRealignment) { + RecordBytes = copyAndAlignSymbol(Sym, AlignedSymbolMem); + Sym = CVSymbol(Sym.kind(), RecordBytes); + } else { + // Otherwise, we can actually mutate the symbol directly, since we + // copied it to apply relocations. + RecordBytes = makeMutableArrayRef( + const_cast<uint8_t *>(Sym.data().data()), Sym.length()); + } + // Discover type index references in the record. Skip it if we don't // know where they are. SmallVector<TiReference, 32> TypeRefs; @@ -724,57 +1070,62 @@ static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjFile *File, return Error::success(); } - // 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, + remapTypesInSymbolRecord(File, Sym.kind(), RecordBytes, IndexMap, TypeRefs); // An object file may have S_xxx_ID symbols, but these get converted to // "real" symbols in a PDB. - translateIdSymbols(NewData, IDTable); + translateIdSymbols(RecordBytes, getIDTable()); + Sym = CVSymbol(symbolKind(RecordBytes), RecordBytes); // 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); + recordStringTableReferences(Sym.kind(), RecordBytes, StringTableRefs); // 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); + if (symbolOpensScope(Sym.kind())) + scopeStackOpen(Scopes, CurSymOffset, Sym); + else if (symbolEndsScope(Sym.kind())) + scopeStackClose(Scopes, CurSymOffset, 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); + if (symbolGoesInGlobalsStream(Sym, Scopes.empty())) + addGlobalSymbol(Builder.getGsiBuilder(), + File->ModuleDBI->getModuleIndex(), CurSymOffset, Sym); + + if (symbolGoesInModuleStream(Sym, Scopes.empty())) { + // Add symbols to the module in bulk. If this symbol is contiguous + // with the previous run of symbols to add, combine the ranges. If + // not, close the previous range of symbols and start a new one. + if (Sym.data().data() == BulkSymbols.end()) { + BulkSymbols = makeArrayRef(BulkSymbols.data(), + BulkSymbols.size() + Sym.length()); + } else { + File->ModuleDBI->addSymbolsInBulk(BulkSymbols); + BulkSymbols = RecordBytes; + } + CurSymOffset += Sym.length(); + } return Error::success(); - }); - cantFail(std::move(EC)); + })); + + // Add any remaining symbols we've accumulated. + File->ModuleDBI->addSymbolsInBulk(BulkSymbols); } -// Allocate memory for a .debug$S section and relocate it. +// Allocate memory for a .debug$S / .debug$F section and relocate it. static ArrayRef<uint8_t> relocateDebugChunk(BumpPtrAllocator &Alloc, - SectionChunk *DebugChunk) { - uint8_t *Buffer = Alloc.Allocate<uint8_t>(DebugChunk->getSize()); - assert(DebugChunk->OutputSectionOff == 0 && + SectionChunk &DebugChunk) { + uint8_t *Buffer = Alloc.Allocate<uint8_t>(DebugChunk.getSize()); + assert(DebugChunk.OutputSectionOff == 0 && "debug sections should not be in output sections"); - DebugChunk->writeTo(Buffer); - return consumeDebugMagic(makeArrayRef(Buffer, DebugChunk->getSize()), - ".debug$S"); + DebugChunk.readRelocTargets(); + DebugChunk.writeTo(Buffer); + return makeArrayRef(Buffer, DebugChunk.getSize()); } static pdb::SectionContrib createSectionContrib(const Chunk *C, uint32_t Modi) { @@ -803,25 +1154,137 @@ static pdb::SectionContrib createSectionContrib(const Chunk *C, uint32_t Modi) { return SC; } -void PDBLinker::addObjFile(ObjFile *File) { +static uint32_t +translateStringTableIndex(uint32_t ObjIndex, + const DebugStringTableSubsectionRef &ObjStrTable, + DebugStringTableSubsection &PdbStrTable) { + auto ExpectedString = ObjStrTable.getString(ObjIndex); + if (!ExpectedString) { + warn("Invalid string table reference"); + consumeError(ExpectedString.takeError()); + return 0; + } + + return PdbStrTable.insert(*ExpectedString); +} + +void DebugSHandler::handleDebugS(lld::coff::SectionChunk &DebugS) { + DebugSubsectionArray Subsections; + + ArrayRef<uint8_t> RelocatedDebugContents = consumeDebugMagic( + relocateDebugChunk(Linker.Alloc, DebugS), DebugS.getSectionName()); + + BinaryStreamReader Reader(RelocatedDebugContents, support::little); + ExitOnErr(Reader.readArray(Subsections, RelocatedDebugContents.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: + // We can add the relocated line table directly to the PDB without + // modification because the file checksum offsets will stay the same. + File.ModuleDBI->addDebugSubsection(SS); + break; + case DebugSubsectionKind::FrameData: { + // We need to re-write string table indices here, so save off all + // frame data subsections until we've processed the entire list of + // subsections so that we can be sure we have the string table. + DebugFrameDataSubsectionRef FDS; + ExitOnErr(FDS.initialize(SS.getRecordData())); + NewFpoFrames.push_back(std::move(FDS)); + break; + } + case DebugSubsectionKind::Symbols: { + Linker.mergeSymbolRecords(&File, IndexMap, StringTableReferences, + SS.getRecordData()); + break; + } + default: + // FIXME: Process the rest of the subsections. + break; + } + } +} + +void DebugSHandler::finish() { + pdb::DbiStreamBuilder &DbiBuilder = Linker.Builder.getDbiBuilder(); + + // 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 string table indices in the Fpo Data and symbol records to refer to + // the global PDB string table instead of the object file string table. + for (DebugFrameDataSubsectionRef &FDS : NewFpoFrames) { + const ulittle32_t *Reloc = FDS.getRelocPtr(); + for (codeview::FrameData FD : FDS) { + FD.RvaStart += *Reloc; + FD.FrameFunc = + translateStringTableIndex(FD.FrameFunc, CVStrTab, Linker.PDBStrTab); + DbiBuilder.addNewFpoData(FD); + } + } + + for (ulittle32_t *Ref : StringTableReferences) + *Ref = translateStringTableIndex(*Ref, CVStrTab, Linker.PDBStrTab); + + // 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>(Linker.PDBStrTab); + for (FileChecksumEntry &FC : Checksums) { + SmallString<128> FileName = + ExitOnErr(CVStrTab.getString(FC.FileNameOffset)); + pdbMakeAbsolute(FileName); + ExitOnErr(Linker.Builder.getDbiBuilder().addModuleSourceFile( + *File.ModuleDBI, FileName)); + NewChecksums->addChecksum(FileName, FC.Kind, FC.Checksum); + } + File.ModuleDBI->addDebugSubsection(std::move(NewChecksums)); +} + +void PDBLinker::addObjFile(ObjFile *File, CVIndexMap *ExternIndexMap) { + if (File->wasProcessedForPDB()) + return; // 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 // path absolute. If it's an object in an archive, we make the archive path // absolute. bool InArchive = !File->ParentName.empty(); SmallString<128> Path = InArchive ? File->ParentName : File->getName(); - sys::fs::make_absolute(Path); - sys::path::native(Path, sys::path::Style::windows); + pdbMakeAbsolute(Path); StringRef Name = InArchive ? File->getName() : StringRef(Path); - File->ModuleDBI = &ExitOnErr(Builder.getDbiBuilder().addModuleInfo(Name)); + pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder(); + File->ModuleDBI = &ExitOnErr(DbiBuilder.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()) + if (!SecChunk || !SecChunk->Live) continue; pdb::SectionContrib SC = createSectionContrib(SecChunk, Modi); File->ModuleDBI->setFirstSectionContrib(SC); @@ -833,119 +1296,54 @@ void PDBLinker::addObjFile(ObjFile *File) { // the PDB first, so that we can get the map from object file type and item // indices to PDB type and item indices. CVIndexMap ObjectIndexMap; - auto IndexMapResult = mergeDebugT(File, ObjectIndexMap); + auto IndexMapResult = + mergeDebugT(File, ExternIndexMap ? ExternIndexMap : &ObjectIndexMap); // If the .debug$T sections fail to merge, assume there is no debug info. if (!IndexMapResult) { - warn("Type server PDB for " + Name + " is invalid, ignoring debug info. " + - toString(IndexMapResult.takeError())); + if (!Config->WarnDebugInfoUnusable) { + consumeError(IndexMapResult.takeError()); + return; + } + StringRef FileName = sys::path::filename(Path); + warn("Cannot use debug info for '" + FileName + "' [LNK4099]\n" + + ">>> failed to load reference " + + StringRef(toString(IndexMapResult.takeError()))); return; } - const CVIndexMap &IndexMap = *IndexMapResult; - ScopedTimer T(SymbolMergingTimer); - // Now do all live .debug$S sections. - DebugStringTableSubsectionRef CVStrTab; - DebugChecksumsSubsectionRef Checksums; - std::vector<ulittle32_t *> StringTableReferences; + DebugSHandler DSH(*this, *File, *IndexMapResult); + // Now do all live .debug$S and .debug$F sections. for (SectionChunk *DebugChunk : File->getDebugChunks()) { - if (!DebugChunk->isLive() || DebugChunk->getSectionName() != ".debug$S") + if (!DebugChunk->Live || DebugChunk->getSize() == 0) continue; - ArrayRef<uint8_t> RelocatedDebugContents = - relocateDebugChunk(Alloc, DebugChunk); - if (RelocatedDebugContents.empty()) + if (DebugChunk->getSectionName() == ".debug$S") { + DSH.handleDebugS(*DebugChunk); continue; - - DebugSubsectionArray Subsections; - BinaryStreamReader Reader(RelocatedDebugContents, support::little); - ExitOnErr(Reader.readArray(Subsections, RelocatedDebugContents.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: - // We can add the relocated line table directly to the PDB without - // modification because the file checksum offsets will stay the same. - File->ModuleDBI->addDebugSubsection(SS); - break; - case DebugSubsectionKind::Symbols: - if (Config->DebugGHashes) { - mergeSymbolRecords(Alloc, File, Builder.getGsiBuilder(), IndexMap, - GlobalIDTable, StringTableReferences, - SS.getRecordData()); - } else { - mergeSymbolRecords(Alloc, File, Builder.getGsiBuilder(), IndexMap, - IDTable, StringTableReferences, - SS.getRecordData()); - } - break; - default: - // FIXME: Process the rest of the subsections. - break; - } } - } - // 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 (DebugChunk->getSectionName() == ".debug$F") { + ArrayRef<uint8_t> RelocatedDebugContents = + relocateDebugChunk(Alloc, *DebugChunk); - if (!StringTableReferences.empty()) - warn("No StringTable subsection was encountered, but there are string " - "table references"); - return; - } + FixedStreamArray<object::FpoData> FpoRecords; + BinaryStreamReader Reader(RelocatedDebugContents, support::little); + uint32_t Count = RelocatedDebugContents.size() / sizeof(object::FpoData); + ExitOnErr(Reader.readArray(FpoRecords, Count)); - // 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()); + // These are already relocated and don't refer to the string table, so we + // can just copy it. + for (const object::FpoData &FD : FpoRecords) + DbiBuilder.addOldFpoData(FD); 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)); + // Do any post-processing now that all .debug$S sections have been processed. + DSH.finish(); } static PublicSym32 createPublic(Defined *Def) { @@ -977,13 +1375,8 @@ void PDBLinker::addObjectsToPDB() { // Construct TPI and IPI stream contents. ScopedTimer T2(TpiStreamLayoutTimer); - if (Config->DebugGHashes) { - addTypeInfo(Builder.getTpiBuilder(), GlobalTypeTable); - addTypeInfo(Builder.getIpiBuilder(), GlobalIDTable); - } else { - addTypeInfo(Builder.getTpiBuilder(), TypeTable); - addTypeInfo(Builder.getIpiBuilder(), IDTable); - } + addTypeInfo(Builder.getTpiBuilder(), getTypeTable()); + addTypeInfo(Builder.getIpiBuilder(), getIDTable()); T2.stop(); ScopedTimer T3(GlobalsLayoutTimer); @@ -999,10 +1392,10 @@ void PDBLinker::addObjectsToPDB() { if (!Publics.empty()) { // Sort the public symbols and add them to the stream. - std::sort(Publics.begin(), Publics.end(), - [](const PublicSym32 &L, const PublicSym32 &R) { - return L.Name < R.Name; - }); + sort(parallel::par, Publics.begin(), Publics.end(), + [](const PublicSym32 &L, const PublicSym32 &R) { + return L.Name < R.Name; + }); for (const PublicSym32 &Pub : Publics) GsiBuilder.addPublicSymbol(Pub); } @@ -1037,6 +1430,32 @@ static codeview::CPUType toCodeViewMachine(COFF::MachineTypes Machine) { } } +// Mimic MSVC which surrounds arguments containing whitespace with quotes. +// Double double-quotes are handled, so that the resulting string can be +// executed again on the cmd-line. +static std::string quote(ArrayRef<StringRef> Args) { + std::string R; + R.reserve(256); + for (StringRef A : Args) { + if (!R.empty()) + R.push_back(' '); + bool HasWS = A.find(' ') != StringRef::npos; + bool HasQ = A.find('"') != StringRef::npos; + if (HasWS || HasQ) + R.push_back('"'); + if (HasQ) { + SmallVector<StringRef, 4> S; + A.split(S, '"'); + R.append(join(S, "\"\"")); + } else { + R.append(A); + } + if (HasWS || HasQ) + R.push_back('"'); + } + return R; +} + static void addCommonLinkerModuleSymbols(StringRef Path, pdb::DbiModuleDescriptorBuilder &Mod, BumpPtrAllocator &Allocator) { @@ -1072,14 +1491,17 @@ static void addCommonLinkerModuleSymbols(StringRef Path, CS.setLanguage(SourceLanguage::Link); ArrayRef<StringRef> Args = makeArrayRef(Config->Argv).drop_front(); - std::string ArgStr = llvm::join(Args, " "); + std::string ArgStr = quote(Args); EBS.Fields.push_back("cwd"); SmallString<64> cwd; - sys::fs::current_path(cwd); + if (Config->PDBSourcePath.empty()) + sys::fs::current_path(cwd); + else + cwd = Config->PDBSourcePath; EBS.Fields.push_back(cwd); EBS.Fields.push_back("exe"); SmallString<64> exe = Config->Argv[0]; - llvm::sys::fs::make_absolute(exe); + pdbMakeAbsolute(exe); EBS.Fields.push_back(exe); EBS.Fields.push_back("pdb"); EBS.Fields.push_back(Path); @@ -1111,7 +1533,7 @@ static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &Mod, void coff::createPDB(SymbolTable *Symtab, ArrayRef<OutputSection *> OutputSections, ArrayRef<uint8_t> SectionTable, - const llvm::codeview::DebugInfo &BuildId) { + llvm::codeview::DebugInfo *BuildId) { ScopedTimer T1(TotalPdbLinkTimer); PDBLinker PDB(Symtab); @@ -1121,12 +1543,19 @@ void coff::createPDB(SymbolTable *Symtab, PDB.addNatvisFiles(); ScopedTimer T2(DiskCommitTimer); - PDB.commit(); + codeview::GUID Guid; + PDB.commit(&Guid); + memcpy(&BuildId->PDB70.Signature, &Guid, 16); } -void PDBLinker::initialize(const llvm::codeview::DebugInfo &BuildId) { +void PDBLinker::initialize(llvm::codeview::DebugInfo *BuildId) { ExitOnErr(Builder.initialize(4096)); // 4096 is blocksize + BuildId->Signature.CVSignature = OMF::Signature::PDB70; + // Signature is set to a hash of the PDB contents when the PDB is done. + memset(BuildId->PDB70.Signature, 0, 16); + BuildId->PDB70.Age = 1; + // Create streams in MSF for predefined streams, namely // PDB, TPI, DBI and IPI. for (int I = 0; I < (int)pdb::kSpecialStreamCount; ++I) @@ -1134,15 +1563,12 @@ void PDBLinker::initialize(const llvm::codeview::DebugInfo &BuildId) { // Add an Info stream. auto &InfoBuilder = Builder.getInfoBuilder(); - GUID uuid; - memcpy(&uuid, &BuildId.PDB70.Signature, sizeof(uuid)); - InfoBuilder.setAge(BuildId.PDB70.Age); - InfoBuilder.setGuid(uuid); InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70); + InfoBuilder.setHashPDBContentsToGUID(true); // Add an empty DBI stream. pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder(); - DbiBuilder.setAge(BuildId.PDB70.Age); + DbiBuilder.setAge(BuildId->PDB70.Age); DbiBuilder.setVersionHeader(pdb::PdbDbiV70); DbiBuilder.setMachineType(Config->Machine); // Technically we are not link.exe 14.11, but there are known cases where @@ -1157,8 +1583,7 @@ void PDBLinker::addSections(ArrayRef<OutputSection *> OutputSections, // It's not entirely clear what this is, but the * Linker * module uses it. pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder(); NativePath = Config->PDBPath; - sys::fs::make_absolute(NativePath); - sys::path::native(NativePath, sys::path::Style::windows); + pdbMakeAbsolute(NativePath); uint32_t PdbFilePathNI = DbiBuilder.addECName(NativePath); auto &LinkerModule = ExitOnErr(DbiBuilder.addModuleInfo("* Linker *")); LinkerModule.setPdbFilePathNI(PdbFilePathNI); @@ -1167,7 +1592,7 @@ 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()) { + for (Chunk *C : OS->Chunks) { pdb::SectionContrib SC = createSectionContrib(C, LinkerModule.getModuleIndex()); Builder.getDbiBuilder().addSectionContrib(SC); @@ -1186,9 +1611,9 @@ void PDBLinker::addSections(ArrayRef<OutputSection *> OutputSections, DbiBuilder.addDbgStream(pdb::DbgHeaderType::SectionHdr, SectionTable)); } -void PDBLinker::commit() { +void PDBLinker::commit(codeview::GUID *Guid) { // Write to a file. - ExitOnErr(Builder.commit(Config->PDBPath)); + ExitOnErr(Builder.commit(Config->PDBPath, Guid)); } static Expected<StringRef> @@ -1315,20 +1740,26 @@ std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *C, if (!findLineTable(C, Addr, CVStrTab, Checksums, Lines, OffsetInLinetable)) return {"", 0}; - uint32_t NameIndex; - uint32_t LineNumber; + Optional<uint32_t> NameIndex; + Optional<uint32_t> LineNumber; for (LineColumnEntry &Entry : Lines) { for (const LineNumberEntry &LN : Entry.LineNumbers) { + LineInfo LI(LN.Flags); if (LN.Offset > OffsetInLinetable) { + if (!NameIndex) { + NameIndex = Entry.NameIndex; + LineNumber = LI.getStartLine(); + } StringRef Filename = - ExitOnErr(getFileName(CVStrTab, Checksums, NameIndex)); - return {Filename, LineNumber}; + 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}; + if (!NameIndex) + return {"", 0}; + StringRef Filename = ExitOnErr(getFileName(CVStrTab, Checksums, *NameIndex)); + return {Filename, *LineNumber}; } diff --git a/COFF/PDB.h b/COFF/PDB.h index a98d129a633b..ea7a9996f415 100644 --- a/COFF/PDB.h +++ b/COFF/PDB.h @@ -28,7 +28,7 @@ class SymbolTable; void createPDB(SymbolTable *Symtab, llvm::ArrayRef<OutputSection *> OutputSections, llvm::ArrayRef<uint8_t> SectionTable, - const llvm::codeview::DebugInfo &BuildId); + llvm::codeview::DebugInfo *BuildId); std::pair<llvm::StringRef, uint32_t> getFileLine(const SectionChunk *C, uint32_t Addr); diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp index b286d865caaf..1a9e0455dc1d 100644 --- a/COFF/SymbolTable.cpp +++ b/COFF/SymbolTable.cpp @@ -60,16 +60,16 @@ void SymbolTable::addFile(InputFile *File) { } static void errorOrWarn(const Twine &S) { - if (Config->Force) + if (Config->ForceUnresolved) warn(S); else 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) { +// Returns the symbol in SC whose value is <= Addr that is closest to Addr. +// This is generally the global variable or function whose definition contains +// Addr. +static Symbol *getSymbol(SectionChunk *SC, uint32_t Addr) { DefinedRegular *Candidate = nullptr; for (Symbol *S : SC->File->getSymbols()) { @@ -81,14 +81,12 @@ static StringRef getSymbolName(SectionChunk *SC, uint32_t Addr) { Candidate = D; } - if (!Candidate) - return ""; - return Candidate->getName(); + return Candidate; } -static std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex) { +std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex) { struct Location { - StringRef SymName; + Symbol *Sym; std::pair<StringRef, uint32_t> FileLine; }; std::vector<Location> Locations; @@ -102,14 +100,14 @@ static std::string getSymbolLocations(ObjFile *File, uint32_t 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}); + Symbol *Sym = getSymbol(SC, R.VirtualAddress); + if (!FileLine.first.empty() || Sym) + Locations.push_back({Sym, FileLine}); } } if (Locations.empty()) - return "\n>>> referenced by " + toString(File) + "\n"; + return "\n>>> referenced by " + toString(File); std::string Out; llvm::raw_string_ostream OS(Out); @@ -119,13 +117,87 @@ static std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex) { OS << Loc.FileLine.first << ":" << Loc.FileLine.second << "\n>>> "; OS << toString(File); - if (!Loc.SymName.empty()) - OS << ":(" << Loc.SymName << ')'; + if (Loc.Sym) + OS << ":(" << toString(*Loc.Sym) << ')'; } - OS << '\n'; return OS.str(); } +void SymbolTable::loadMinGWAutomaticImports() { + for (auto &I : SymMap) { + Symbol *Sym = I.second; + auto *Undef = dyn_cast<Undefined>(Sym); + if (!Undef) + continue; + if (!Sym->IsUsedInRegularObj) + continue; + + StringRef Name = Undef->getName(); + + if (Name.startswith("__imp_")) + continue; + // If we have an undefined symbol, but we have a Lazy representing a + // symbol we could load from file, make sure to load that. + Lazy *L = dyn_cast_or_null<Lazy>(find(("__imp_" + Name).str())); + if (!L || L->PendingArchiveLoad) + continue; + + log("Loading lazy " + L->getName() + " from " + L->File->getName() + + " for automatic import"); + L->PendingArchiveLoad = true; + L->File->addMember(&L->Sym); + } +} + +bool SymbolTable::handleMinGWAutomaticImport(Symbol *Sym, StringRef Name) { + if (Name.startswith("__imp_")) + return false; + Defined *Imp = dyn_cast_or_null<Defined>(find(("__imp_" + Name).str())); + if (!Imp) + return false; + + // Replace the reference directly to a variable with a reference + // to the import address table instead. This obviously isn't right, + // but we mark the symbol as IsRuntimePseudoReloc, and a later pass + // will add runtime pseudo relocations for every relocation against + // this Symbol. The runtime pseudo relocation framework expects the + // reference itself to point at the IAT entry. + size_t ImpSize = 0; + if (isa<DefinedImportData>(Imp)) { + log("Automatically importing " + Name + " from " + + cast<DefinedImportData>(Imp)->getDLLName()); + ImpSize = sizeof(DefinedImportData); + } else if (isa<DefinedRegular>(Imp)) { + log("Automatically importing " + Name + " from " + + toString(cast<DefinedRegular>(Imp)->File)); + ImpSize = sizeof(DefinedRegular); + } else { + warn("unable to automatically import " + Name + " from " + Imp->getName() + + " from " + toString(cast<DefinedRegular>(Imp)->File) + + "; unexpected symbol type"); + return false; + } + Sym->replaceKeepingName(Imp, ImpSize); + Sym->IsRuntimePseudoReloc = true; + + // There may exist symbols named .refptr.<name> which only consist + // of a single pointer to <name>. If it turns out <name> is + // automatically imported, we don't need to keep the .refptr.<name> + // pointer at all, but redirect all accesses to it to the IAT entry + // for __imp_<name> instead, and drop the whole .refptr.<name> chunk. + DefinedRegular *Refptr = + dyn_cast_or_null<DefinedRegular>(find((".refptr." + Name).str())); + if (Refptr && Refptr->getChunk()->getSize() == Config->Wordsize) { + SectionChunk *SC = dyn_cast_or_null<SectionChunk>(Refptr->getChunk()); + if (SC && SC->Relocs.size() == 1 && *SC->symbols().begin() == Sym) { + log("Replacing .refptr." + Name + " with " + Imp->getName()); + Refptr->getChunk()->Live = false; + Refptr->replaceKeepingName(Imp, ImpSize); + } + } + return true; +} + void SymbolTable::reportRemainingUndefines() { SmallPtrSet<Symbol *, 8> Undefs; DenseMap<Symbol *, Symbol *> LocalImports; @@ -169,9 +241,17 @@ void SymbolTable::reportRemainingUndefines() { } } + // We don't want to report missing Microsoft precompiled headers symbols. + // A proper message will be emitted instead in PDBLinker::aquirePrecompObj + if (Name.contains("_PchSym_")) + continue; + + if (Config->MinGW && handleMinGWAutomaticImport(Sym, Name)) + continue; + // Remaining undefined symbols are not fatal if /force is specified. // They are replaced with dummy defined symbols. - if (Config->Force) + if (Config->ForceUnresolved) replaceSymbol<DefinedAbsolute>(Sym, Name, 0); Undefs.insert(Sym); } @@ -181,10 +261,10 @@ void SymbolTable::reportRemainingUndefines() { for (Symbol *B : Config->GCRoot) { if (Undefs.count(B)) - errorOrWarn("<root>: undefined symbol: " + B->getName()); + errorOrWarn("<root>: undefined symbol: " + toString(*B)); if (Config->WarnLocallyDefinedImported) if (Symbol *Imp = LocalImports.lookup(B)) - warn("<root>: locally defined symbol imported: " + Imp->getName() + + warn("<root>: locally defined symbol imported: " + toString(*Imp) + " (defined in " + toString(Imp->getFile()) + ") [LNK4217]"); } @@ -195,34 +275,41 @@ void SymbolTable::reportRemainingUndefines() { if (!Sym) continue; if (Undefs.count(Sym)) - errorOrWarn("undefined symbol: " + Sym->getName() + + errorOrWarn("undefined symbol: " + toString(*Sym) + 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]"); + warn(toString(File) + + ": locally defined symbol imported: " + toString(*Imp) + + " (defined in " + toString(Imp->getFile()) + ") [LNK4217]"); } } } std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) { + bool Inserted = false; Symbol *&Sym = SymMap[CachedHashStringRef(Name)]; - if (Sym) - return {Sym, false}; - Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>()); - Sym->IsUsedInRegularObj = false; - Sym->PendingArchiveLoad = false; - return {Sym, true}; + if (!Sym) { + Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>()); + Sym->IsUsedInRegularObj = false; + Sym->PendingArchiveLoad = false; + Inserted = true; + } + return {Sym, Inserted}; +} + +std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name, InputFile *File) { + std::pair<Symbol *, bool> Result = insert(Name); + if (!File || !isa<BitcodeFile>(File)) + Result.first->IsUsedInRegularObj = true; + return Result; } Symbol *SymbolTable::addUndefined(StringRef Name, InputFile *F, bool IsWeakAlias) { Symbol *S; bool WasInserted; - std::tie(S, WasInserted) = insert(Name); - if (!F || !isa<BitcodeFile>(F)) - S->IsUsedInRegularObj = true; + std::tie(S, WasInserted) = insert(Name, F); if (WasInserted || (isa<Lazy>(S) && IsWeakAlias)) { replaceSymbol<Undefined>(S, Name); return S; @@ -253,14 +340,20 @@ void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) { } void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) { - error("duplicate symbol: " + toString(*Existing) + " in " + - toString(Existing->getFile()) + " and in " + toString(NewFile)); + std::string Msg = "duplicate symbol: " + toString(*Existing) + " in " + + toString(Existing->getFile()) + " and in " + + toString(NewFile); + + if (Config->ForceMultiple) + warn(Msg); + else + error(Msg); } Symbol *SymbolTable::addAbsolute(StringRef N, COFFSymbolRef Sym) { Symbol *S; bool WasInserted; - std::tie(S, WasInserted) = insert(N); + std::tie(S, WasInserted) = insert(N, nullptr); S->IsUsedInRegularObj = true; if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) replaceSymbol<DefinedAbsolute>(S, N, Sym); @@ -272,7 +365,7 @@ Symbol *SymbolTable::addAbsolute(StringRef N, COFFSymbolRef Sym) { Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) { Symbol *S; bool WasInserted; - std::tie(S, WasInserted) = insert(N); + std::tie(S, WasInserted) = insert(N, nullptr); S->IsUsedInRegularObj = true; if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) replaceSymbol<DefinedAbsolute>(S, N, VA); @@ -284,7 +377,7 @@ Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) { Symbol *SymbolTable::addSynthetic(StringRef N, Chunk *C) { Symbol *S; bool WasInserted; - std::tie(S, WasInserted) = insert(N); + std::tie(S, WasInserted) = insert(N, nullptr); S->IsUsedInRegularObj = true; if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) replaceSymbol<DefinedSynthetic>(S, N, C); @@ -298,9 +391,7 @@ Symbol *SymbolTable::addRegular(InputFile *F, StringRef N, SectionChunk *C) { Symbol *S; bool WasInserted; - std::tie(S, WasInserted) = insert(N); - if (!isa<BitcodeFile>(F)) - S->IsUsedInRegularObj = true; + std::tie(S, WasInserted) = insert(N, F); if (WasInserted || !isa<DefinedRegular>(S)) replaceSymbol<DefinedRegular>(S, F, N, /*IsCOMDAT*/ false, /*IsExternal*/ true, Sym, C); @@ -314,9 +405,7 @@ SymbolTable::addComdat(InputFile *F, StringRef N, const coff_symbol_generic *Sym) { Symbol *S; bool WasInserted; - std::tie(S, WasInserted) = insert(N); - if (!isa<BitcodeFile>(F)) - S->IsUsedInRegularObj = true; + std::tie(S, WasInserted) = insert(N, F); if (WasInserted || !isa<DefinedRegular>(S)) { replaceSymbol<DefinedRegular>(S, F, N, /*IsCOMDAT*/ true, /*IsExternal*/ true, Sym, nullptr); @@ -331,9 +420,7 @@ Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size, const coff_symbol_generic *Sym, CommonChunk *C) { Symbol *S; bool WasInserted; - std::tie(S, WasInserted) = insert(N); - if (!isa<BitcodeFile>(F)) - S->IsUsedInRegularObj = true; + std::tie(S, WasInserted) = insert(N, F); if (WasInserted || !isa<DefinedCOFF>(S)) replaceSymbol<DefinedCommon>(S, F, N, Size, Sym, C); else if (auto *DC = dyn_cast<DefinedCommon>(S)) @@ -345,7 +432,7 @@ Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size, Symbol *SymbolTable::addImportData(StringRef N, ImportFile *F) { Symbol *S; bool WasInserted; - std::tie(S, WasInserted) = insert(N); + std::tie(S, WasInserted) = insert(N, nullptr); S->IsUsedInRegularObj = true; if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) { replaceSymbol<DefinedImportData>(S, N, F); @@ -360,7 +447,7 @@ Symbol *SymbolTable::addImportThunk(StringRef Name, DefinedImportData *ID, uint16_t Machine) { Symbol *S; bool WasInserted; - std::tie(S, WasInserted) = insert(Name); + std::tie(S, WasInserted) = insert(Name, nullptr); S->IsUsedInRegularObj = true; if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) { replaceSymbol<DefinedImportThunk>(S, Name, ID, Machine); diff --git a/COFF/SymbolTable.h b/COFF/SymbolTable.h index 30cb1a5410c3..00e55dbb7a02 100644 --- a/COFF/SymbolTable.h +++ b/COFF/SymbolTable.h @@ -54,6 +54,9 @@ public: // symbols. void reportRemainingUndefines(); + void loadMinGWAutomaticImports(); + bool handleMinGWAutomaticImport(Symbol *Sym, StringRef Name); + // Returns a list of chunks of selected symbols. std::vector<Chunk *> getChunks(); @@ -108,7 +111,10 @@ public: } private: + /// Inserts symbol if not already present. std::pair<Symbol *, bool> insert(StringRef Name); + /// Same as insert(Name), but also sets IsUsedInRegularObj. + std::pair<Symbol *, bool> insert(StringRef Name, InputFile *F); StringRef findByPrefix(StringRef Prefix); llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> SymMap; @@ -117,6 +123,8 @@ private: extern SymbolTable *Symtab; +std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex); + } // namespace coff } // namespace lld diff --git a/COFF/Symbols.cpp b/COFF/Symbols.cpp index 7c8b7d5e8fc5..ccaf86417f10 100644 --- a/COFF/Symbols.cpp +++ b/COFF/Symbols.cpp @@ -54,7 +54,7 @@ InputFile *Symbol::getFile() { bool Symbol::isLive() const { if (auto *R = dyn_cast<DefinedRegular>(this)) - return R->getChunk()->isLive(); + return R->getChunk()->Live; if (auto *Imp = dyn_cast<DefinedImportData>(this)) return Imp->File->Live; if (auto *Imp = dyn_cast<DefinedImportThunk>(this)) @@ -63,6 +63,13 @@ bool Symbol::isLive() const { return true; } +// MinGW specific. +void Symbol::replaceKeepingName(Symbol *Other, size_t Size) { + StringRef OrigName = Name; + memcpy(this, Other, Size); + Name = OrigName; +} + COFFSymbolRef DefinedCOFF::getCOFFSymbol() { size_t SymSize = cast<ObjFile>(File)->getCOFFObj()->getSymbolTableEntrySize(); if (SymSize == sizeof(coff_symbol16)) diff --git a/COFF/Symbols.h b/COFF/Symbols.h index 783965adbd9a..4a8693e22e3c 100644 --- a/COFF/Symbols.h +++ b/COFF/Symbols.h @@ -39,9 +39,9 @@ class Symbol { public: enum Kind { // The order of these is significant. We start with the regular defined - // symbols as those are the most prevelant and the zero tag is the cheapest + // symbols as those are the most prevalent and the zero tag is the cheapest // to set. Among the defined kinds, the lower the kind is preferred over - // the higher kind when testing wether one symbol should take precedence + // the higher kind when testing whether one symbol should take precedence // over another. DefinedRegularKind = 0, DefinedCommonKind, @@ -66,6 +66,8 @@ public: // Returns the symbol name. StringRef getName(); + void replaceKeepingName(Symbol *Other, size_t Size); + // Returns the file from which this symbol was created. InputFile *getFile(); @@ -78,7 +80,7 @@ protected: explicit Symbol(Kind K, StringRef N = "") : SymbolKind(K), IsExternal(true), IsCOMDAT(false), WrittenToSymtab(false), PendingArchiveLoad(false), IsGCRoot(false), - Name(N) {} + IsRuntimePseudoReloc(false), Name(N) {} const unsigned SymbolKind : 8; unsigned IsExternal : 1; @@ -102,6 +104,8 @@ public: /// True if we've already added this symbol to the list of GC roots. unsigned IsGCRoot : 1; + unsigned IsRuntimePseudoReloc : 1; + protected: StringRef Name; }; @@ -331,8 +335,8 @@ private: Chunk *Data; }; -// If you have a symbol "__imp_foo" in your object file, a symbol name -// "foo" becomes automatically available as a pointer to "__imp_foo". +// If you have a symbol "foo" in your object file, a symbol name +// "__imp_foo" becomes automatically available as a pointer to "foo". // This class is for such automatically-created symbols. // Yes, this is an odd feature. We didn't intend to implement that. // This is here just for compatibility with MSVC. diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp index d17405ec26ab..258796ea6057 100644 --- a/COFF/Writer.cpp +++ b/COFF/Writer.cpp @@ -77,36 +77,38 @@ static const int SectorSize = 512; 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; +static const int NumberOfDataDirectory = 16; namespace { class DebugDirectoryChunk : public Chunk { public: - DebugDirectoryChunk(const std::vector<Chunk *> &R) : Records(R) {} + DebugDirectoryChunk(const std::vector<Chunk *> &R, bool WriteRepro) + : Records(R), WriteRepro(WriteRepro) {} size_t getSize() const override { - return Records.size() * sizeof(debug_directory); + return (Records.size() + int(WriteRepro)) * sizeof(debug_directory); } void writeTo(uint8_t *B) const override { auto *D = reinterpret_cast<debug_directory *>(B + OutputSectionOff); for (const Chunk *Record : Records) { - D->Characteristics = 0; - D->TimeDateStamp = 0; - D->MajorVersion = 0; - D->MinorVersion = 0; - D->Type = COFF::IMAGE_DEBUG_TYPE_CODEVIEW; - D->SizeOfData = Record->getSize(); - D->AddressOfRawData = Record->getRVA(); OutputSection *OS = Record->getOutputSection(); uint64_t Offs = OS->getFileOff() + (Record->getRVA() - OS->getRVA()); - D->PointerToRawData = Offs; - - TimeDateStamps.push_back(&D->TimeDateStamp); + fillEntry(D, COFF::IMAGE_DEBUG_TYPE_CODEVIEW, Record->getSize(), + Record->getRVA(), Offs); ++D; } + + if (WriteRepro) { + // FIXME: The COFF spec allows either a 0-sized entry to just say + // "the timestamp field is really a hash", or a 4-byte size field + // followed by that many bytes containing a longer hash (with the + // lowest 4 bytes usually being the timestamp in little-endian order). + // Consider storing the full 8 bytes computed by xxHash64 here. + fillEntry(D, COFF::IMAGE_DEBUG_TYPE_REPRO, 0, 0, 0); + } } void setTimeDateStamp(uint32_t TimeDateStamp) { @@ -115,8 +117,23 @@ public: } private: + void fillEntry(debug_directory *D, COFF::DebugType DebugType, size_t Size, + uint64_t RVA, uint64_t Offs) const { + D->Characteristics = 0; + D->TimeDateStamp = 0; + D->MajorVersion = 0; + D->MinorVersion = 0; + D->Type = DebugType; + D->SizeOfData = Size; + D->AddressOfRawData = RVA; + D->PointerToRawData = Offs; + + TimeDateStamps.push_back(&D->TimeDateStamp); + } + mutable std::vector<support::ulittle32_t *> TimeDateStamps; const std::vector<Chunk *> &Records; + bool WriteRepro; }; class CVDebugRecordChunk : public Chunk { @@ -150,14 +167,22 @@ private: void createSections(); void createMiscChunks(); void createImportTables(); + void appendImportThunks(); + void locateImportTables( + std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> &Map); void createExportTable(); void mergeSections(); + void readRelocTargets(); + void removeUnusedSections(); void assignAddresses(); + void finalizeAddresses(); void removeEmptySections(); void createSymbolAndStringTable(); void openFile(StringRef OutputPath); template <typename PEHeaderTy> void writeHeader(); void createSEHTable(); + void createRuntimePseudoRelocs(); + void insertCtorDtorSymbols(); void createGuardCFTables(); void markSymbolsForRVATable(ObjFile *File, ArrayRef<SectionChunk *> SymIdxChunks, @@ -168,6 +193,7 @@ private: void writeSections(); void writeBuildId(); void sortExceptionTable(); + void sortCRTSectionChunks(std::vector<Chunk *> &Chunks); llvm::Optional<coff_symbol16> createSymbol(Defined *D); size_t addEntryToStringTable(StringRef Str); @@ -184,6 +210,10 @@ private: std::vector<char> Strtab; std::vector<llvm::object::coff_symbol16> OutputSymtab; IdataContents Idata; + Chunk *ImportTableStart = nullptr; + uint64_t ImportTableSize = 0; + Chunk *IATStart = nullptr; + uint64_t IATSize = 0; DelayLoadContents DelayIdata; EdataContents Edata; bool SetNoSEHCharacteristic = false; @@ -191,7 +221,6 @@ private: DebugDirectoryChunk *DebugDirectory = nullptr; std::vector<Chunk *> DebugRecords; CVDebugRecordChunk *BuildId = nullptr; - Optional<codeview::DebugInfo> PreviousBuildId; ArrayRef<uint8_t> SectionTable; uint64_t FileSize; @@ -209,6 +238,8 @@ private: OutputSection *DidatSec; OutputSection *RsrcSec; OutputSection *RelocSec; + OutputSection *CtorsSec; + OutputSection *DtorsSec; // The first and last .pdata sections in the output file. // @@ -237,6 +268,11 @@ void OutputSection::addChunk(Chunk *C) { C->setOutputSection(this); } +void OutputSection::insertChunkAtStart(Chunk *C) { + Chunks.insert(Chunks.begin(), C); + C->setOutputSection(this); +} + void OutputSection::setPermissions(uint32_t C) { Header.Characteristics &= ~PermMask; Header.Characteristics |= C; @@ -267,77 +303,206 @@ void OutputSection::writeHeaderTo(uint8_t *Buf) { } // namespace coff } // namespace lld -// PDBs are matched against executables using a build id which consists of three -// components: -// 1. A 16-bit GUID -// 2. An age -// 3. A time stamp. +// Check whether the target address S is in range from a relocation +// of type RelType at address P. +static bool isInRange(uint16_t RelType, uint64_t S, uint64_t P, int Margin) { + assert(Config->Machine == ARMNT); + int64_t Diff = AbsoluteDifference(S, P + 4) + Margin; + switch (RelType) { + case IMAGE_REL_ARM_BRANCH20T: + return isInt<21>(Diff); + case IMAGE_REL_ARM_BRANCH24T: + case IMAGE_REL_ARM_BLX23T: + return isInt<25>(Diff); + default: + return true; + } +} + +// Return the last thunk for the given target if it is in range, +// or create a new one. +static std::pair<Defined *, bool> +getThunk(DenseMap<uint64_t, Defined *> &LastThunks, Defined *Target, uint64_t P, + uint16_t Type, int Margin) { + Defined *&LastThunk = LastThunks[Target->getRVA()]; + if (LastThunk && isInRange(Type, LastThunk->getRVA(), P, Margin)) + return {LastThunk, false}; + RangeExtensionThunk *C = make<RangeExtensionThunk>(Target); + Defined *D = make<DefinedSynthetic>("", C); + LastThunk = D; + return {D, true}; +} + +// This checks all relocations, and for any relocation which isn't in range +// it adds a thunk after the section chunk that contains the relocation. +// If the latest thunk for the specific target is in range, that is used +// instead of creating a new thunk. All range checks are done with the +// specified margin, to make sure that relocations that originally are in +// range, but only barely, also get thunks - in case other added thunks makes +// the target go out of range. // -// Debuggers and symbol servers match executables against debug info by checking -// each of these components of the EXE/DLL against the corresponding value in -// the PDB and failing a match if any of the components differ. In the case of -// symbol servers, symbols are cached in a folder that is a function of the -// GUID. As a result, in order to avoid symbol cache pollution where every -// incremental build copies a new PDB to the symbol cache, we must try to re-use -// the existing GUID if one exists, but bump the age. This way the match will -// fail, so the symbol cache knows to use the new PDB, but the GUID matches, so -// it overwrites the existing item in the symbol cache rather than making a new -// one. -static Optional<codeview::DebugInfo> loadExistingBuildId(StringRef Path) { - // We don't need to incrementally update a previous build id if we're not - // writing codeview debug info. - if (!Config->Debug) - return None; +// After adding thunks, we verify that all relocations are in range (with +// no extra margin requirements). If this failed, we restart (throwing away +// the previously created thunks) and retry with a wider margin. +static bool createThunks(std::vector<Chunk *> &Chunks, int Margin) { + bool AddressesChanged = false; + DenseMap<uint64_t, Defined *> LastThunks; + size_t ThunksSize = 0; + // Recheck Chunks.size() each iteration, since we can insert more + // elements into it. + for (size_t I = 0; I != Chunks.size(); ++I) { + SectionChunk *SC = dyn_cast_or_null<SectionChunk>(Chunks[I]); + if (!SC) + continue; + size_t ThunkInsertionSpot = I + 1; + + // Try to get a good enough estimate of where new thunks will be placed. + // Offset this by the size of the new thunks added so far, to make the + // estimate slightly better. + size_t ThunkInsertionRVA = SC->getRVA() + SC->getSize() + ThunksSize; + for (size_t J = 0, E = SC->Relocs.size(); J < E; ++J) { + const coff_relocation &Rel = SC->Relocs[J]; + Symbol *&RelocTarget = SC->RelocTargets[J]; + + // The estimate of the source address P should be pretty accurate, + // but we don't know whether the target Symbol address should be + // offset by ThunkSize or not (or by some of ThunksSize but not all of + // it), giving us some uncertainty once we have added one thunk. + uint64_t P = SC->getRVA() + Rel.VirtualAddress + ThunksSize; + + Defined *Sym = dyn_cast_or_null<Defined>(RelocTarget); + if (!Sym) + continue; - auto ExpectedBinary = llvm::object::createBinary(Path); - if (!ExpectedBinary) { - consumeError(ExpectedBinary.takeError()); - return None; + uint64_t S = Sym->getRVA(); + + if (isInRange(Rel.Type, S, P, Margin)) + continue; + + // If the target isn't in range, hook it up to an existing or new + // thunk. + Defined *Thunk; + bool WasNew; + std::tie(Thunk, WasNew) = getThunk(LastThunks, Sym, P, Rel.Type, Margin); + if (WasNew) { + Chunk *ThunkChunk = Thunk->getChunk(); + ThunkChunk->setRVA( + ThunkInsertionRVA); // Estimate of where it will be located. + Chunks.insert(Chunks.begin() + ThunkInsertionSpot, ThunkChunk); + ThunkInsertionSpot++; + ThunksSize += ThunkChunk->getSize(); + ThunkInsertionRVA += ThunkChunk->getSize(); + AddressesChanged = true; + } + RelocTarget = Thunk; + } } + return AddressesChanged; +} - auto Binary = std::move(*ExpectedBinary); - if (!Binary.getBinary()->isCOFF()) - return None; +// Verify that all relocations are in range, with no extra margin requirements. +static bool verifyRanges(const std::vector<Chunk *> Chunks) { + for (Chunk *C : Chunks) { + SectionChunk *SC = dyn_cast_or_null<SectionChunk>(C); + if (!SC) + continue; - std::error_code EC; - COFFObjectFile File(Binary.getBinary()->getMemoryBufferRef(), EC); - if (EC) - return None; + for (size_t J = 0, E = SC->Relocs.size(); J < E; ++J) { + const coff_relocation &Rel = SC->Relocs[J]; + Symbol *RelocTarget = SC->RelocTargets[J]; - // If the machine of the binary we're outputting doesn't match the machine - // of the existing binary, don't try to re-use the build id. - if (File.is64() != Config->is64() || File.getMachine() != Config->Machine) - return None; + Defined *Sym = dyn_cast_or_null<Defined>(RelocTarget); + if (!Sym) + continue; - for (const auto &DebugDir : File.debug_directories()) { - if (DebugDir.Type != IMAGE_DEBUG_TYPE_CODEVIEW) - continue; + uint64_t P = SC->getRVA() + Rel.VirtualAddress; + uint64_t S = Sym->getRVA(); - const codeview::DebugInfo *ExistingDI = nullptr; - StringRef PDBFileName; - if (auto EC = File.getDebugPDBInfo(ExistingDI, PDBFileName)) { - (void)EC; - return None; + if (!isInRange(Rel.Type, S, P, 0)) + return false; } - // We only support writing PDBs in v70 format. So if this is not a build - // id that we recognize / support, ignore it. - if (ExistingDI->Signature.CVSignature != OMF::Signature::PDB70) - return None; - return *ExistingDI; } - return None; + return true; +} + +// Assign addresses and add thunks if necessary. +void Writer::finalizeAddresses() { + assignAddresses(); + if (Config->Machine != ARMNT) + return; + + size_t OrigNumChunks = 0; + for (OutputSection *Sec : OutputSections) { + Sec->OrigChunks = Sec->Chunks; + OrigNumChunks += Sec->Chunks.size(); + } + + int Pass = 0; + int Margin = 1024 * 100; + while (true) { + // First check whether we need thunks at all, or if the previous pass of + // adding them turned out ok. + bool RangesOk = true; + size_t NumChunks = 0; + for (OutputSection *Sec : OutputSections) { + if (!verifyRanges(Sec->Chunks)) { + RangesOk = false; + break; + } + NumChunks += Sec->Chunks.size(); + } + if (RangesOk) { + if (Pass > 0) + log("Added " + Twine(NumChunks - OrigNumChunks) + " thunks with " + + "margin " + Twine(Margin) + " in " + Twine(Pass) + " passes"); + return; + } + + if (Pass >= 10) + fatal("adding thunks hasn't converged after " + Twine(Pass) + " passes"); + + if (Pass > 0) { + // If the previous pass didn't work out, reset everything back to the + // original conditions before retrying with a wider margin. This should + // ideally never happen under real circumstances. + for (OutputSection *Sec : OutputSections) { + Sec->Chunks = Sec->OrigChunks; + for (Chunk *C : Sec->Chunks) + C->resetRelocTargets(); + } + Margin *= 2; + } + + // Try adding thunks everywhere where it is needed, with a margin + // to avoid things going out of range due to the added thunks. + bool AddressesChanged = false; + for (OutputSection *Sec : OutputSections) + AddressesChanged |= createThunks(Sec->Chunks, Margin); + // If the verification above thought we needed thunks, we should have + // added some. + assert(AddressesChanged); + + // Recalculate the layout for the whole image (and verify the ranges at + // the start of the next round). + assignAddresses(); + + Pass++; + } } // The main function of the writer. void Writer::run() { ScopedTimer T1(CodeLayoutTimer); + createImportTables(); createSections(); createMiscChunks(); - createImportTables(); + appendImportThunks(); createExportTable(); mergeSections(); - assignAddresses(); + readRelocTargets(); + removeUnusedSections(); + finalizeAddresses(); removeEmptySections(); setSectionPermissions(); createSymbolAndStringTable(); @@ -346,9 +511,6 @@ void Writer::run() { 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); openFile(Config->OutputFile); if (Config->is64()) { writeHeader<pe32plus_header>(); @@ -357,14 +519,14 @@ void Writer::run() { } writeSections(); sortExceptionTable(); - writeBuildId(); T1.stop(); if (!Config->PDBPath.empty() && Config->Debug) { assert(BuildId); - createPDB(Symtab, OutputSections, SectionTable, *BuildId->BuildId); + createPDB(Symtab, OutputSections, SectionTable, BuildId->BuildId); } + writeBuildId(); writeMapFile(OutputSections); @@ -396,6 +558,110 @@ static void sortBySectionOrder(std::vector<Chunk *> &Chunks) { }); } +// Sort concrete section chunks from GNU import libraries. +// +// GNU binutils doesn't use short import files, but instead produces import +// libraries that consist of object files, with section chunks for the .idata$* +// sections. These are linked just as regular static libraries. Each import +// library consists of one header object, one object file for every imported +// symbol, and one trailer object. In order for the .idata tables/lists to +// be formed correctly, the section chunks within each .idata$* section need +// to be grouped by library, and sorted alphabetically within each library +// (which makes sure the header comes first and the trailer last). +static bool fixGnuImportChunks( + std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> &Map) { + uint32_t RDATA = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; + + // Make sure all .idata$* section chunks are mapped as RDATA in order to + // be sorted into the same sections as our own synthesized .idata chunks. + for (auto &Pair : Map) { + StringRef SectionName = Pair.first.first; + uint32_t OutChars = Pair.first.second; + if (!SectionName.startswith(".idata")) + continue; + if (OutChars == RDATA) + continue; + std::vector<Chunk *> &SrcVect = Pair.second; + std::vector<Chunk *> &DestVect = Map[{SectionName, RDATA}]; + DestVect.insert(DestVect.end(), SrcVect.begin(), SrcVect.end()); + SrcVect.clear(); + } + + bool HasIdata = false; + // Sort all .idata$* chunks, grouping chunks from the same library, + // with alphabetical ordering of the object fils within a library. + for (auto &Pair : Map) { + StringRef SectionName = Pair.first.first; + if (!SectionName.startswith(".idata")) + continue; + + std::vector<Chunk *> &Chunks = Pair.second; + if (!Chunks.empty()) + HasIdata = true; + std::stable_sort(Chunks.begin(), Chunks.end(), [&](Chunk *S, Chunk *T) { + SectionChunk *SC1 = dyn_cast_or_null<SectionChunk>(S); + SectionChunk *SC2 = dyn_cast_or_null<SectionChunk>(T); + if (!SC1 || !SC2) { + // if SC1, order them ascending. If SC2 or both null, + // S is not less than T. + return SC1 != nullptr; + } + // Make a string with "libraryname/objectfile" for sorting, achieving + // both grouping by library and sorting of objects within a library, + // at once. + std::string Key1 = + (SC1->File->ParentName + "/" + SC1->File->getName()).str(); + std::string Key2 = + (SC2->File->ParentName + "/" + SC2->File->getName()).str(); + return Key1 < Key2; + }); + } + return HasIdata; +} + +// Add generated idata chunks, for imported symbols and DLLs, and a +// terminator in .idata$2. +static void addSyntheticIdata( + IdataContents &Idata, + std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> &Map) { + uint32_t RDATA = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; + Idata.create(); + + // Add the .idata content in the right section groups, to allow + // chunks from other linked in object files to be grouped together. + // See Microsoft PE/COFF spec 5.4 for details. + auto Add = [&](StringRef N, std::vector<Chunk *> &V) { + std::vector<Chunk *> &DestVect = Map[{N, RDATA}]; + DestVect.insert(DestVect.end(), V.begin(), V.end()); + }; + + // The loader assumes a specific order of data. + // Add each type in the correct order. + Add(".idata$2", Idata.Dirs); + Add(".idata$4", Idata.Lookups); + Add(".idata$5", Idata.Addresses); + Add(".idata$6", Idata.Hints); + Add(".idata$7", Idata.DLLNames); +} + +// Locate the first Chunk and size of the import directory list and the +// IAT. +void Writer::locateImportTables( + std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> &Map) { + uint32_t RDATA = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; + std::vector<Chunk *> &ImportTables = Map[{".idata$2", RDATA}]; + if (!ImportTables.empty()) + ImportTableStart = ImportTables.front(); + for (Chunk *C : ImportTables) + ImportTableSize += C->getSize(); + + std::vector<Chunk *> &IAT = Map[{".idata$5", RDATA}]; + if (!IAT.empty()) + IATStart = IAT.front(); + for (Chunk *C : IAT) + IATSize += C->getSize(); +} + // Create output section objects and add them to OutputSections. void Writer::createSections() { // First, create the builtin sections. @@ -429,12 +695,14 @@ void Writer::createSections() { DidatSec = CreateSection(".didat", DATA | R); RsrcSec = CreateSection(".rsrc", DATA | R); RelocSec = CreateSection(".reloc", DATA | DISCARDABLE | R); + CtorsSec = CreateSection(".ctors", DATA | R | W); + DtorsSec = CreateSection(".dtors", DATA | R | W); // 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()) { + if (SC && !SC->Live) { if (Config->Verbose) SC->printDiscardedMessage(); continue; @@ -442,26 +710,43 @@ void Writer::createSections() { Map[{C->getSectionName(), C->getOutputCharacteristics()}].push_back(C); } + // Even in non MinGW cases, we might need to link against GNU import + // libraries. + bool HasIdata = fixGnuImportChunks(Map); + if (!Idata.empty()) + HasIdata = true; + + if (HasIdata) + addSyntheticIdata(Idata, Map); + // Process an /order option. if (!Config->Order.empty()) for (auto &Pair : Map) sortBySectionOrder(Pair.second); + if (HasIdata) + locateImportTables(Map); + // 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. - for (auto Pair : Map) { + for (auto &Pair : Map) { 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") + if (Name == ".CRT") { + // 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. OutChars = DATA | R; + log("Processing section " + Pair.first.first + " -> " + Name); + + sortCRTSectionChunks(Pair.second); + } + OutputSection *Sec = CreateSection(Name, OutChars); std::vector<Chunk *> &Chunks = Pair.second; for (Chunk *C : Chunks) @@ -499,20 +784,20 @@ void Writer::createMiscChunks() { } // Create Debug Information Chunks - if (Config->Debug) { - DebugDirectory = make<DebugDirectoryChunk>(DebugRecords); - - OutputSection *DebugInfoSec = Config->MinGW ? BuildidSec : RdataSec; + OutputSection *DebugInfoSec = Config->MinGW ? BuildidSec : RdataSec; + if (Config->Debug || Config->Repro) { + DebugDirectory = make<DebugDirectoryChunk>(DebugRecords, Config->Repro); + DebugInfoSec->addChunk(DebugDirectory); + } + if (Config->Debug) { // 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 // if we're ultimately not going to write CodeView data to the PDB. - auto *CVChunk = make<CVDebugRecordChunk>(); - BuildId = CVChunk; - DebugRecords.push_back(CVChunk); + BuildId = make<CVDebugRecordChunk>(); + DebugRecords.push_back(BuildId); - DebugInfoSec->addChunk(DebugDirectory); for (Chunk *C : DebugRecords) DebugInfoSec->addChunk(C); } @@ -524,6 +809,12 @@ void Writer::createMiscChunks() { // Create /guard:cf tables if requested. if (Config->GuardCF != GuardCFLevel::Off) createGuardCFTables(); + + if (Config->MinGW) { + createRuntimePseudoRelocs(); + + insertCtorDtorSymbols(); + } } // Create .idata section for the DLL-imported symbol table. @@ -531,9 +822,6 @@ void Writer::createMiscChunks() { // IdataContents class abstracted away the details for us, // so we just let it create chunks and add them to the section. void Writer::createImportTables() { - if (ImportFile::Instances.empty()) - return; - // Initialize DLLOrder so that import entries are ordered in // the same order as in the command line. (That affects DLL // initialization order, and this ordering is MSVC-compatible.) @@ -545,14 +833,6 @@ void Writer::createImportTables() { if (Config->DLLOrder.count(DLL) == 0) Config->DLLOrder[DLL] = Config->DLLOrder.size(); - 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); @@ -565,10 +845,25 @@ void Writer::createImportTables() { Idata.add(ImpSym); } } +} - if (!Idata.empty()) - for (Chunk *C : Idata.getChunks()) - IdataSec->addChunk(C); +void Writer::appendImportThunks() { + if (ImportFile::Instances.empty()) + return; + + for (ImportFile *File : ImportFile::Instances) { + if (!File->Live) + continue; + + if (!File->ThunkSym) + continue; + + 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 (!DelayIdata.empty()) { Defined *Helper = cast<Defined>(Config->DelayLoadHelper); @@ -589,6 +884,21 @@ void Writer::createExportTable() { EdataSec->addChunk(C); } +void Writer::removeUnusedSections() { + // Remove sections that we can be sure won't get content, to avoid + // allocating space for their section headers. + auto IsUnused = [this](OutputSection *S) { + if (S == RelocSec) + return false; // This section is populated later. + // MergeChunks have zero size at this point, as their size is finalized + // later. Only remove sections that have no Chunks at all. + return S->Chunks.empty(); + }; + OutputSections.erase( + std::remove_if(OutputSections.begin(), OutputSections.end(), IsUnused), + OutputSections.end()); +} + // The Windows loader doesn't seem to like empty sections, // so we remove them if any. void Writer::removeEmptySections() { @@ -699,9 +1009,9 @@ void Writer::createSymbolAndStringTable() { } void Writer::mergeSections() { - if (!PdataSec->getChunks().empty()) { - FirstPdata = PdataSec->getChunks().front(); - LastPdata = PdataSec->getChunks().back(); + if (!PdataSec->Chunks.empty()) { + FirstPdata = PdataSec->Chunks.front(); + LastPdata = PdataSec->Chunks.back(); } for (auto &P : Config->Merge) { @@ -729,11 +1039,18 @@ void Writer::mergeSections() { } } +// Visits all sections to initialize their relocation targets. +void Writer::readRelocTargets() { + for (OutputSection *Sec : OutputSections) + for_each(parallel::par, Sec->Chunks.begin(), Sec->Chunks.end(), + [&](Chunk *C) { C->readRelocTargets(); }); +} + // Visits all sections to assign incremental, non-overlapping RVAs and // file offsets. void Writer::assignAddresses() { SizeOfHeaders = DOSStubSize + sizeof(PEMagic) + sizeof(coff_file_header) + - sizeof(data_directory) * NumberfOfDataDirectory + + sizeof(data_directory) * NumberOfDataDirectory + sizeof(coff_section) * OutputSections.size(); SizeOfHeaders += Config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header); @@ -746,7 +1063,7 @@ void Writer::assignAddresses() { addBaserels(); uint64_t RawSize = 0, VirtualSize = 0; Sec->Header.VirtualAddress = RVA; - for (Chunk *C : Sec->getChunks()) { + for (Chunk *C : Sec->Chunks) { VirtualSize = alignTo(VirtualSize, C->Alignment); C->setRVA(RVA + VirtualSize); C->OutputSectionOff = VirtualSize; @@ -808,7 +1125,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() { if (!Config->Relocatable) COFF->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED; COFF->SizeOfOptionalHeader = - sizeof(PEHeaderTy) + sizeof(data_directory) * NumberfOfDataDirectory; + sizeof(PEHeaderTy) + sizeof(data_directory) * NumberOfDataDirectory; // Write PE header auto *PE = reinterpret_cast<PEHeaderTy *>(Buf); @@ -866,7 +1183,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() { PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH; if (Config->TerminalServerAware) PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE; - PE->NumberOfRvaAndSize = NumberfOfDataDirectory; + PE->NumberOfRvaAndSize = NumberOfDataDirectory; if (TextSec->getVirtualSize()) { PE->BaseOfCode = TextSec->getRVA(); PE->SizeOfCode = TextSec->getRawSize(); @@ -875,16 +1192,18 @@ template <typename PEHeaderTy> void Writer::writeHeader() { // Write data directory auto *Dir = reinterpret_cast<data_directory *>(Buf); - Buf += sizeof(*Dir) * NumberfOfDataDirectory; + Buf += sizeof(*Dir) * NumberOfDataDirectory; 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(); - Dir[IMPORT_TABLE].Size = Idata.getDirSize(); - Dir[IAT].RelativeVirtualAddress = Idata.getIATRVA(); - Dir[IAT].Size = Idata.getIATSize(); + if (ImportTableStart) { + Dir[IMPORT_TABLE].RelativeVirtualAddress = ImportTableStart->getRVA(); + Dir[IMPORT_TABLE].Size = ImportTableSize; + } + if (IATStart) { + Dir[IAT].RelativeVirtualAddress = IATStart->getRVA(); + Dir[IAT].Size = IATSize; } if (RsrcSec->getVirtualSize()) { Dir[RESOURCE_TABLE].RelativeVirtualAddress = RsrcSec->getRVA(); @@ -907,7 +1226,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() { : sizeof(object::coff_tls_directory32); } } - if (Config->Debug) { + if (DebugDirectory) { Dir[DEBUG_DIRECTORY].RelativeVirtualAddress = DebugDirectory->getRVA(); Dir[DEBUG_DIRECTORY].Size = DebugDirectory->getSize(); } @@ -1002,6 +1321,25 @@ static void addSymbolToRVASet(SymbolRVASet &RVASet, Defined *S) { RVASet.insert({C, Off}); } +// Given a symbol, add it to the GFIDs table if it is a live, defined, function +// symbol in an executable section. +static void maybeAddAddressTakenFunction(SymbolRVASet &AddressTakenSyms, + Symbol *S) { + auto *D = dyn_cast_or_null<DefinedCOFF>(S); + + // Ignore undefined symbols and references to non-functions (e.g. globals and + // labels). + if (!D || + D->getCOFFSymbol().getComplexType() != COFF::IMAGE_SYM_DTYPE_FUNCTION) + return; + + // Mark the symbol as address taken if it's in an executable section. + Chunk *RefChunk = D->getChunk(); + OutputSection *OS = RefChunk ? RefChunk->getOutputSection() : nullptr; + if (OS && OS->Header.Characteristics & IMAGE_SCN_MEM_EXECUTE) + addSymbolToRVASet(AddressTakenSyms, D); +} + // Visit all relocations from all section contributions of this object file and // mark the relocation target as address-taken. static void markSymbolsWithRelocations(ObjFile *File, @@ -1010,21 +1348,17 @@ static void markSymbolsWithRelocations(ObjFile *File, // 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()) + if (!SC || !SC->Live) 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); - } + for (const coff_relocation &Reloc : SC->Relocs) { + if (Config->Machine == I386 && Reloc.Type == COFF::IMAGE_REL_I386_REL32) + // Ignore relative relocations on x86. On x86_64 they can't be ignored + // since they're also used to compute absolute addresses. + continue; + + Symbol *Ref = SC->File->getSymbol(Reloc.SymbolTableIndex); + maybeAddAddressTakenFunction(UsedSymbols, Ref); } } } @@ -1051,7 +1385,11 @@ void Writer::createGuardCFTables() { // Mark the image entry as address-taken. if (Config->Entry) - addSymbolToRVASet(AddressTakenSyms, cast<Defined>(Config->Entry)); + maybeAddAddressTakenFunction(AddressTakenSyms, Config->Entry); + + // Mark exported symbols in executable sections as address-taken. + for (Export &E : Config->Exports) + maybeAddAddressTakenFunction(AddressTakenSyms, E.Sym); // Ensure sections referenced in the gfid table are 16-byte aligned. for (const ChunkAndOffset &C : AddressTakenSyms) @@ -1087,7 +1425,7 @@ void Writer::markSymbolsForRVATable(ObjFile *File, // 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()) + if (!C->Live) continue; // Validate that the contents look like symbol table indices. @@ -1134,6 +1472,56 @@ void Writer::maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym, cast<DefinedAbsolute>(C)->setVA(TableChunk->getSize() / 4); } +// MinGW specific. Gather all relocations that are imported from a DLL even +// though the code didn't expect it to, produce the table that the runtime +// uses for fixing them up, and provide the synthetic symbols that the +// runtime uses for finding the table. +void Writer::createRuntimePseudoRelocs() { + std::vector<RuntimePseudoReloc> Rels; + + for (Chunk *C : Symtab->getChunks()) { + auto *SC = dyn_cast<SectionChunk>(C); + if (!SC || !SC->Live) + continue; + SC->getRuntimePseudoRelocs(Rels); + } + + if (!Rels.empty()) + log("Writing " + Twine(Rels.size()) + " runtime pseudo relocations"); + PseudoRelocTableChunk *Table = make<PseudoRelocTableChunk>(Rels); + RdataSec->addChunk(Table); + EmptyChunk *EndOfList = make<EmptyChunk>(); + RdataSec->addChunk(EndOfList); + + Symbol *HeadSym = Symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__"); + Symbol *EndSym = Symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__"); + replaceSymbol<DefinedSynthetic>(HeadSym, HeadSym->getName(), Table); + replaceSymbol<DefinedSynthetic>(EndSym, EndSym->getName(), EndOfList); +} + +// MinGW specific. +// The MinGW .ctors and .dtors lists have sentinels at each end; +// a (uintptr_t)-1 at the start and a (uintptr_t)0 at the end. +// There's a symbol pointing to the start sentinel pointer, __CTOR_LIST__ +// and __DTOR_LIST__ respectively. +void Writer::insertCtorDtorSymbols() { + AbsolutePointerChunk *CtorListHead = make<AbsolutePointerChunk>(-1); + AbsolutePointerChunk *CtorListEnd = make<AbsolutePointerChunk>(0); + AbsolutePointerChunk *DtorListHead = make<AbsolutePointerChunk>(-1); + AbsolutePointerChunk *DtorListEnd = make<AbsolutePointerChunk>(0); + CtorsSec->insertChunkAtStart(CtorListHead); + CtorsSec->addChunk(CtorListEnd); + DtorsSec->insertChunkAtStart(DtorListHead); + DtorsSec->addChunk(DtorListEnd); + + Symbol *CtorListSym = Symtab->findUnderscore("__CTOR_LIST__"); + Symbol *DtorListSym = Symtab->findUnderscore("__DTOR_LIST__"); + replaceSymbol<DefinedSynthetic>(CtorListSym, CtorListSym->getName(), + CtorListHead); + replaceSymbol<DefinedSynthetic>(DtorListSym, DtorListSym->getName(), + DtorListHead); +} + // Handles /section options to allow users to overwrite // section attributes. void Writer::setSectionPermissions() { @@ -1160,7 +1548,7 @@ void Writer::writeSections() { // ADD instructions). if (Sec->Header.Characteristics & IMAGE_SCN_CNT_CODE) memset(SecBuf, 0xCC, Sec->getRawSize()); - for_each(parallel::par, Sec->getChunks().begin(), Sec->getChunks().end(), + for_each(parallel::par, Sec->Chunks.begin(), Sec->Chunks.end(), [&](Chunk *C) { C->writeTo(SecBuf); }); } } @@ -1171,25 +1559,10 @@ void Writer::writeBuildId() { // 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). + // PE contents. 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); - } + // BuildId->BuildId was filled in when the PDB was written. } // At this point the only fields in the COFF file which remain unset are the @@ -1201,8 +1574,25 @@ void Writer::writeBuildId() { Buffer->getBufferSize()); uint32_t Timestamp = Config->Timestamp; + uint64_t Hash = 0; + bool GenerateSyntheticBuildId = + Config->MinGW && Config->Debug && Config->PDBPath.empty(); + + if (Config->Repro || GenerateSyntheticBuildId) + Hash = xxHash64(OutputFileData); + if (Config->Repro) - Timestamp = static_cast<uint32_t>(xxHash64(OutputFileData)); + Timestamp = static_cast<uint32_t>(Hash); + + if (GenerateSyntheticBuildId) { + // For MinGW builds without a PDB file, we still generate a build id + // to allow associating a crash dump to the executable. + BuildId->BuildId->PDB70.CVSignature = OMF::Signature::PDB70; + BuildId->BuildId->PDB70.Age = 1; + memcpy(BuildId->BuildId->PDB70.Signature, &Hash, 8); + // xxhash only gives us 8 bytes, so put some fixed data in the other half. + memcpy(&BuildId->BuildId->PDB70.Signature[8], "LLD PDB.", 8); + } if (DebugDirectory) DebugDirectory->setTimeDateStamp(Timestamp); @@ -1240,6 +1630,42 @@ void Writer::sortExceptionTable() { errs() << "warning: don't know how to handle .pdata.\n"; } +// The CRT section contains, among other things, the array of function +// pointers that initialize every global variable that is not trivially +// constructed. The CRT calls them one after the other prior to invoking +// main(). +// +// As per C++ spec, 3.6.2/2.3, +// "Variables with ordered initialization defined within a single +// translation unit shall be initialized in the order of their definitions +// in the translation unit" +// +// It is therefore critical to sort the chunks containing the function +// pointers in the order that they are listed in the object file (top to +// bottom), otherwise global objects might not be initialized in the +// correct order. +void Writer::sortCRTSectionChunks(std::vector<Chunk *> &Chunks) { + auto SectionChunkOrder = [](const Chunk *A, const Chunk *B) { + auto SA = dyn_cast<SectionChunk>(A); + auto SB = dyn_cast<SectionChunk>(B); + assert(SA && SB && "Non-section chunks in CRT section!"); + + StringRef SAObj = SA->File->MB.getBufferIdentifier(); + StringRef SBObj = SB->File->MB.getBufferIdentifier(); + + return SAObj == SBObj && SA->getSectionNumber() < SB->getSectionNumber(); + }; + std::stable_sort(Chunks.begin(), Chunks.end(), SectionChunkOrder); + + if (Config->Verbose) { + for (auto &C : Chunks) { + auto SC = dyn_cast<SectionChunk>(C); + log(" " + SC->File->MB.getBufferIdentifier().str() + + ", SectionID: " + Twine(SC->getSectionNumber())); + } + } +} + OutputSection *Writer::findSection(StringRef Name) { for (OutputSection *Sec : OutputSections) if (Sec->Name == Name) @@ -1259,12 +1685,13 @@ uint32_t Writer::getSizeOfInitializedData() { void Writer::addBaserels() { if (!Config->Relocatable) return; + RelocSec->Chunks.clear(); std::vector<Baserel> V; for (OutputSection *Sec : OutputSections) { if (Sec->Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) continue; // Collect all locations for base relocations. - for (Chunk *C : Sec->getChunks()) + for (Chunk *C : Sec->Chunks) C->getBaserels(&V); // Add the addresses to .reloc section. if (!V.empty()) diff --git a/COFF/Writer.h b/COFF/Writer.h index d37276cb6d91..727582480c91 100644 --- a/COFF/Writer.h +++ b/COFF/Writer.h @@ -34,8 +34,8 @@ public: Header.Characteristics = Chars; } void addChunk(Chunk *C); + void insertChunkAtStart(Chunk *C); void merge(OutputSection *Other); - ArrayRef<Chunk *> getChunks() { return Chunks; } void addPermissions(uint32_t C); void setPermissions(uint32_t C); uint64_t getRVA() { return Header.VirtualAddress; } @@ -62,9 +62,11 @@ public: llvm::StringRef Name; llvm::object::coff_section Header = {}; + std::vector<Chunk *> Chunks; + std::vector<Chunk *> OrigChunks; + private: uint32_t StringTableOff = 0; - std::vector<Chunk *> Chunks; }; } diff --git a/Common/Args.cpp b/Common/Args.cpp index ff77bfcc3b76..3f0671d72a66 100644 --- a/Common/Args.cpp +++ b/Common/Args.cpp @@ -13,6 +13,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Option/ArgList.h" +#include "llvm/Support/Path.h" using namespace llvm; using namespace lld; @@ -40,7 +41,7 @@ std::vector<StringRef> lld::args::getStrings(opt::InputArgList &Args, int Id) { uint64_t lld::args::getZOptionValue(opt::InputArgList &Args, int Id, StringRef Key, uint64_t Default) { - for (auto *Arg : Args.filtered(Id)) { + for (auto *Arg : Args.filtered_reverse(Id)) { std::pair<StringRef, StringRef> KV = StringRef(Arg->getValue()).split('='); if (KV.first == Key) { uint64_t Result = Default; @@ -64,3 +65,9 @@ std::vector<StringRef> lld::args::getLines(MemoryBufferRef MB) { } return Ret; } + +StringRef lld::args::getFilenameWithoutExe(StringRef Path) { + if (Path.endswith_lower(".exe")) + return sys::path::stem(Path); + return sys::path::filename(Path); +} diff --git a/Common/ErrorHandler.cpp b/Common/ErrorHandler.cpp index d1cb3dbbe03c..c059516daf94 100644 --- a/Common/ErrorHandler.cpp +++ b/Common/ErrorHandler.cpp @@ -47,8 +47,9 @@ ErrorHandler &lld::errorHandler() { } void lld::exitLld(int Val) { - // Delete the output buffer so that any tempory file is deleted. - errorHandler().OutputBuffer.reset(); + // Delete any temporary file, while keeping the memory mapping open. + if (errorHandler().OutputBuffer) + errorHandler().OutputBuffer->discard(); // Dealloc/destroy ManagedStatic variables before calling // _exit(). In a non-LTO build, this is a nop. In an LTO diff --git a/Common/Strings.cpp b/Common/Strings.cpp index 36f4f77d8476..6f74865b7f42 100644 --- a/Common/Strings.cpp +++ b/Common/Strings.cpp @@ -16,14 +16,6 @@ #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; @@ -45,18 +37,21 @@ Optional<std::string> lld::demangleItanium(StringRef Name) { 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); +Optional<std::string> lld::demangleMSVC(StringRef Name) { + std::string Prefix; + if (Name.consume_front("__imp_")) + Prefix = "__declspec(dllimport) "; + + // Demangle only C++ names. + if (!Name.startswith("?")) + return None; - 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; + char *Buf = microsoftDemangle(Name.str().c_str(), nullptr, nullptr, nullptr); + if (!Buf) + return None; + std::string S(Buf); + free(Buf); + return Prefix + S; } StringMatcher::StringMatcher(ArrayRef<StringRef> Pat) { diff --git a/Common/TargetOptionsCommandFlags.cpp b/Common/TargetOptionsCommandFlags.cpp index b46df363c361..7a3fc510704f 100644 --- a/Common/TargetOptionsCommandFlags.cpp +++ b/Common/TargetOptionsCommandFlags.cpp @@ -32,3 +32,4 @@ llvm::Optional<llvm::CodeModel::Model> lld::GetCodeModelFromCMModel() { } std::string lld::GetCPUStr() { return ::getCPUStr(); } +std::vector<std::string> lld::GetMAttrs() { return ::MAttrs; } diff --git a/ELF/AArch64ErrataFix.cpp b/ELF/AArch64ErrataFix.cpp index 7551919cf86f..ac753cb58265 100644 --- a/ELF/AArch64ErrataFix.cpp +++ b/ELF/AArch64ErrataFix.cpp @@ -356,7 +356,7 @@ static uint64_t scanCortexA53Errata843419(InputSection *IS, uint64_t &Off, } uint64_t PatchOff = 0; - const uint8_t *Buf = IS->Data.begin(); + const uint8_t *Buf = IS->data().begin(); const ulittle32_t *InstBuf = reinterpret_cast<const ulittle32_t *>(Buf + Off); uint32_t Instr1 = *InstBuf++; uint32_t Instr2 = *InstBuf++; @@ -411,7 +411,7 @@ uint64_t lld::elf::Patch843419Section::getLDSTAddr() const { void lld::elf::Patch843419Section::writeTo(uint8_t *Buf) { // Copy the instruction that we will be replacing with a branch in the // Patchee Section. - write32le(Buf, read32le(Patchee->Data.begin() + PatcheeOffset)); + write32le(Buf, read32le(Patchee->data().begin() + PatcheeOffset)); // Apply any relocation transferred from the original PatcheeSection. // For a SyntheticSection Buf already has OutSecOff added, but relocateAlloc @@ -451,7 +451,7 @@ void AArch64Err843419Patcher::init() { continue; if (!IsCodeMapSymbol(Def) && !IsDataMapSymbol(Def)) continue; - if (auto *Sec = dyn_cast<InputSection>(Def->Section)) + if (auto *Sec = dyn_cast_or_null<InputSection>(Def->Section)) if (Sec->Flags & SHF_EXECINSTR) SectionMap[Sec].push_back(Def); } @@ -487,7 +487,8 @@ void AArch64Err843419Patcher::insertPatches( InputSectionDescription &ISD, std::vector<Patch843419Section *> &Patches) { uint64_t ISLimit; uint64_t PrevISLimit = ISD.Sections.front()->OutSecOff; - uint64_t PatchUpperBound = PrevISLimit + Target->ThunkSectionSpacing; + uint64_t PatchUpperBound = PrevISLimit + Target->getThunkSectionSpacing(); + uint64_t OutSecAddr = ISD.Sections.front()->getParent()->Addr; // Set the OutSecOff of patches to the place where we want to insert them. // We use a similar strategy to Thunk placement. Place patches roughly @@ -498,12 +499,12 @@ void AArch64Err843419Patcher::insertPatches( ISLimit = IS->OutSecOff + IS->getSize(); if (ISLimit > PatchUpperBound) { while (PatchIt != PatchEnd) { - if ((*PatchIt)->getLDSTAddr() >= PrevISLimit) + if ((*PatchIt)->getLDSTAddr() - OutSecAddr >= PrevISLimit) break; (*PatchIt)->OutSecOff = PrevISLimit; ++PatchIt; } - PatchUpperBound = PrevISLimit + Target->ThunkSectionSpacing; + PatchUpperBound = PrevISLimit + Target->getThunkSectionSpacing(); } PrevISLimit = ISLimit; } @@ -538,20 +539,24 @@ static void implementPatch(uint64_t AdrpAddr, uint64_t PatcheeOffset, InputSection *IS, std::vector<Patch843419Section *> &Patches) { // There may be a relocation at the same offset that we are patching. There - // are three cases that we need to consider. + // are four cases that we need to consider. // Case 1: R_AARCH64_JUMP26 branch relocation. We have already patched this // instance of the erratum on a previous patch and altered the relocation. We // have nothing more to do. - // Case 2: A load/store register (unsigned immediate) class relocation. There + // Case 2: A TLS Relaxation R_RELAX_TLS_IE_TO_LE. In this case the ADRP that + // we read will be transformed into a MOVZ later so we actually don't match + // the sequence and have nothing more to do. + // Case 3: A load/store register (unsigned immediate) class relocation. There // are two of these R_AARCH_LD64_ABS_LO12_NC and R_AARCH_LD64_GOT_LO12_NC and // they are both absolute. We need to add the same relocation to the patch, // and replace the relocation with a R_AARCH_JUMP26 branch relocation. - // Case 3: No relocation. We must create a new R_AARCH64_JUMP26 branch + // Case 4: No relocation. We must create a new R_AARCH64_JUMP26 branch // relocation at the offset. auto RelIt = std::find_if( IS->Relocations.begin(), IS->Relocations.end(), [=](const Relocation &R) { return R.Offset == PatcheeOffset; }); - if (RelIt != IS->Relocations.end() && RelIt->Type == R_AARCH64_JUMP26) + if (RelIt != IS->Relocations.end() && + (RelIt->Type == R_AARCH64_JUMP26 || RelIt->Expr == R_RELAX_TLS_IE_TO_LE)) return; log("detected cortex-a53-843419 erratum sequence starting at " + @@ -598,7 +603,7 @@ AArch64Err843419Patcher::patchInputSectionDescription( auto DataSym = std::next(CodeSym); uint64_t Off = (*CodeSym)->Value; uint64_t Limit = - (DataSym == MapSyms.end()) ? IS->Data.size() : (*DataSym)->Value; + (DataSym == MapSyms.end()) ? IS->data().size() : (*DataSym)->Value; while (Off < Limit) { uint64_t StartAddr = IS->getVA(Off); diff --git a/ELF/Arch/AArch64.cpp b/ELF/Arch/AArch64.cpp index c7b3c0801de2..08ffe2a08c0f 100644 --- a/ELF/Arch/AArch64.cpp +++ b/ELF/Arch/AArch64.cpp @@ -41,6 +41,7 @@ public: int32_t Index, unsigned RelOff) const override; bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, uint64_t BranchAddr, const Symbol &S) const override; + uint32_t getThunkSectionSpacing() const override; bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override; bool usesOnlyLowPageBits(RelType Type) const override; void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; @@ -57,6 +58,7 @@ AArch64::AArch64() { RelativeRel = R_AARCH64_RELATIVE; IRelativeRel = R_AARCH64_IRELATIVE; GotRel = R_AARCH64_GLOB_DAT; + NoneRel = R_AARCH64_NONE; PltRel = R_AARCH64_JUMP_SLOT; TlsDescRel = R_AARCH64_TLSDESC; TlsGotRel = R_AARCH64_TLS_TPREL64; @@ -66,22 +68,18 @@ AArch64::AArch64() { PltHeaderSize = 32; DefaultMaxPageSize = 65536; - // It doesn't seem to be documented anywhere, but tls on aarch64 uses variant - // 1 of the tls structures and the tcb size is 16. - TcbSize = 16; - NeedsThunks = true; + // Align to the 2 MiB page size (known as a superpage or huge page). + // FreeBSD automatically promotes 2 MiB-aligned allocations. + DefaultImageBase = 0x200000; - // See comment in Arch/ARM.cpp for a more detailed explanation of - // ThunkSectionSpacing. For AArch64 the only branches we are permitted to - // Thunk have a range of +/- 128 MiB - ThunkSectionSpacing = (128 * 1024 * 1024) - 0x30000; + NeedsThunks = true; } RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const { switch (Type) { case R_AARCH64_TLSDESC_ADR_PAGE21: - return R_TLSDESC_PAGE; + return R_AARCH64_TLSDESC_PAGE; case R_AARCH64_TLSDESC_LD64_LO12: case R_AARCH64_TLSDESC_ADD_LO12: return R_TLSDESC; @@ -107,13 +105,13 @@ RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S, case R_AARCH64_LD_PREL_LO19: return R_PC; case R_AARCH64_ADR_PREL_PG_HI21: - return R_PAGE_PC; + return R_AARCH64_PAGE_PC; case R_AARCH64_LD64_GOT_LO12_NC: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: return R_GOT; case R_AARCH64_ADR_GOT_PAGE: case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: - return R_GOT_PAGE_PC; + return R_AARCH64_GOT_PAGE_PC; case R_AARCH64_NONE: return R_NONE; default: @@ -125,7 +123,7 @@ RelExpr AArch64::adjustRelaxExpr(RelType Type, const uint8_t *Data, RelExpr Expr) const { if (Expr == R_RELAX_TLS_GD_TO_IE) { if (Type == R_AARCH64_TLSDESC_ADR_PAGE21) - return R_RELAX_TLS_GD_TO_IE_PAGE_PC; + return R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC; return R_RELAX_TLS_GD_TO_IE_ABS; } return Expr; @@ -156,7 +154,7 @@ RelType AArch64::getDynRel(RelType Type) const { } void AArch64::writeGotPlt(uint8_t *Buf, const Symbol &) const { - write64le(Buf, InX::Plt->getVA()); + write64le(Buf, In.Plt->getVA()); } void AArch64::writePltHeader(uint8_t *Buf) const { @@ -172,8 +170,8 @@ void AArch64::writePltHeader(uint8_t *Buf) const { }; memcpy(Buf, PltData, sizeof(PltData)); - uint64_t Got = InX::GotPlt->getVA(); - uint64_t Plt = InX::Plt->getVA(); + uint64_t Got = In.GotPlt->getVA(); + uint64_t Plt = In.Plt->getVA(); relocateOne(Buf + 4, R_AARCH64_ADR_PREL_PG_HI21, getAArch64Page(Got + 16) - getAArch64Page(Plt + 4)); relocateOne(Buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, Got + 16); @@ -208,6 +206,13 @@ bool AArch64::needsThunk(RelExpr Expr, RelType Type, const InputFile *File, return !inBranchRange(Type, BranchAddr, Dst); } +uint32_t AArch64::getThunkSectionSpacing() const { + // See comment in Arch/ARM.cpp for a more detailed explanation of + // getThunkSectionSpacing(). For AArch64 the only branches we are permitted to + // Thunk have a range of +/- 128 MiB + return (128 * 1024 * 1024) - 0x30000; +} + bool AArch64::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const { if (Type != R_AARCH64_CALL26 && Type != R_AARCH64_JUMP26) return true; @@ -338,7 +343,7 @@ void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { or32le(Loc, (Val & 0xFFFC) << 3); break; case R_AARCH64_TLSLE_ADD_TPREL_HI12: - checkInt(Loc, Val, 24, Type); + checkUInt(Loc, Val, 24, Type); or32AArch64Imm(Loc, Val >> 12); break; case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: diff --git a/ELF/Arch/AMDGPU.cpp b/ELF/Arch/AMDGPU.cpp index 48b27f23510c..a7c6c84ceecd 100644 --- a/ELF/Arch/AMDGPU.cpp +++ b/ELF/Arch/AMDGPU.cpp @@ -35,6 +35,7 @@ public: AMDGPU::AMDGPU() { RelativeRel = R_AMDGPU_RELATIVE64; GotRel = R_AMDGPU_ABS64; + NoneRel = R_AMDGPU_NONE; GotEntrySize = 8; } diff --git a/ELF/Arch/ARM.cpp b/ELF/Arch/ARM.cpp index acf9a615f20b..120caca671af 100644 --- a/ELF/Arch/ARM.cpp +++ b/ELF/Arch/ARM.cpp @@ -40,6 +40,7 @@ public: void addPltHeaderSymbols(InputSection &ISD) const override; bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, uint64_t BranchAddr, const Symbol &S) const override; + uint32_t getThunkSectionSpacing() const override; bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override; void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; }; @@ -50,6 +51,7 @@ ARM::ARM() { RelativeRel = R_ARM_RELATIVE; IRelativeRel = R_ARM_IRELATIVE; GotRel = R_ARM_GLOB_DAT; + NoneRel = R_ARM_NONE; PltRel = R_ARM_JUMP_SLOT; TlsGotRel = R_ARM_TLS_TPOFF32; TlsModuleIndexRel = R_ARM_TLS_DTPMOD32; @@ -59,41 +61,8 @@ ARM::ARM() { GotPltEntrySize = 4; PltEntrySize = 16; PltHeaderSize = 32; - TrapInstr = 0xd4d4d4d4; - // ARM uses Variant 1 TLS - TcbSize = 8; + TrapInstr = {0xd4, 0xd4, 0xd4, 0xd4}; NeedsThunks = true; - - // The placing of pre-created ThunkSections is controlled by the - // ThunkSectionSpacing parameter. The aim is to place the - // ThunkSection such that all branches from the InputSections prior to the - // ThunkSection can reach a Thunk placed at the end of the ThunkSection. - // Graphically: - // | up to ThunkSectionSpacing .text input sections | - // | ThunkSection | - // | up to ThunkSectionSpacing .text input sections | - // | ThunkSection | - - // Pre-created ThunkSections are spaced roughly 16MiB apart on ARM. This is to - // match the most common expected case of a Thumb 2 encoded BL, BLX or B.W - // ARM B, BL, BLX range +/- 32MiB - // Thumb B.W, BL, BLX range +/- 16MiB - // Thumb B<cc>.W range +/- 1MiB - // If a branch cannot reach a pre-created ThunkSection a new one will be - // created so we can handle the rare cases of a Thumb 2 conditional branch. - // We intentionally use a lower size for ThunkSectionSpacing than the maximum - // branch range so the end of the ThunkSection is more likely to be within - // range of the branch instruction that is furthest away. The value we shorten - // ThunkSectionSpacing by is set conservatively to allow us to create 16,384 - // 12 byte Thunks at any offset in a ThunkSection without risk of a branch to - // one of the Thunks going out of range. - - // FIXME: lld assumes that the Thumb BL and BLX encoding permits the J1 and - // J2 bits to be used to extend the branch range. On earlier Architectures - // such as ARMv4, ARMv5 and ARMv6 (except ARMv6T2) the range is +/- 4MiB. If - // support for the earlier encodings is added then when they are used the - // ThunkSectionSpacing will need lowering. - ThunkSectionSpacing = 0x1000000 - 0x30000; } uint32_t ARM::calcEFlags() const { @@ -165,6 +134,12 @@ RelExpr ARM::getRelExpr(RelType Type, const Symbol &S, return R_NONE; case R_ARM_TLS_LE32: return R_TLS; + case R_ARM_V4BX: + // V4BX is just a marker to indicate there's a "bx rN" instruction at the + // given address. It can be used to implement a special linker mode which + // rewrites ARMv4T inputs to ARMv4. Since we support only ARMv4 input and + // not ARMv4 output, we can just ignore it. + return R_HINT; default: return R_ABS; } @@ -177,7 +152,7 @@ RelType ARM::getDynRel(RelType Type) const { } void ARM::writeGotPlt(uint8_t *Buf, const Symbol &) const { - write32le(Buf, InX::Plt->getVA()); + write32le(Buf, In.Plt->getVA()); } void ARM::writeIgotPlt(uint8_t *Buf, const Symbol &S) const { @@ -198,8 +173,8 @@ static void writePltHeaderLong(uint8_t *Buf) { 0xd4, 0xd4, 0xd4, 0xd4, // Pad to 32-byte boundary 0xd4, 0xd4, 0xd4, 0xd4}; memcpy(Buf, PltData, sizeof(PltData)); - uint64_t GotPlt = InX::GotPlt->getVA(); - uint64_t L1 = InX::Plt->getVA() + 8; + uint64_t GotPlt = In.GotPlt->getVA(); + uint64_t L1 = In.Plt->getVA() + 8; write32le(Buf + 16, GotPlt - L1 - 8); } @@ -217,7 +192,7 @@ void ARM::writePltHeader(uint8_t *Buf) const { 0xe5bef000, // ldr pc, [lr, #0x00000NNN] &(.got.plt -L1 - 4) }; - uint64_t Offset = InX::GotPlt->getVA() - InX::Plt->getVA() - 4; + uint64_t Offset = In.GotPlt->getVA() - In.Plt->getVA() - 4; if (!llvm::isUInt<27>(Offset)) { // We cannot encode the Offset, use the long form. writePltHeaderLong(Buf); @@ -227,10 +202,10 @@ void ARM::writePltHeader(uint8_t *Buf) const { write32le(Buf + 4, PltData[1] | ((Offset >> 20) & 0xff)); write32le(Buf + 8, PltData[2] | ((Offset >> 12) & 0xff)); write32le(Buf + 12, PltData[3] | (Offset & 0xfff)); - write32le(Buf + 16, TrapInstr); // Pad to 32-byte boundary - write32le(Buf + 20, TrapInstr); - write32le(Buf + 24, TrapInstr); - write32le(Buf + 28, TrapInstr); + memcpy(Buf + 16, TrapInstr.data(), 4); // Pad to 32-byte boundary + memcpy(Buf + 20, TrapInstr.data(), 4); + memcpy(Buf + 24, TrapInstr.data(), 4); + memcpy(Buf + 28, TrapInstr.data(), 4); } void ARM::addPltHeaderSymbols(InputSection &IS) const { @@ -279,7 +254,7 @@ void ARM::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, write32le(Buf + 0, PltData[0] | ((Offset >> 20) & 0xff)); write32le(Buf + 4, PltData[1] | ((Offset >> 12) & 0xff)); write32le(Buf + 8, PltData[2] | (Offset & 0xfff)); - write32le(Buf + 12, TrapInstr); // Pad to 16-byte boundary + memcpy(Buf + 12, TrapInstr.data(), 4); // Pad to 16-byte boundary } void ARM::addPltSymbols(InputSection &IS, uint64_t Off) const { @@ -324,6 +299,40 @@ bool ARM::needsThunk(RelExpr Expr, RelType Type, const InputFile *File, return false; } +uint32_t ARM::getThunkSectionSpacing() const { + // The placing of pre-created ThunkSections is controlled by the value + // ThunkSectionSpacing returned by getThunkSectionSpacing(). The aim is to + // place the ThunkSection such that all branches from the InputSections + // prior to the ThunkSection can reach a Thunk placed at the end of the + // ThunkSection. Graphically: + // | up to ThunkSectionSpacing .text input sections | + // | ThunkSection | + // | up to ThunkSectionSpacing .text input sections | + // | ThunkSection | + + // Pre-created ThunkSections are spaced roughly 16MiB apart on ARMv7. This + // is to match the most common expected case of a Thumb 2 encoded BL, BLX or + // B.W: + // ARM B, BL, BLX range +/- 32MiB + // Thumb B.W, BL, BLX range +/- 16MiB + // Thumb B<cc>.W range +/- 1MiB + // If a branch cannot reach a pre-created ThunkSection a new one will be + // created so we can handle the rare cases of a Thumb 2 conditional branch. + // We intentionally use a lower size for ThunkSectionSpacing than the maximum + // branch range so the end of the ThunkSection is more likely to be within + // range of the branch instruction that is furthest away. The value we shorten + // ThunkSectionSpacing by is set conservatively to allow us to create 16,384 + // 12 byte Thunks at any offset in a ThunkSection without risk of a branch to + // one of the Thunks going out of range. + + // On Arm the ThunkSectionSpacing depends on the range of the Thumb Branch + // range. On earlier Architectures such as ARMv4, ARMv5 and ARMv6 (except + // ARMv6T2) the range is +/- 4MiB. + + return (Config->ARMJ1J2BranchEncoding) ? 0x1000000 - 0x30000 + : 0x400000 - 0x7500; +} + bool ARM::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const { uint64_t Range; uint64_t InstrSize; @@ -342,7 +351,7 @@ bool ARM::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const { break; case R_ARM_THM_JUMP24: case R_ARM_THM_CALL: - Range = 0x1000000; + Range = Config->ARMJ1J2BranchEncoding ? 0x1000000 : 0x400000; InstrSize = 2; break; default: @@ -447,11 +456,23 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { } // Bit 12 is 0 for BLX, 1 for BL write16le(Loc + 2, (read16le(Loc + 2) & ~0x1000) | (Val & 1) << 12); + if (!Config->ARMJ1J2BranchEncoding) { + // Older Arm architectures do not support R_ARM_THM_JUMP24 and have + // different encoding rules and range due to J1 and J2 always being 1. + checkInt(Loc, Val, 23, Type); + write16le(Loc, + 0xf000 | // opcode + ((Val >> 12) & 0x07ff)); // imm11 + write16le(Loc + 2, + (read16le(Loc + 2) & 0xd000) | // opcode + 0x2800 | // J1 == J2 == 1 + ((Val >> 1) & 0x07ff)); // imm11 + break; + } // Fall through as rest of encoding is the same as B.W LLVM_FALLTHROUGH; 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(Loc, Val, 25, Type); write16le(Loc, 0xf000 | // opcode @@ -470,14 +491,12 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { break; case R_ARM_MOVT_ABS: case R_ARM_MOVT_PREL: - 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(Loc, Val, 32, Type); write16le(Loc, 0xf2c0 | // opcode ((Val >> 17) & 0x0400) | // i @@ -542,10 +561,19 @@ int64_t ARM::getImplicitAddend(const uint8_t *Buf, RelType Type) const { ((Lo & 0x07ff) << 1)); // imm11:0 } case R_ARM_THM_CALL: + if (!Config->ARMJ1J2BranchEncoding) { + // Older Arm architectures do not support R_ARM_THM_JUMP24 and have + // different encoding rules and range due to J1 and J2 always being 1. + uint16_t Hi = read16le(Buf); + uint16_t Lo = read16le(Buf + 2); + return SignExtend64<22>(((Hi & 0x7ff) << 12) | // imm11 + ((Lo & 0x7ff) << 1)); // imm11:0 + break; + } + LLVM_FALLTHROUGH; case R_ARM_THM_JUMP24: { // Encoding B T4, BL T1, BLX T2: A = S:I1:I2:imm10:imm11:0 // I1 = NOT(J1 EOR S), I2 = NOT(J2 EOR S) - // FIXME: I1 and I2 require v6T2ops uint16_t Hi = read16le(Buf); uint16_t Lo = read16le(Buf + 2); return SignExtend64<24>(((Hi & 0x0400) << 14) | // S diff --git a/ELF/Arch/AVR.cpp b/ELF/Arch/AVR.cpp index 02ac770127b9..637da3778bd2 100644 --- a/ELF/Arch/AVR.cpp +++ b/ELF/Arch/AVR.cpp @@ -43,12 +43,15 @@ using namespace lld::elf; namespace { class AVR final : public TargetInfo { public: + AVR(); 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 +AVR::AVR() { NoneRel = R_AVR_NONE; } + RelExpr AVR::getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const { return R_ABS; diff --git a/ELF/Arch/Hexagon.cpp b/ELF/Arch/Hexagon.cpp index ff5e862bafa2..b4d33be2ad39 100644 --- a/ELF/Arch/Hexagon.cpp +++ b/ELF/Arch/Hexagon.cpp @@ -9,6 +9,7 @@ #include "InputFiles.h" #include "Symbols.h" +#include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/BinaryFormat/ELF.h" @@ -25,15 +26,48 @@ using namespace lld::elf; namespace { class Hexagon final : public TargetInfo { public: + Hexagon(); 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; + 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; }; } // namespace -// Support V60 only at the moment. -uint32_t Hexagon::calcEFlags() const { return 0x60; } +Hexagon::Hexagon() { + PltRel = R_HEX_JMP_SLOT; + RelativeRel = R_HEX_RELATIVE; + GotRel = R_HEX_GLOB_DAT; + GotEntrySize = 4; + // The zero'th GOT entry is reserved for the address of _DYNAMIC. The + // next 3 are reserved for the dynamic loader. + GotPltHeaderEntriesNum = 4; + GotPltEntrySize = 4; + + PltEntrySize = 16; + PltHeaderSize = 32; + + // Hexagon Linux uses 64K pages by default. + DefaultMaxPageSize = 0x10000; + NoneRel = R_HEX_NONE; +} + +uint32_t Hexagon::calcEFlags() const { + assert(!ObjectFiles.empty()); + + // The architecture revision must always be equal to or greater than + // greatest revision in the list of inputs. + uint32_t Ret = 0; + for (InputFile *F : ObjectFiles) { + uint32_t EFlags = cast<ObjFile<ELF32LE>>(F)->getObj().getHeader()->e_flags; + if (EFlags > Ret) + Ret = EFlags; + } + return Ret; +} static uint32_t applyMask(uint32_t Mask, uint32_t Data) { uint32_t Result = 0; @@ -53,29 +87,143 @@ static uint32_t applyMask(uint32_t Mask, uint32_t Data) { RelExpr Hexagon::getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const { switch (Type) { + case R_HEX_B9_PCREL: + case R_HEX_B9_PCREL_X: + case R_HEX_B13_PCREL: case R_HEX_B15_PCREL: case R_HEX_B15_PCREL_X: + case R_HEX_6_PCREL_X: + case R_HEX_32_PCREL: + return R_PC; case R_HEX_B22_PCREL: + case R_HEX_PLT_B22_PCREL: case R_HEX_B22_PCREL_X: case R_HEX_B32_PCREL_X: - return R_PC; + return R_PLT_PC; + case R_HEX_GOT_11_X: + case R_HEX_GOT_16_X: + case R_HEX_GOT_32_6_X: + return R_HEXAGON_GOT; default: return R_ABS; } } +static uint32_t findMaskR6(uint32_t Insn) { + // There are (arguably too) many relocation masks for the DSP's + // R_HEX_6_X type. The table below is used to select the correct mask + // for the given instruction. + struct InstructionMask { + uint32_t CmpMask; + uint32_t RelocMask; + }; + + static const InstructionMask R6[] = { + {0x38000000, 0x0000201f}, {0x39000000, 0x0000201f}, + {0x3e000000, 0x00001f80}, {0x3f000000, 0x00001f80}, + {0x40000000, 0x000020f8}, {0x41000000, 0x000007e0}, + {0x42000000, 0x000020f8}, {0x43000000, 0x000007e0}, + {0x44000000, 0x000020f8}, {0x45000000, 0x000007e0}, + {0x46000000, 0x000020f8}, {0x47000000, 0x000007e0}, + {0x6a000000, 0x00001f80}, {0x7c000000, 0x001f2000}, + {0x9a000000, 0x00000f60}, {0x9b000000, 0x00000f60}, + {0x9c000000, 0x00000f60}, {0x9d000000, 0x00000f60}, + {0x9f000000, 0x001f0100}, {0xab000000, 0x0000003f}, + {0xad000000, 0x0000003f}, {0xaf000000, 0x00030078}, + {0xd7000000, 0x006020e0}, {0xd8000000, 0x006020e0}, + {0xdb000000, 0x006020e0}, {0xdf000000, 0x006020e0}}; + + // Duplex forms have a fixed mask and parse bits 15:14 are always + // zero. Non-duplex insns will always have at least one bit set in the + // parse field. + if ((0xC000 & Insn) == 0x0) + return 0x03f00000; + + for (InstructionMask I : R6) + if ((0xff000000 & Insn) == I.CmpMask) + return I.RelocMask; + + error("unrecognized instruction for R_HEX_6 relocation: 0x" + + utohexstr(Insn)); + return 0; +} + +static uint32_t findMaskR8(uint32_t Insn) { + if ((0xff000000 & Insn) == 0xde000000) + return 0x00e020e8; + if ((0xff000000 & Insn) == 0x3c000000) + return 0x0000207f; + return 0x00001fe0; +} + +static uint32_t findMaskR11(uint32_t Insn) { + if ((0xff000000 & Insn) == 0xa1000000) + return 0x060020ff; + return 0x06003fe0; +} + +static uint32_t findMaskR16(uint32_t Insn) { + if ((0xff000000 & Insn) == 0x48000000) + return 0x061f20ff; + if ((0xff000000 & Insn) == 0x49000000) + return 0x061f3fe0; + if ((0xff000000 & Insn) == 0x78000000) + return 0x00df3fe0; + if ((0xff000000 & Insn) == 0xb0000000) + return 0x0fe03fe0; + + error("unrecognized instruction for R_HEX_16_X relocation: 0x" + + utohexstr(Insn)); + return 0; +} + 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_6_PCREL_X: + case R_HEX_6_X: + or32le(Loc, applyMask(findMaskR6(read32le(Loc)), Val)); + break; + case R_HEX_8_X: + or32le(Loc, applyMask(findMaskR8(read32le(Loc)), Val)); + break; + case R_HEX_9_X: + or32le(Loc, applyMask(0x00003fe0, Val & 0x3f)); + break; + case R_HEX_10_X: + or32le(Loc, applyMask(0x00203fe0, Val & 0x3f)); + break; + case R_HEX_11_X: + case R_HEX_GOT_11_X: + or32le(Loc, applyMask(findMaskR11(read32le(Loc)), Val & 0x3f)); + break; case R_HEX_12_X: or32le(Loc, applyMask(0x000007e0, Val)); break; + case R_HEX_16_X: // These relocs only have 6 effective bits. + case R_HEX_GOT_16_X: + or32le(Loc, applyMask(findMaskR16(read32le(Loc)), Val & 0x3f)); + break; + case R_HEX_32: + case R_HEX_32_PCREL: + or32le(Loc, Val); + break; case R_HEX_32_6_X: + case R_HEX_GOT_32_6_X: or32le(Loc, applyMask(0x0fff3fff, Val >> 6)); break; + case R_HEX_B9_PCREL: + or32le(Loc, applyMask(0x003000fe, Val >> 2)); + break; + case R_HEX_B9_PCREL_X: + or32le(Loc, applyMask(0x003000fe, Val & 0x3f)); + break; + case R_HEX_B13_PCREL: + or32le(Loc, applyMask(0x00202ffe, Val >> 2)); + break; case R_HEX_B15_PCREL: or32le(Loc, applyMask(0x00df20fe, Val >> 2)); break; @@ -83,6 +231,7 @@ void Hexagon::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { or32le(Loc, applyMask(0x00df20fe, Val & 0x3f)); break; case R_HEX_B22_PCREL: + case R_HEX_PLT_B22_PCREL: or32le(Loc, applyMask(0x1ff3ffe, Val >> 2)); break; case R_HEX_B22_PCREL_X: @@ -91,12 +240,52 @@ void Hexagon::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_HEX_B32_PCREL_X: or32le(Loc, applyMask(0x0fff3fff, Val >> 6)); break; + case R_HEX_HI16: + or32le(Loc, applyMask(0x00c03fff, Val >> 16)); + break; + case R_HEX_LO16: + or32le(Loc, applyMask(0x00c03fff, Val)); + break; default: error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type)); break; } } +void Hexagon::writePltHeader(uint8_t *Buf) const { + const uint8_t PltData[] = { + 0x00, 0x40, 0x00, 0x00, // { immext (#0) + 0x1c, 0xc0, 0x49, 0x6a, // r28 = add (pc, ##GOT0@PCREL) } # @GOT0 + 0x0e, 0x42, 0x9c, 0xe2, // { r14 -= add (r28, #16) # offset of GOTn + 0x4f, 0x40, 0x9c, 0x91, // r15 = memw (r28 + #8) # object ID at GOT2 + 0x3c, 0xc0, 0x9c, 0x91, // r28 = memw (r28 + #4) }# dynamic link at GOT1 + 0x0e, 0x42, 0x0e, 0x8c, // { r14 = asr (r14, #2) # index of PLTn + 0x00, 0xc0, 0x9c, 0x52, // jumpr r28 } # call dynamic linker + 0x0c, 0xdb, 0x00, 0x54, // trap0(#0xdb) # bring plt0 into 16byte alignment + }; + memcpy(Buf, PltData, sizeof(PltData)); + + // Offset from PLT0 to the GOT. + uint64_t Off = In.GotPlt->getVA() - In.Plt->getVA(); + relocateOne(Buf, R_HEX_B32_PCREL_X, Off); + relocateOne(Buf + 4, R_HEX_6_PCREL_X, Off); +} + +void Hexagon::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, + uint64_t PltEntryAddr, int32_t Index, + unsigned RelOff) const { + const uint8_t Inst[] = { + 0x00, 0x40, 0x00, 0x00, // { immext (#0) + 0x0e, 0xc0, 0x49, 0x6a, // r14 = add (pc, ##GOTn@PCREL) } + 0x1c, 0xc0, 0x8e, 0x91, // r28 = memw (r14) + 0x00, 0xc0, 0x9c, 0x52, // jumpr r28 + }; + memcpy(Buf, Inst, sizeof(Inst)); + + relocateOne(Buf, R_HEX_B32_PCREL_X, GotPltEntryAddr - PltEntryAddr); + relocateOne(Buf + 4, R_HEX_6_PCREL_X, GotPltEntryAddr - PltEntryAddr); +} + TargetInfo *elf::getHexagonTargetInfo() { static Hexagon Target; return &Target; diff --git a/ELF/Arch/MSP430.cpp b/ELF/Arch/MSP430.cpp new file mode 100644 index 000000000000..fe0c0fe64daf --- /dev/null +++ b/ELF/Arch/MSP430.cpp @@ -0,0 +1,94 @@ +//===- MSP430.cpp ---------------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// The MSP430 is a 16-bit microcontroller RISC architecture. The instruction set +// has only 27 core instructions orthogonally augmented with a variety +// of addressing modes for source and destination operands. Entire address space +// of MSP430 is 64KB (the extended MSP430X architecture is not considered here). +// A typical MSP430 MCU has several kilobytes of RAM and ROM, plenty +// of peripherals and is generally optimized for a low power consumption. +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Symbols.h" +#include "Target.h" +#include "lld/Common/ErrorHandler.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 MSP430 final : public TargetInfo { +public: + MSP430(); + 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 + +MSP430::MSP430() { + // mov.b #0, r3 + TrapInstr = {0x43, 0x43, 0x43, 0x43}; +} + +RelExpr MSP430::getRelExpr(RelType Type, const Symbol &S, + const uint8_t *Loc) const { + switch (Type) { + case R_MSP430_10_PCREL: + case R_MSP430_16_PCREL: + case R_MSP430_16_PCREL_BYTE: + case R_MSP430_2X_PCREL: + case R_MSP430_RL_PCREL: + case R_MSP430_SYM_DIFF: + return R_PC; + default: + return R_ABS; + } +} + +void MSP430::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { + switch (Type) { + case R_MSP430_8: + checkIntUInt(Loc, Val, 8, Type); + *Loc = Val; + break; + case R_MSP430_16: + case R_MSP430_16_PCREL: + case R_MSP430_16_BYTE: + case R_MSP430_16_PCREL_BYTE: + checkIntUInt(Loc, Val, 16, Type); + write16le(Loc, Val); + break; + case R_MSP430_32: + checkIntUInt(Loc, Val, 32, Type); + write32le(Loc, Val); + break; + case R_MSP430_10_PCREL: { + int16_t Offset = ((int16_t)Val >> 1) - 1; + checkInt(Loc, Offset, 10, Type); + write16le(Loc, (read16le(Loc) & 0xFC00) | (Offset & 0x3FF)); + break; + } + default: + error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type)); + } +} + +TargetInfo *elf::getMSP430TargetInfo() { + static MSP430 Target; + return &Target; +} diff --git a/ELF/Arch/Mips.cpp b/ELF/Arch/Mips.cpp index dc70401c0b0e..23b0c1dd8a2d 100644 --- a/ELF/Arch/Mips.cpp +++ b/ELF/Arch/Mips.cpp @@ -53,9 +53,12 @@ template <class ELFT> MIPS<ELFT>::MIPS() { PltEntrySize = 16; PltHeaderSize = 32; CopyRel = R_MIPS_COPY; + NoneRel = R_MIPS_NONE; PltRel = R_MIPS_JUMP_SLOT; NeedsThunks = true; - TrapInstr = 0xefefefef; + + // Set `sigrie 1` as a trap instruction. + write32(TrapInstr.data(), 0x04170001); if (ELFT::Is64Bits) { RelativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32; @@ -185,7 +188,7 @@ template <class ELFT> RelType MIPS<ELFT>::getDynRel(RelType Type) const { template <class ELFT> void MIPS<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &) const { - uint64_t VA = InX::Plt->getVA(); + uint64_t VA = In.Plt->getVA(); if (isMicroMips()) VA |= 1; write32<ELFT::TargetEndianness>(Buf, VA); @@ -239,8 +242,8 @@ static void writeMicroRelocation16(uint8_t *Loc, uint64_t V, uint8_t BitsSize, template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *Buf) const { const endianness E = ELFT::TargetEndianness; if (isMicroMips()) { - uint64_t GotPlt = InX::GotPlt->getVA(); - uint64_t Plt = InX::Plt->getVA(); + uint64_t GotPlt = In.GotPlt->getVA(); + uint64_t Plt = In.Plt->getVA(); // Overwrite trap instructions written by Writer::writeTrapInstr. memset(Buf, 0, PltHeaderSize); @@ -292,7 +295,7 @@ template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *Buf) const { write32<E>(Buf + 24, JalrInst); // jalr.hb $25 or jalr $25 write32<E>(Buf + 28, 0x2718fffe); // subu $24, $24, 2 - uint64_t GotPlt = InX::GotPlt->getVA(); + uint64_t GotPlt = In.GotPlt->getVA(); writeValue<E>(Buf, GotPlt + 0x8000, 16, 16); writeValue<E>(Buf + 4, GotPlt, 16, 0); writeValue<E>(Buf + 8, GotPlt, 16, 0); diff --git a/ELF/Arch/PPC.cpp b/ELF/Arch/PPC.cpp index 20cae0e59cf4..767378067341 100644 --- a/ELF/Arch/PPC.cpp +++ b/ELF/Arch/PPC.cpp @@ -29,6 +29,7 @@ public: } // namespace PPC::PPC() { + NoneRel = R_PPC_NONE; GotBaseSymOff = 0x8000; GotBaseSymInGotPlt = false; } @@ -36,6 +37,7 @@ PPC::PPC() { RelExpr PPC::getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const { switch (Type) { + case R_PPC_REL14: case R_PPC_REL24: case R_PPC_REL32: return R_PC; @@ -61,6 +63,9 @@ void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_PPC_REL32: write32be(Loc, Val); break; + case R_PPC_REL14: + write32be(Loc, read32be(Loc) | (Val & 0xFFFC)); + break; case R_PPC_PLTREL24: case R_PPC_REL24: write32be(Loc, read32be(Loc) | (Val & 0x3FFFFFC)); diff --git a/ELF/Arch/PPC64.cpp b/ELF/Arch/PPC64.cpp index fa3bf6c62a0d..8a320c9a4e9e 100644 --- a/ELF/Arch/PPC64.cpp +++ b/ELF/Arch/PPC64.cpp @@ -23,12 +23,49 @@ using namespace lld::elf; static uint64_t PPC64TocOffset = 0x8000; static uint64_t DynamicThreadPointerOffset = 0x8000; +// The instruction encoding of bits 21-30 from the ISA for the Xform and Dform +// instructions that can be used as part of the initial exec TLS sequence. +enum XFormOpcd { + LBZX = 87, + LHZX = 279, + LWZX = 23, + LDX = 21, + STBX = 215, + STHX = 407, + STWX = 151, + STDX = 149, + ADD = 266, +}; + +enum DFormOpcd { + LBZ = 34, + LBZU = 35, + LHZ = 40, + LHZU = 41, + LHAU = 43, + LWZ = 32, + LWZU = 33, + LFSU = 49, + LD = 58, + LFDU = 51, + STB = 38, + STBU = 39, + STH = 44, + STHU = 45, + STW = 36, + STWU = 37, + STFSU = 53, + STFDU = 55, + STD = 62, + ADDI = 14 +}; + uint64_t elf::getPPC64TocBase() { // The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The // TOC starts where the first of these sections starts. We always create a // .got when we see a relocation that uses it, so for us the start is always // the .got. - uint64_t TocVA = InX::Got->getVA(); + uint64_t TocVA = In.Got->getVA(); // Per the ppc64-elf-linux ABI, The TOC base is TOC value plus 0x8000 // thus permitting a full 64 Kbytes segment. Note that the glibc startup @@ -37,6 +74,31 @@ uint64_t elf::getPPC64TocBase() { return TocVA + PPC64TocOffset; } +unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t StOther) { + // The offset is encoded into the 3 most significant bits of the st_other + // field, with some special values described in section 3.4.1 of the ABI: + // 0 --> Zero offset between the GEP and LEP, and the function does NOT use + // the TOC pointer (r2). r2 will hold the same value on returning from + // the function as it did on entering the function. + // 1 --> Zero offset between the GEP and LEP, and r2 should be treated as a + // caller-saved register for all callers. + // 2-6 --> The binary logarithm of the offset eg: + // 2 --> 2^2 = 4 bytes --> 1 instruction. + // 6 --> 2^6 = 64 bytes --> 16 instructions. + // 7 --> Reserved. + uint8_t GepToLep = (StOther >> 5) & 7; + if (GepToLep < 2) + return 0; + + // The value encoded in the st_other bits is the + // log-base-2(offset). + if (GepToLep < 7) + return 1 << GepToLep; + + error("reserved value of 7 in the 3 most-significant-bits of st_other"); + return 0; +} + namespace { class PPC64 final : public TargetInfo { public: @@ -51,11 +113,16 @@ public: void writeGotHeader(uint8_t *Buf) const override; bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, uint64_t BranchAddr, const Symbol &S) const override; + bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) 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; + void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; + + bool adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End, + uint8_t StOther) const override; }; } // namespace @@ -71,8 +138,64 @@ 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; } +// Extracts the 'PO' field of an instruction encoding. +static uint8_t getPrimaryOpCode(uint32_t Encoding) { return (Encoding >> 26); } + +static bool isDQFormInstruction(uint32_t Encoding) { + switch (getPrimaryOpCode(Encoding)) { + default: + return false; + case 56: + // The only instruction with a primary opcode of 56 is `lq`. + return true; + case 61: + // There are both DS and DQ instruction forms with this primary opcode. + // Namely `lxv` and `stxv` are the DQ-forms that use it. + // The DS 'XO' bits being set to 01 is restricted to DQ form. + return (Encoding & 3) == 0x1; + } +} + +static bool isInstructionUpdateForm(uint32_t Encoding) { + switch (getPrimaryOpCode(Encoding)) { + default: + return false; + case LBZU: + case LHAU: + case LHZU: + case LWZU: + case LFSU: + case LFDU: + case STBU: + case STHU: + case STWU: + case STFSU: + case STFDU: + return true; + // LWA has the same opcode as LD, and the DS bits is what differentiates + // between LD/LDU/LWA + case LD: + case STD: + return (Encoding & 3) == 1; + } +} + +// There are a number of places when we either want to read or write an +// instruction when handling a half16 relocation type. On big-endian the buffer +// pointer is pointing into the middle of the word we want to extract, and on +// little-endian it is pointing to the start of the word. These 2 helpers are to +// simplify reading and writing in that context. +static void writeInstrFromHalf16(uint8_t *Loc, uint32_t Instr) { + write32(Loc - (Config->EKind == ELF64BEKind ? 2 : 0), Instr); +} + +static uint32_t readInstrFromHalf16(const uint8_t *Loc) { + return read32(Loc - (Config->EKind == ELF64BEKind ? 2 : 0)); +} + PPC64::PPC64() { GotRel = R_PPC64_GLOB_DAT; + NoneRel = R_PPC64_NONE; PltRel = R_PPC64_JMP_SLOT; RelativeRel = R_PPC64_RELATIVE; IRelativeRel = R_PPC64_IRELATIVE; @@ -85,14 +208,14 @@ PPC64::PPC64() { GotPltHeaderEntriesNum = 2; PltHeaderSize = 60; NeedsThunks = true; - TcbSize = 8; - TlsTpOffset = 0x7000; TlsModuleIndexRel = R_PPC64_DTPMOD64; TlsOffsetRel = R_PPC64_DTPREL64; TlsGotRel = R_PPC64_TPREL64; + NeedsMoreStackNonSplit = false; + // We need 64K pages (at least under glibc/Linux, the loader won't // set different permissions on a finer granularity than that). DefaultMaxPageSize = 65536; @@ -107,8 +230,7 @@ PPC64::PPC64() { // use 0x10000000 as the starting address. DefaultImageBase = 0x10000000; - TrapInstr = - (Config->IsLE == sys::IsLittleEndianHost) ? 0x7fe00008 : 0x0800e07f; + write32(TrapInstr.data(), 0x7fe00008); } static uint32_t getEFlags(InputFile *File) { @@ -146,27 +268,29 @@ void PPC64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { // 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 + writeInstrFromHalf16(Loc, 0x60000000); // nop break; + case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_LO: - write32(Loc - EndianOffset, 0x3c6d0000); // addis r3, r13 + writeInstrFromHalf16(Loc, 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); + // Since we are relocating a half16 type relocation and Loc + 4 points to + // the start of an instruction we need to advance the buffer by an extra + // 2 bytes on BE. + relocateOne(Loc + 4 + (Config->EKind == ELF64BEKind ? 2 : 0), + 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: @@ -183,13 +307,12 @@ void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { // 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 + writeInstrFromHalf16(Loc, 0x60000000); // nop break; case R_PPC64_GOT_TLSLD16_LO: - write32(Loc - EndianOffset, 0x3c6d0000); // addis r3, r13, 0 + writeInstrFromHalf16(Loc, 0x3c6d0000); // addis r3, r13, 0 break; case R_PPC64_TLSLD: write32(Loc, 0x60000000); // nop @@ -212,9 +335,90 @@ void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { } } +static unsigned getDFormOp(unsigned SecondaryOp) { + switch (SecondaryOp) { + case LBZX: + return LBZ; + case LHZX: + return LHZ; + case LWZX: + return LWZ; + case LDX: + return LD; + case STBX: + return STB; + case STHX: + return STH; + case STWX: + return STW; + case STDX: + return STD; + case ADD: + return ADDI; + default: + error("unrecognized instruction for IE to LE R_PPC64_TLS"); + return 0; + } +} + +void PPC64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { + // The initial exec code sequence for a global `x` will look like: + // Instruction Relocation Symbol + // addis r9, r2, x@got@tprel@ha R_PPC64_GOT_TPREL16_HA x + // ld r9, x@got@tprel@l(r9) R_PPC64_GOT_TPREL16_LO_DS x + // add r9, r9, x@tls R_PPC64_TLS x + + // Relaxing to local exec entails converting: + // addis r9, r2, x@got@tprel@ha into nop + // ld r9, x@got@tprel@l(r9) into addis r9, r13, x@tprel@ha + // add r9, r9, x@tls into addi r9, r9, x@tprel@l + + // x@tls R_PPC64_TLS is a relocation which does not compute anything, + // it is replaced with r13 (thread pointer). + + // The add instruction in the initial exec sequence has multiple variations + // that need to be handled. If we are building an address it will use an add + // instruction, if we are accessing memory it will use any of the X-form + // indexed load or store instructions. + + unsigned Offset = (Config->EKind == ELF64BEKind) ? 2 : 0; + switch (Type) { + case R_PPC64_GOT_TPREL16_HA: + write32(Loc - Offset, 0x60000000); // nop + break; + case R_PPC64_GOT_TPREL16_LO_DS: + case R_PPC64_GOT_TPREL16_DS: { + uint32_t RegNo = read32(Loc - Offset) & 0x03E00000; // bits 6-10 + write32(Loc - Offset, 0x3C0D0000 | RegNo); // addis RegNo, r13 + relocateOne(Loc, R_PPC64_TPREL16_HA, Val); + break; + } + case R_PPC64_TLS: { + uint32_t PrimaryOp = getPrimaryOpCode(read32(Loc)); + if (PrimaryOp != 31) + error("unrecognized instruction for IE to LE R_PPC64_TLS"); + uint32_t SecondaryOp = (read32(Loc) & 0x000007FE) >> 1; // bits 21-30 + uint32_t DFormOp = getDFormOp(SecondaryOp); + write32(Loc, ((DFormOp << 26) | (read32(Loc) & 0x03FFFFFF))); + relocateOne(Loc + Offset, R_PPC64_TPREL16_LO, Val); + break; + } + default: + llvm_unreachable("unknown relocation for IE to LE"); + break; + } +} + RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const { switch (Type) { + case R_PPC64_GOT16: + case R_PPC64_GOT16_DS: + case R_PPC64_GOT16_HA: + case R_PPC64_GOT16_HI: + case R_PPC64_GOT16_LO: + case R_PPC64_GOT16_LO_DS: + return R_GOT_OFF; case R_PPC64_TOC16: case R_PPC64_TOC16_DS: case R_PPC64_TOC16_HA: @@ -224,6 +428,7 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S, return R_GOTREL; case R_PPC64_TOC: return R_PPC_TOC; + case R_PPC64_REL14: case R_PPC64_REL24: return R_PPC_CALL_PLT; case R_PPC64_REL16_LO: @@ -279,7 +484,7 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S, case R_PPC64_TLSLD: return R_TLSLD_HINT; case R_PPC64_TLS: - return R_HINT; + return R_TLSIE_HINT; default: return R_ABS; } @@ -308,16 +513,16 @@ void PPC64::writePltHeader(uint8_t *Buf) const { // 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); + int64_t GotPltOffset = In.GotPlt->getVA() - (In.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 { - int32_t Offset = PltHeaderSize + Index * PltEntrySize; - // bl __glink_PLTresolve - write32(Buf, 0x48000000 | ((-Offset) & 0x03FFFFFc)); + 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) { @@ -328,30 +533,36 @@ static std::pair<RelType, uint64_t> toAddr16Rel(RelType Type, uint64_t Val) { switch (Type) { // TOC biased relocation. + case R_PPC64_GOT16: case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSLD16: case R_PPC64_TOC16: return {R_PPC64_ADDR16, TocBiasedVal}; + case R_PPC64_GOT16_DS: case R_PPC64_TOC16_DS: case R_PPC64_GOT_TPREL16_DS: case R_PPC64_GOT_DTPREL16_DS: return {R_PPC64_ADDR16_DS, TocBiasedVal}; + case R_PPC64_GOT16_HA: 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, TocBiasedVal}; + case R_PPC64_GOT16_HI: 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, TocBiasedVal}; + case R_PPC64_GOT16_LO: case R_PPC64_GOT_TLSGD16_LO: case R_PPC64_GOT_TLSLD16_LO: case R_PPC64_TOC16_LO: return {R_PPC64_ADDR16_LO, TocBiasedVal}; + case R_PPC64_GOT16_LO_DS: case R_PPC64_TOC16_LO_DS: case R_PPC64_GOT_TPREL16_LO_DS: case R_PPC64_GOT_DTPREL16_LO_DS: @@ -386,9 +597,27 @@ static std::pair<RelType, uint64_t> toAddr16Rel(RelType Type, uint64_t Val) { } } +static bool isTocOptType(RelType Type) { + switch (Type) { + case R_PPC64_GOT16_HA: + case R_PPC64_GOT16_LO_DS: + case R_PPC64_TOC16_HA: + case R_PPC64_TOC16_LO_DS: + case R_PPC64_TOC16_LO: + return true; + default: + return false; + } +} + void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - // For a TOC-relative relocation, proceed in terms of the corresponding - // ADDR16 relocation type. + // We need to save the original relocation type to use in diagnostics, and + // use the original type to determine if we should toc-optimize the + // instructions being relocated. + RelType OriginalType = Type; + bool ShouldTocOptimize = isTocOptType(Type); + // For dynamic thread pointer relative, toc-relative, and got-indirect + // relocations, proceed in terms of the corresponding ADDR16 relocation type. std::tie(Type, Val) = toAddr16Rel(Type, Val); switch (Type) { @@ -401,18 +630,25 @@ void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { } case R_PPC64_ADDR16: case R_PPC64_TPREL16: - checkInt(Loc, Val, 16, Type); + checkInt(Loc, Val, 16, OriginalType); write16(Loc, Val); break; case R_PPC64_ADDR16_DS: - case R_PPC64_TPREL16_DS: - checkInt(Loc, Val, 16, Type); - write16(Loc, (read16(Loc) & 3) | (Val & ~3)); - break; + case R_PPC64_TPREL16_DS: { + checkInt(Loc, Val, 16, OriginalType); + // DQ-form instructions use bits 28-31 as part of the instruction encoding + // DS-form instructions only use bits 30-31. + uint16_t Mask = isDQFormInstruction(readInstrFromHalf16(Loc)) ? 0xF : 0x3; + checkAlignment(Loc, lo(Val), Mask + 1, OriginalType); + write16(Loc, (read16(Loc) & Mask) | lo(Val)); + } break; case R_PPC64_ADDR16_HA: case R_PPC64_REL16_HA: case R_PPC64_TPREL16_HA: - write16(Loc, ha(Val)); + if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0) + writeInstrFromHalf16(Loc, 0x60000000); + else + write16(Loc, ha(Val)); break; case R_PPC64_ADDR16_HI: case R_PPC64_REL16_HI: @@ -438,12 +674,40 @@ void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_PPC64_ADDR16_LO: case R_PPC64_REL16_LO: case R_PPC64_TPREL16_LO: + // When the high-adjusted part of a toc relocation evalutes to 0, it is + // changed into a nop. The lo part then needs to be updated to use the + // toc-pointer register r2, as the base register. + if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0) { + uint32_t Instr = readInstrFromHalf16(Loc); + if (isInstructionUpdateForm(Instr)) + error(getErrorLocation(Loc) + + "can't toc-optimize an update instruction: 0x" + + utohexstr(Instr)); + Instr = (Instr & 0xFFE00000) | 0x00020000; + writeInstrFromHalf16(Loc, Instr); + } write16(Loc, lo(Val)); break; case R_PPC64_ADDR16_LO_DS: - case R_PPC64_TPREL16_LO_DS: - write16(Loc, (read16(Loc) & 3) | (lo(Val) & ~3)); - break; + case R_PPC64_TPREL16_LO_DS: { + // DQ-form instructions use bits 28-31 as part of the instruction encoding + // DS-form instructions only use bits 30-31. + uint32_t Inst = readInstrFromHalf16(Loc); + uint16_t Mask = isDQFormInstruction(Inst) ? 0xF : 0x3; + checkAlignment(Loc, lo(Val), Mask + 1, OriginalType); + if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0) { + // When the high-adjusted part of a toc relocation evalutes to 0, it is + // changed into a nop. The lo part then needs to be updated to use the toc + // pointer register r2, as the base register. + if (isInstructionUpdateForm(Inst)) + error(getErrorLocation(Loc) + + "Can't toc-optimize an update instruction: 0x" + + Twine::utohexstr(Inst)); + Inst = (Inst & 0xFFE0000F) | 0x00020000; + writeInstrFromHalf16(Loc, Inst); + } + write16(Loc, (read16(Loc) & Mask) | lo(Val)); + } break; case R_PPC64_ADDR32: case R_PPC64_REL32: checkInt(Loc, Val, 32, Type); @@ -454,9 +718,17 @@ void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_PPC64_TOC: write64(Loc, Val); break; + case R_PPC64_REL14: { + uint32_t Mask = 0x0000FFFC; + checkInt(Loc, Val, 16, Type); + checkAlignment(Loc, Val, 4, Type); + write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask)); + break; + } case R_PPC64_REL24: { uint32_t Mask = 0x03FFFFFC; - checkInt(Loc, Val, 24, Type); + checkInt(Loc, Val, 26, Type); + checkAlignment(Loc, Val, 4, Type); write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask)); break; } @@ -470,9 +742,30 @@ void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { 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(); + if (Type != R_PPC64_REL14 && Type != R_PPC64_REL24) + return false; + + // If a function is in the Plt it needs to be called with a call-stub. + if (S.isInPlt()) + return true; + + // If a symbol is a weak undefined and we are compiling an executable + // it doesn't need a range-extending thunk since it can't be called. + if (S.isUndefWeak() && !Config->Shared) + return false; + + // If the offset exceeds the range of the branch type then it will need + // a range-extending thunk. + return !inBranchRange(Type, BranchAddr, S.getVA()); +} + +bool PPC64::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const { + int64_t Offset = Dst - Src; + if (Type == R_PPC64_REL14) + return isInt<16>(Offset); + if (Type == R_PPC64_REL24) + return isInt<26>(Offset); + llvm_unreachable("unsupported relocation type used in branch"); } RelExpr PPC64::adjustRelaxExpr(RelType Type, const uint8_t *Data, @@ -511,9 +804,8 @@ void PPC64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const { 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); + uint32_t InputRegister = (readInstrFromHalf16(Loc) & (0x1f << 16)); + writeInstrFromHalf16(Loc, 0xE8600000 | InputRegister); relocateOne(Loc, R_PPC64_GOT_TPREL16_LO_DS, Val); return; } @@ -526,6 +818,113 @@ void PPC64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const { } } +// The prologue for a split-stack function is expected to look roughly +// like this: +// .Lglobal_entry_point: +// # TOC pointer initalization. +// ... +// .Llocal_entry_point: +// # load the __private_ss member of the threads tcbhead. +// ld r0,-0x7000-64(r13) +// # subtract the functions stack size from the stack pointer. +// addis r12, r1, ha(-stack-frame size) +// addi r12, r12, l(-stack-frame size) +// # compare needed to actual and branch to allocate_more_stack if more +// # space is needed, otherwise fallthrough to 'normal' function body. +// cmpld cr7,r12,r0 +// blt- cr7, .Lallocate_more_stack +// +// -) The allocate_more_stack block might be placed after the split-stack +// prologue and the `blt-` replaced with a `bge+ .Lnormal_func_body` +// instead. +// -) If either the addis or addi is not needed due to the stack size being +// smaller then 32K or a multiple of 64K they will be replaced with a nop, +// but there will always be 2 instructions the linker can overwrite for the +// adjusted stack size. +// +// The linkers job here is to increase the stack size used in the addis/addi +// pair by split-stack-size-adjust. +// addis r12, r1, ha(-stack-frame size - split-stack-adjust-size) +// addi r12, r12, l(-stack-frame size - split-stack-adjust-size) +bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End, + uint8_t StOther) const { + // If the caller has a global entry point adjust the buffer past it. The start + // of the split-stack prologue will be at the local entry point. + Loc += getPPC64GlobalEntryToLocalEntryOffset(StOther); + + // At the very least we expect to see a load of some split-stack data from the + // tcb, and 2 instructions that calculate the ending stack address this + // function will require. If there is not enough room for at least 3 + // instructions it can't be a split-stack prologue. + if (Loc + 12 >= End) + return false; + + // First instruction must be `ld r0, -0x7000-64(r13)` + if (read32(Loc) != 0xe80d8fc0) + return false; + + int16_t HiImm = 0; + int16_t LoImm = 0; + // First instruction can be either an addis if the frame size is larger then + // 32K, or an addi if the size is less then 32K. + int32_t FirstInstr = read32(Loc + 4); + if (getPrimaryOpCode(FirstInstr) == 15) { + HiImm = FirstInstr & 0xFFFF; + } else if (getPrimaryOpCode(FirstInstr) == 14) { + LoImm = FirstInstr & 0xFFFF; + } else { + return false; + } + + // Second instruction is either an addi or a nop. If the first instruction was + // an addi then LoImm is set and the second instruction must be a nop. + uint32_t SecondInstr = read32(Loc + 8); + if (!LoImm && getPrimaryOpCode(SecondInstr) == 14) { + LoImm = SecondInstr & 0xFFFF; + } else if (SecondInstr != 0x60000000) { + return false; + } + + // The register operands of the first instruction should be the stack-pointer + // (r1) as the input (RA) and r12 as the output (RT). If the second + // instruction is not a nop, then it should use r12 as both input and output. + auto CheckRegOperands = [](uint32_t Instr, uint8_t ExpectedRT, + uint8_t ExpectedRA) { + return ((Instr & 0x3E00000) >> 21 == ExpectedRT) && + ((Instr & 0x1F0000) >> 16 == ExpectedRA); + }; + if (!CheckRegOperands(FirstInstr, 12, 1)) + return false; + if (SecondInstr != 0x60000000 && !CheckRegOperands(SecondInstr, 12, 12)) + return false; + + int32_t StackFrameSize = (HiImm * 65536) + LoImm; + // Check that the adjusted size doesn't overflow what we can represent with 2 + // instructions. + if (StackFrameSize < Config->SplitStackAdjustSize + INT32_MIN) { + error(getErrorLocation(Loc) + "split-stack prologue adjustment overflows"); + return false; + } + + int32_t AdjustedStackFrameSize = + StackFrameSize - Config->SplitStackAdjustSize; + + LoImm = AdjustedStackFrameSize & 0xFFFF; + HiImm = (AdjustedStackFrameSize + 0x8000) >> 16; + if (HiImm) { + write32(Loc + 4, 0x3D810000 | (uint16_t)HiImm); + // If the low immediate is zero the second instruction will be a nop. + SecondInstr = LoImm ? 0x398C0000 | (uint16_t)LoImm : 0x60000000; + write32(Loc + 8, SecondInstr); + } else { + // addi r12, r1, imm + write32(Loc + 4, (0x39810000) | (uint16_t)LoImm); + write32(Loc + 8, 0x60000000); + } + + return true; +} + TargetInfo *elf::getPPC64TargetInfo() { static PPC64 Target; return &Target; diff --git a/ELF/Arch/RISCV.cpp b/ELF/Arch/RISCV.cpp new file mode 100644 index 000000000000..461e8d35c3e6 --- /dev/null +++ b/ELF/Arch/RISCV.cpp @@ -0,0 +1,279 @@ +//===- RISCV.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 "Target.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 RISCV final : public TargetInfo { +public: + RISCV(); + 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; +}; + +} // end anonymous namespace + +RISCV::RISCV() { NoneRel = R_RISCV_NONE; } + +static uint32_t getEFlags(InputFile *F) { + if (Config->Is64) + return cast<ObjFile<ELF64LE>>(F)->getObj().getHeader()->e_flags; + return cast<ObjFile<ELF32LE>>(F)->getObj().getHeader()->e_flags; +} + +uint32_t RISCV::calcEFlags() const { + assert(!ObjectFiles.empty()); + + uint32_t Target = getEFlags(ObjectFiles.front()); + + for (InputFile *F : ObjectFiles) { + uint32_t EFlags = getEFlags(F); + if (EFlags & EF_RISCV_RVC) + Target |= EF_RISCV_RVC; + + if ((EFlags & EF_RISCV_FLOAT_ABI) != (Target & EF_RISCV_FLOAT_ABI)) + error(toString(F) + + ": cannot link object files with different floating-point ABI"); + + if ((EFlags & EF_RISCV_RVE) != (Target & EF_RISCV_RVE)) + error(toString(F) + + ": cannot link object files with different EF_RISCV_RVE"); + } + + return Target; +} + +RelExpr RISCV::getRelExpr(const RelType Type, const Symbol &S, + const uint8_t *Loc) const { + switch (Type) { + case R_RISCV_JAL: + case R_RISCV_BRANCH: + case R_RISCV_CALL: + case R_RISCV_PCREL_HI20: + case R_RISCV_RVC_BRANCH: + case R_RISCV_RVC_JUMP: + case R_RISCV_32_PCREL: + return R_PC; + case R_RISCV_PCREL_LO12_I: + case R_RISCV_PCREL_LO12_S: + return R_RISCV_PC_INDIRECT; + case R_RISCV_RELAX: + case R_RISCV_ALIGN: + return R_HINT; + default: + return R_ABS; + } +} + +// Extract bits V[Begin:End], where range is inclusive, and Begin must be < 63. +static uint32_t extractBits(uint64_t V, uint32_t Begin, uint32_t End) { + return (V & ((1ULL << (Begin + 1)) - 1)) >> End; +} + +void RISCV::relocateOne(uint8_t *Loc, const RelType Type, + const uint64_t Val) const { + switch (Type) { + case R_RISCV_32: + write32le(Loc, Val); + return; + case R_RISCV_64: + write64le(Loc, Val); + return; + + case R_RISCV_RVC_BRANCH: { + checkInt(Loc, static_cast<int64_t>(Val) >> 1, 8, Type); + checkAlignment(Loc, Val, 2, Type); + uint16_t Insn = read16le(Loc) & 0xE383; + uint16_t Imm8 = extractBits(Val, 8, 8) << 12; + uint16_t Imm4_3 = extractBits(Val, 4, 3) << 10; + uint16_t Imm7_6 = extractBits(Val, 7, 6) << 5; + uint16_t Imm2_1 = extractBits(Val, 2, 1) << 3; + uint16_t Imm5 = extractBits(Val, 5, 5) << 2; + Insn |= Imm8 | Imm4_3 | Imm7_6 | Imm2_1 | Imm5; + + write16le(Loc, Insn); + return; + } + + case R_RISCV_RVC_JUMP: { + checkInt(Loc, static_cast<int64_t>(Val) >> 1, 11, Type); + checkAlignment(Loc, Val, 2, Type); + uint16_t Insn = read16le(Loc) & 0xE003; + uint16_t Imm11 = extractBits(Val, 11, 11) << 12; + uint16_t Imm4 = extractBits(Val, 4, 4) << 11; + uint16_t Imm9_8 = extractBits(Val, 9, 8) << 9; + uint16_t Imm10 = extractBits(Val, 10, 10) << 8; + uint16_t Imm6 = extractBits(Val, 6, 6) << 7; + uint16_t Imm7 = extractBits(Val, 7, 7) << 6; + uint16_t Imm3_1 = extractBits(Val, 3, 1) << 3; + uint16_t Imm5 = extractBits(Val, 5, 5) << 2; + Insn |= Imm11 | Imm4 | Imm9_8 | Imm10 | Imm6 | Imm7 | Imm3_1 | Imm5; + + write16le(Loc, Insn); + return; + } + + case R_RISCV_RVC_LUI: { + int32_t Imm = ((Val + 0x800) >> 12); + checkUInt(Loc, Imm, 6, Type); + if (Imm == 0) { // `c.lui rd, 0` is illegal, convert to `c.li rd, 0` + write16le(Loc, (read16le(Loc) & 0x0F83) | 0x4000); + } else { + uint16_t Imm17 = extractBits(Val + 0x800, 17, 17) << 12; + uint16_t Imm16_12 = extractBits(Val + 0x800, 16, 12) << 2; + write16le(Loc, (read16le(Loc) & 0xEF83) | Imm17 | Imm16_12); + } + return; + } + + case R_RISCV_JAL: { + checkInt(Loc, static_cast<int64_t>(Val) >> 1, 20, Type); + checkAlignment(Loc, Val, 2, Type); + + uint32_t Insn = read32le(Loc) & 0xFFF; + uint32_t Imm20 = extractBits(Val, 20, 20) << 31; + uint32_t Imm10_1 = extractBits(Val, 10, 1) << 21; + uint32_t Imm11 = extractBits(Val, 11, 11) << 20; + uint32_t Imm19_12 = extractBits(Val, 19, 12) << 12; + Insn |= Imm20 | Imm10_1 | Imm11 | Imm19_12; + + write32le(Loc, Insn); + return; + } + + case R_RISCV_BRANCH: { + checkInt(Loc, static_cast<int64_t>(Val) >> 1, 12, Type); + checkAlignment(Loc, Val, 2, Type); + + uint32_t Insn = read32le(Loc) & 0x1FFF07F; + uint32_t Imm12 = extractBits(Val, 12, 12) << 31; + uint32_t Imm10_5 = extractBits(Val, 10, 5) << 25; + uint32_t Imm4_1 = extractBits(Val, 4, 1) << 8; + uint32_t Imm11 = extractBits(Val, 11, 11) << 7; + Insn |= Imm12 | Imm10_5 | Imm4_1 | Imm11; + + write32le(Loc, Insn); + return; + } + + // auipc + jalr pair + case R_RISCV_CALL: { + checkInt(Loc, Val, 32, Type); + if (isInt<32>(Val)) { + relocateOne(Loc, R_RISCV_PCREL_HI20, Val); + relocateOne(Loc + 4, R_RISCV_PCREL_LO12_I, Val); + } + return; + } + + case R_RISCV_PCREL_HI20: + case R_RISCV_HI20: { + checkInt(Loc, Val, 32, Type); + uint32_t Hi = Val + 0x800; + write32le(Loc, (read32le(Loc) & 0xFFF) | (Hi & 0xFFFFF000)); + return; + } + + case R_RISCV_PCREL_LO12_I: + case R_RISCV_LO12_I: { + checkInt(Loc, Val, 32, Type); + uint32_t Hi = Val + 0x800; + uint32_t Lo = Val - (Hi & 0xFFFFF000); + write32le(Loc, (read32le(Loc) & 0xFFFFF) | ((Lo & 0xFFF) << 20)); + return; + } + + case R_RISCV_PCREL_LO12_S: + case R_RISCV_LO12_S: { + checkInt(Loc, Val, 32, Type); + uint32_t Hi = Val + 0x800; + uint32_t Lo = Val - (Hi & 0xFFFFF000); + uint32_t Imm11_5 = extractBits(Lo, 11, 5) << 25; + uint32_t Imm4_0 = extractBits(Lo, 4, 0) << 7; + write32le(Loc, (read32le(Loc) & 0x1FFF07F) | Imm11_5 | Imm4_0); + return; + } + + case R_RISCV_ADD8: + *Loc += Val; + return; + case R_RISCV_ADD16: + write16le(Loc, read16le(Loc) + Val); + return; + case R_RISCV_ADD32: + write32le(Loc, read32le(Loc) + Val); + return; + case R_RISCV_ADD64: + write64le(Loc, read64le(Loc) + Val); + return; + case R_RISCV_SUB6: + *Loc = (*Loc & 0xc0) | (((*Loc & 0x3f) - Val) & 0x3f); + return; + case R_RISCV_SUB8: + *Loc -= Val; + return; + case R_RISCV_SUB16: + write16le(Loc, read16le(Loc) - Val); + return; + case R_RISCV_SUB32: + write32le(Loc, read32le(Loc) - Val); + return; + case R_RISCV_SUB64: + write64le(Loc, read64le(Loc) - Val); + return; + case R_RISCV_SET6: + *Loc = (*Loc & 0xc0) | (Val & 0x3f); + return; + case R_RISCV_SET8: + *Loc = Val; + return; + case R_RISCV_SET16: + write16le(Loc, Val); + return; + case R_RISCV_SET32: + case R_RISCV_32_PCREL: + write32le(Loc, Val); + return; + + case R_RISCV_ALIGN: + case R_RISCV_RELAX: + return; // Ignored (for now) + case R_RISCV_NONE: + return; // Do nothing + + // These are handled by the dynamic linker + case R_RISCV_RELATIVE: + case R_RISCV_COPY: + case R_RISCV_JUMP_SLOT: + // GP-relative relocations are only produced after relaxation, which + // we don't support for now + case R_RISCV_GPREL_I: + case R_RISCV_GPREL_S: + default: + error(getErrorLocation(Loc) + + "unimplemented relocation: " + toString(Type)); + return; + } +} + +TargetInfo *elf::getRISCVTargetInfo() { + static RISCV Target; + return &Target; +} diff --git a/ELF/Arch/SPARCV9.cpp b/ELF/Arch/SPARCV9.cpp index 36f5c836930e..831aa2028e7f 100644 --- a/ELF/Arch/SPARCV9.cpp +++ b/ELF/Arch/SPARCV9.cpp @@ -35,6 +35,7 @@ public: SPARCV9::SPARCV9() { CopyRel = R_SPARC_COPY; GotRel = R_SPARC_GLOB_DAT; + NoneRel = R_SPARC_NONE; PltRel = R_SPARC_JMP_SLOT; RelativeRel = R_SPARC_RELATIVE; GotEntrySize = 8; diff --git a/ELF/Arch/X86.cpp b/ELF/Arch/X86.cpp index 19a0b6017f1a..e910375d2fc7 100644 --- a/ELF/Arch/X86.cpp +++ b/ELF/Arch/X86.cpp @@ -48,6 +48,7 @@ public: X86::X86() { CopyRel = R_386_COPY; GotRel = R_386_GLOB_DAT; + NoneRel = R_386_NONE; PltRel = R_386_JUMP_SLOT; IRelativeRel = R_386_IRELATIVE; RelativeRel = R_386_RELATIVE; @@ -59,7 +60,11 @@ X86::X86() { PltEntrySize = 16; PltHeaderSize = 16; TlsGdRelaxSkip = 2; - TrapInstr = 0xcccccccc; // 0xcc = INT3 + TrapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 + + // Align to the non-PAE large page size (known as a superpage or huge page). + // FreeBSD automatically promotes large, superpage-aligned allocations. + DefaultImageBase = 0x400000; } static bool hasBaseReg(uint8_t ModRM) { return (ModRM & 0xc7) != 0x5; } @@ -152,7 +157,7 @@ RelExpr X86::adjustRelaxExpr(RelType Type, const uint8_t *Data, } void X86::writeGotPltHeader(uint8_t *Buf) const { - write32le(Buf, InX::Dynamic->getVA()); + write32le(Buf, In.Dynamic->getVA()); } void X86::writeGotPlt(uint8_t *Buf, const Symbol &S) const { @@ -183,8 +188,8 @@ void X86::writePltHeader(uint8_t *Buf) const { }; memcpy(Buf, V, sizeof(V)); - uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize(); - uint32_t GotPlt = InX::GotPlt->getVA() - Ebx; + uint32_t Ebx = In.Got->getVA() + In.Got->getSize(); + uint32_t GotPlt = In.GotPlt->getVA() - Ebx; write32le(Buf + 2, GotPlt + 4); write32le(Buf + 8, GotPlt + 8); return; @@ -196,7 +201,7 @@ void X86::writePltHeader(uint8_t *Buf) const { 0x90, 0x90, 0x90, 0x90, // nop }; memcpy(Buf, PltData, sizeof(PltData)); - uint32_t GotPlt = InX::GotPlt->getVA(); + uint32_t GotPlt = In.GotPlt->getVA(); write32le(Buf + 2, GotPlt + 4); write32le(Buf + 8, GotPlt + 8); } @@ -213,7 +218,7 @@ void X86::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, if (Config->Pic) { // jmp *foo@GOT(%ebx) - uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize(); + uint32_t Ebx = In.Got->getVA() + In.Got->getSize(); Buf[1] = 0xa3; write32le(Buf + 2, GotPltEntryAddr - Ebx); } else { @@ -447,8 +452,8 @@ void RetpolinePic::writePltHeader(uint8_t *Buf) const { }; memcpy(Buf, Insn, sizeof(Insn)); - uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize(); - uint32_t GotPlt = InX::GotPlt->getVA() - Ebx; + uint32_t Ebx = In.Got->getVA() + In.Got->getSize(); + uint32_t GotPlt = In.GotPlt->getVA() - Ebx; write32le(Buf + 2, GotPlt + 4); write32le(Buf + 9, GotPlt + 8); } @@ -467,7 +472,7 @@ void RetpolinePic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, }; memcpy(Buf, Insn, sizeof(Insn)); - uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize(); + uint32_t Ebx = In.Got->getVA() + In.Got->getSize(); unsigned Off = getPltEntryOffset(Index); write32le(Buf + 3, GotPltEntryAddr - Ebx); write32le(Buf + 8, -Off - 12 + 32); @@ -506,7 +511,7 @@ void RetpolineNoPic::writePltHeader(uint8_t *Buf) const { }; memcpy(Buf, Insn, sizeof(Insn)); - uint32_t GotPlt = InX::GotPlt->getVA(); + uint32_t GotPlt = In.GotPlt->getVA(); write32le(Buf + 2, GotPlt + 4); write32le(Buf + 8, GotPlt + 8); } diff --git a/ELF/Arch/X86_64.cpp b/ELF/Arch/X86_64.cpp index d4bdb3730c58..06314155dcc9 100644 --- a/ELF/Arch/X86_64.cpp +++ b/ELF/Arch/X86_64.cpp @@ -43,8 +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; + bool adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End, + uint8_t StOther) const override; private: void relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op, @@ -55,6 +55,7 @@ private: template <class ELFT> X86_64<ELFT>::X86_64() { CopyRel = R_X86_64_COPY; GotRel = R_X86_64_GLOB_DAT; + NoneRel = R_X86_64_NONE; PltRel = R_X86_64_JUMP_SLOT; RelativeRel = R_X86_64_RELATIVE; IRelativeRel = R_X86_64_IRELATIVE; @@ -66,7 +67,7 @@ template <class ELFT> X86_64<ELFT>::X86_64() { PltEntrySize = 16; PltHeaderSize = 16; TlsGdRelaxSkip = 2; - TrapInstr = 0xcccccccc; // 0xcc = INT3 + TrapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 // Align to the large page size (known as a superpage or huge page). // FreeBSD automatically promotes large, superpage-aligned allocations. @@ -124,7 +125,7 @@ template <class ELFT> void X86_64<ELFT>::writeGotPltHeader(uint8_t *Buf) const { // required, but it is documented in the psabi and the glibc dynamic linker // seems to use it (note that this is relevant for linking ld.so, not any // other program). - write64le(Buf, InX::Dynamic->getVA()); + write64le(Buf, In.Dynamic->getVA()); } template <class ELFT> @@ -140,8 +141,8 @@ template <class ELFT> void X86_64<ELFT>::writePltHeader(uint8_t *Buf) const { 0x0f, 0x1f, 0x40, 0x00, // nop }; memcpy(Buf, PltData, sizeof(PltData)); - uint64_t GotPlt = InX::GotPlt->getVA(); - uint64_t Plt = InX::Plt->getVA(); + uint64_t GotPlt = In.GotPlt->getVA(); + uint64_t Plt = In.Plt->getVA(); write32le(Buf + 2, GotPlt - Plt + 2); // GOTPLT+8 write32le(Buf + 8, GotPlt - Plt + 4); // GOTPLT+16 } @@ -481,23 +482,27 @@ namespace { // 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 { + uint8_t *End, + uint8_t StOther) const { + if (Loc + 8 >= End) + return false; + // 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) { + if (memcmp(Loc, "\x64\x48\x3b\x24\x25", 5) == 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); + // Adjust "lea X(%rsp),%rYY" to lea "(X - 0x4000)(%rsp),%rYY" where rYY could + // be r10 or r11. The lea instruction feeds a subsequent compare which checks + // if there is X available stack space. Making X larger effectively reserves + // that much additional space. The stack grows downward so subtract the value. + if (memcmp(Loc, "\x4c\x8d\x94\x24", 4) == 0 || + memcmp(Loc, "\x4c\x8d\x9c\x24", 4) == 0) { + // The offset bytes are encoded four bytes after the start of the + // instruction. + write32le(Loc + 4, read32le(Loc + 4) - 0x4000); return true; } return false; @@ -505,7 +510,8 @@ bool X86_64<ELF64LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc, template <> bool X86_64<ELF32LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc, - uint8_t *End) const { + uint8_t *End, + uint8_t StOther) const { llvm_unreachable("Target doesn't support split stacks."); } @@ -566,8 +572,8 @@ template <class ELFT> void Retpoline<ELFT>::writePltHeader(uint8_t *Buf) const { }; memcpy(Buf, Insn, sizeof(Insn)); - uint64_t GotPlt = InX::GotPlt->getVA(); - uint64_t Plt = InX::Plt->getVA(); + uint64_t GotPlt = In.GotPlt->getVA(); + uint64_t Plt = In.Plt->getVA(); write32le(Buf + 2, GotPlt - Plt - 6 + 8); write32le(Buf + 9, GotPlt - Plt - 13 + 16); } @@ -586,7 +592,7 @@ void Retpoline<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, }; memcpy(Buf, Insn, sizeof(Insn)); - uint64_t Off = TargetInfo::getPltEntryOffset(Index); + uint64_t Off = getPltEntryOffset(Index); write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7); write32le(Buf + 8, -Off - 12 + 32); @@ -629,7 +635,7 @@ void RetpolineZNow<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, memcpy(Buf, Insn, sizeof(Insn)); write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7); - write32le(Buf + 8, -TargetInfo::getPltEntryOffset(Index) - 12); + write32le(Buf + 8, -getPltEntryOffset(Index) - 12); } template <class ELFT> static TargetInfo *getTargetInfo() { diff --git a/ELF/CMakeLists.txt b/ELF/CMakeLists.txt index fb2f53a72025..a1c23b0d49ac 100644 --- a/ELF/CMakeLists.txt +++ b/ELF/CMakeLists.txt @@ -15,17 +15,19 @@ add_lld_library(lldELF Arch/Hexagon.cpp Arch/Mips.cpp Arch/MipsArchTree.cpp + Arch/MSP430.cpp Arch/PPC.cpp Arch/PPC64.cpp + Arch/RISCV.cpp Arch/SPARCV9.cpp Arch/X86.cpp Arch/X86_64.cpp CallGraphSort.cpp + DWARF.cpp Driver.cpp DriverUtils.cpp EhFrame.cpp Filesystem.cpp - GdbIndex.cpp ICF.cpp InputFiles.cpp InputSection.cpp diff --git a/ELF/CallGraphSort.cpp b/ELF/CallGraphSort.cpp index 33ac159a6e26..2a7d78664b8e 100644 --- a/ELF/CallGraphSort.cpp +++ b/ELF/CallGraphSort.cpp @@ -57,10 +57,7 @@ struct Edge { }; struct Cluster { - Cluster(int Sec, size_t S) { - Sections.push_back(Sec); - Size = S; - } + Cluster(int Sec, size_t S) : Sections{Sec}, Size(S) {} double getDensity() const { if (Size == 0) @@ -72,7 +69,7 @@ struct Cluster { size_t Size = 0; uint64_t Weight = 0; uint64_t InitialWeight = 0; - std::vector<Edge> Preds; + Edge BestPred = {-1, 0}; }; class CallGraphSort { @@ -96,12 +93,14 @@ constexpr int MAX_DENSITY_DEGRADATION = 8; constexpr uint64_t MAX_CLUSTER_SIZE = 1024 * 1024; } // end anonymous namespace +typedef std::pair<const InputSectionBase *, const InputSectionBase *> + SectionPair; + // 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; + MapVector<SectionPair, uint64_t> &Profile = Config->CallGraphProfile; DenseMap<const InputSectionBase *, int> SecToCluster; auto GetOrCreateNode = [&](const InputSectionBase *IS) -> int { @@ -114,7 +113,7 @@ CallGraphSort::CallGraphSort() { }; // Create the graph. - for (const auto &C : Profile) { + for (std::pair<SectionPair, uint64_t> &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; @@ -136,8 +135,12 @@ CallGraphSort::CallGraphSort() { if (From == To) continue; - // Add an edge - Clusters[To].Preds.push_back({From, Weight}); + // Remember the best edge. + Cluster &ToC = Clusters[To]; + if (ToC.BestPred.From == -1 || ToC.BestPred.Weight < Weight) { + ToC.BestPred.From = From; + ToC.BestPred.Weight = Weight; + } } for (Cluster &C : Clusters) C.InitialWeight = C.Weight; @@ -146,9 +149,7 @@ CallGraphSort::CallGraphSort() { // 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; + return NewDensity < A.getDensity() / MAX_DENSITY_DEGRADATION; } static void mergeClusters(Cluster &Into, Cluster &From) { @@ -167,9 +168,9 @@ 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]; + for (size_t I = 0; I < Clusters.size(); ++I) { + SortedSecs[I] = I; + SecToCluster[I] = &Clusters[I]; } std::stable_sort(SortedSecs.begin(), SortedSecs.end(), [&](int A, int B) { @@ -181,21 +182,11 @@ void CallGraphSort::groupClusters() { // 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) + // Don't consider merging if the edge is unlikely. + if (C.BestPred.From == -1 || C.BestPred.Weight * 10 <= C.InitialWeight) continue; - Cluster *PredC = SecToCluster[BestPred]; + Cluster *PredC = SecToCluster[C.BestPred.From]; if (PredC == &C) continue; @@ -229,7 +220,7 @@ DenseMap<const InputSectionBase *, int> CallGraphSort::run() { groupClusters(); // Generate order. - llvm::DenseMap<const InputSectionBase *, int> OrderMap; + DenseMap<const InputSectionBase *, int> OrderMap; ssize_t CurOrder = 1; for (const Cluster &C : Clusters) diff --git a/ELF/Config.h b/ELF/Config.h index 622324c13e2d..8fb760e592eb 100644 --- a/ELF/Config.h +++ b/ELF/Config.h @@ -47,7 +47,7 @@ enum class ICFLevel { None, Safe, All }; enum class StripPolicy { None, All, Debug }; // For --unresolved-symbols. -enum class UnresolvedPolicy { ReportError, Warn, Ignore, IgnoreAll }; +enum class UnresolvedPolicy { ReportError, Warn, Ignore }; // For --orphan-handling. enum class OrphanHandlingPolicy { Place, Warn, Error }; @@ -127,6 +127,7 @@ struct Configuration { bool AsNeeded = false; bool Bsymbolic; bool BsymbolicFunctions; + bool CallGraphProfileSort; bool CheckSections; bool CompressDebugSections; bool Cref; @@ -134,11 +135,13 @@ struct Configuration { bool Demangle = true; bool DisableVerify; bool EhFrameHdr; + bool EmitLLVM; bool EmitRelocs; bool EnableNewDtags; bool ExecuteOnly; bool ExportDynamic; bool FixCortexA53Errata843419; + bool FormatBinary = false; bool GcSections; bool GdbIndex; bool GnuHash = false; @@ -170,19 +173,24 @@ struct Configuration { bool Trace; bool ThinLTOEmitImportsFiles; bool ThinLTOIndexOnly; + bool TocOptimize; bool UndefinedVersion; bool UseAndroidRelrTags = false; bool WarnBackrefs; bool WarnCommon; + bool WarnIfuncTextrel; bool WarnMissingEntry; bool WarnSymbolOrdering; bool WriteAddends; bool ZCombreloc; bool ZCopyreloc; bool ZExecstack; + bool ZGlobal; bool ZHazardplt; bool ZInitfirst; + bool ZInterpose; bool ZKeepTextSectionPrefix; + bool ZNodefaultlib; bool ZNodelete; bool ZNodlopen; bool ZNow; @@ -212,6 +220,7 @@ struct Configuration { unsigned LTOO; unsigned Optimize; unsigned ThinLTOJobs; + int32_t SplitStackAdjustSize; // The following config options do not directly correspond to any // particualr command line options. diff --git a/ELF/GdbIndex.cpp b/ELF/DWARF.cpp index 85449a200647..17e1a4d600eb 100644 --- a/ELF/GdbIndex.cpp +++ b/ELF/DWARF.cpp @@ -1,4 +1,4 @@ -//===- GdbIndex.cpp -------------------------------------------------------===// +//===- DWARF.cpp ----------------------------------------------------------===// // // The LLVM Linker // @@ -14,8 +14,9 @@ // //===----------------------------------------------------------------------===// -#include "GdbIndex.h" +#include "DWARF.h" #include "Symbols.h" +#include "Target.h" #include "lld/Common/Memory.h" #include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" #include "llvm/Object/ELFObjectFile.h" @@ -29,24 +30,28 @@ template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *Obj) { for (InputSectionBase *Sec : Obj->getSections()) { if (!Sec) continue; - if (LLDDWARFSection *M = StringSwitch<LLDDWARFSection *>(Sec->Name) - .Case(".debug_info", &InfoSection) - .Case(".debug_ranges", &RangeSection) - .Case(".debug_line", &LineSection) - .Default(nullptr)) { - Sec->maybeDecompress(); - M->Data = toStringRef(Sec->Data); + + if (LLDDWARFSection *M = + StringSwitch<LLDDWARFSection *>(Sec->Name) + .Case(".debug_addr", &AddrSection) + .Case(".debug_gnu_pubnames", &GnuPubNamesSection) + .Case(".debug_gnu_pubtypes", &GnuPubTypesSection) + .Case(".debug_info", &InfoSection) + .Case(".debug_ranges", &RangeSection) + .Case(".debug_rnglists", &RngListsSection) + .Case(".debug_line", &LineSection) + .Default(nullptr)) { + M->Data = toStringRef(Sec->data()); M->Sec = Sec; continue; } + if (Sec->Name == ".debug_abbrev") - AbbrevSection = toStringRef(Sec->Data); - else if (Sec->Name == ".debug_gnu_pubnames") - GnuPubNamesSection = toStringRef(Sec->Data); - else if (Sec->Name == ".debug_gnu_pubtypes") - GnuPubTypesSection = toStringRef(Sec->Data); + AbbrevSection = toStringRef(Sec->data()); else if (Sec->Name == ".debug_str") - StrSection = toStringRef(Sec->Data); + StrSection = toStringRef(Sec->data()); + else if (Sec->Name == ".debug_line_str") + LineStringSection = toStringRef(Sec->data()); } } @@ -73,7 +78,10 @@ LLDDwarfObj<ELFT>::findAux(const InputSectionBase &Sec, uint64_t Pos, // Broken debug info can point to a non-Defined symbol. auto *DR = dyn_cast<Defined>(&File->getRelocTargetSym(Rel)); if (!DR) { - error("unsupported relocation target while parsing debug info"); + RelType Type = Rel.getType(Config->IsMips64EL); + if (Type != Target->NoneRel) + error(toString(File) + ": relocation " + lld::toString(Type) + " at 0x" + + llvm::utohexstr(Rel.r_offset) + " has unsupported target"); return None; } uint64_t Val = DR->Value + getAddend<ELFT>(Rel); diff --git a/ELF/GdbIndex.h b/ELF/DWARF.h index eba1ba22f879..8ecf02c77fb4 100644 --- a/ELF/GdbIndex.h +++ b/ELF/DWARF.h @@ -1,4 +1,4 @@ -//===- GdbIndex.h --------------------------------------------*- C++ -*-===// +//===- DWARF.h -----------------------------------------------*- C++ -*-===// // // The LLVM Linker // @@ -7,10 +7,11 @@ // //===-------------------------------------------------------------------===// -#ifndef LLD_ELF_GDB_INDEX_H -#define LLD_ELF_GDB_INDEX_H +#ifndef LLD_ELF_DWARF_H +#define LLD_ELF_DWARF_H #include "InputFiles.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/Object/ELF.h" @@ -24,44 +25,66 @@ struct LLDDWARFSection final : public llvm::DWARFSection { }; template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject { - LLDDWARFSection InfoSection; - LLDDWARFSection RangeSection; - LLDDWARFSection LineSection; - StringRef AbbrevSection; - StringRef GnuPubNamesSection; - StringRef GnuPubTypesSection; - StringRef StrSection; - - template <class RelTy> - llvm::Optional<llvm::RelocAddrEntry> findAux(const InputSectionBase &Sec, - uint64_t Pos, - ArrayRef<RelTy> Rels) const; - public: explicit LLDDwarfObj(ObjFile<ELFT> *Obj); - const llvm::DWARFSection &getInfoSection() const override { - return InfoSection; + + void forEachInfoSections( + llvm::function_ref<void(const llvm::DWARFSection &)> F) const override { + F(InfoSection); } + const llvm::DWARFSection &getRangeSection() const override { return RangeSection; } + + const llvm::DWARFSection &getRnglistsSection() const override { + return RngListsSection; + } + const llvm::DWARFSection &getLineSection() const override { return LineSection; } - StringRef getFileName() const override { return ""; } - StringRef getAbbrevSection() const override { return AbbrevSection; } - StringRef getStringSection() const override { return StrSection; } - StringRef getGnuPubNamesSection() const override { + + const llvm::DWARFSection &getAddrSection() const override { + return AddrSection; + } + + const llvm::DWARFSection &getGnuPubNamesSection() const override { return GnuPubNamesSection; } - StringRef getGnuPubTypesSection() const override { + + const llvm::DWARFSection &getGnuPubTypesSection() const override { return GnuPubTypesSection; } + + StringRef getFileName() const override { return ""; } + StringRef getAbbrevSection() const override { return AbbrevSection; } + StringRef getStringSection() const override { return StrSection; } + StringRef getLineStringSection() const override { return LineStringSection; } + bool isLittleEndian() const override { return ELFT::TargetEndianness == llvm::support::little; } + llvm::Optional<llvm::RelocAddrEntry> find(const llvm::DWARFSection &Sec, uint64_t Pos) const override; + +private: + template <class RelTy> + llvm::Optional<llvm::RelocAddrEntry> findAux(const InputSectionBase &Sec, + uint64_t Pos, + ArrayRef<RelTy> Rels) const; + + LLDDWARFSection GnuPubNamesSection; + LLDDWARFSection GnuPubTypesSection; + LLDDWARFSection InfoSection; + LLDDWARFSection RangeSection; + LLDDWARFSection RngListsSection; + LLDDWARFSection LineSection; + LLDDWARFSection AddrSection; + StringRef AbbrevSection; + StringRef StrSection; + StringRef LineStringSection; }; } // namespace elf diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp index 693dba64ab5a..13b6119e2dc9 100644 --- a/ELF/Driver.cpp +++ b/ELF/Driver.cpp @@ -63,6 +63,7 @@ using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; using namespace llvm::sys; +using namespace llvm::support; using namespace lld; using namespace lld::elf; @@ -74,7 +75,7 @@ static void setConfigs(opt::InputArgList &Args); bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly, raw_ostream &Error) { - errorHandler().LogName = sys::path::filename(Args[0]); + errorHandler().LogName = args::getFilenameWithoutExe(Args[0]); errorHandler().ErrorLimitExceededMsg = "too many errors emitted, stopping now (use " "-error-limit=0 to see all errors)"; @@ -84,7 +85,6 @@ bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly, InputSections.clear(); OutputSections.clear(); - Tar = nullptr; BinaryFiles.clear(); BitcodeFiles.clear(); ObjectFiles.clear(); @@ -94,6 +94,10 @@ bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly, Driver = make<LinkerDriver>(); Script = make<LinkerScript>(); Symtab = make<SymbolTable>(); + + Tar = nullptr; + memset(&In, 0, sizeof(In)); + Config->ProgName = Args[0]; Driver->main(Args); @@ -125,9 +129,11 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) { .Case("elf32_x86_64", {ELF32LEKind, EM_X86_64}) .Cases("elf32btsmip", "elf32btsmipn32", {ELF32BEKind, EM_MIPS}) .Cases("elf32ltsmip", "elf32ltsmipn32", {ELF32LEKind, EM_MIPS}) + .Case("elf32lriscv", {ELF32LEKind, EM_RISCV}) .Case("elf32ppc", {ELF32BEKind, EM_PPC}) .Case("elf64btsmip", {ELF64BEKind, EM_MIPS}) .Case("elf64ltsmip", {ELF64LEKind, EM_MIPS}) + .Case("elf64lriscv", {ELF64LEKind, EM_RISCV}) .Case("elf64ppc", {ELF64BEKind, EM_PPC64}) .Case("elf64lppc", {ELF64LEKind, EM_PPC64}) .Cases("elf_amd64", "elf_x86_64", {ELF64LEKind, EM_X86_64}) @@ -183,7 +189,7 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) { return; MemoryBufferRef MBRef = *Buffer; - if (InBinary) { + if (Config->FormatBinary) { Files.push_back(make<BinaryFile>(MBRef)); return; } @@ -218,7 +224,7 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) { return; } case file_magic::elf_shared_object: - if (Config->Relocatable) { + if (Config->Static || Config->Relocatable) { error("attempted static link of dynamic object " + Path); return; } @@ -269,14 +275,17 @@ static void initLLVM() { // Some command line options or some combinations of them are not allowed. // This function checks for such errors. -static void checkOptions(opt::InputArgList &Args) { +static void checkOptions() { // The MIPS ABI as of 2016 does not support the GNU-style symbol lookup // table which is a relatively new feature. if (Config->EMachine == EM_MIPS && Config->GnuHash) - error("the .gnu.hash section is not compatible with the MIPS target."); + error("the .gnu.hash section is not compatible with the MIPS target"); if (Config->FixCortexA53Errata843419 && Config->EMachine != EM_AARCH64) - error("--fix-cortex-a53-843419 is only supported on AArch64 targets."); + error("--fix-cortex-a53-843419 is only supported on AArch64 targets"); + + if (Config->TocOptimize && Config->EMachine != EM_PPC64) + error("--toc-optimize is only supported on the PowerPC64 target"); if (Config->Pie && Config->Shared) error("-shared and -pie may not be used together"); @@ -336,12 +345,13 @@ static bool getZFlag(opt::InputArgList &Args, StringRef K1, StringRef K2, return Default; } -static bool isKnown(StringRef S) { +static bool isKnownZFlag(StringRef S) { return S == "combreloc" || S == "copyreloc" || S == "defs" || - S == "execstack" || S == "hazardplt" || S == "initfirst" || + S == "execstack" || S == "global" || S == "hazardplt" || + S == "initfirst" || S == "interpose" || S == "keep-text-section-prefix" || S == "lazy" || S == "muldefs" || - S == "nocombreloc" || S == "nocopyreloc" || S == "nodelete" || - S == "nodlopen" || S == "noexecstack" || + S == "nocombreloc" || S == "nocopyreloc" || S == "nodefaultlib" || + 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" || @@ -351,7 +361,7 @@ static bool isKnown(StringRef S) { // 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())) + if (!isKnownZFlag(Arg->getValue())) error("unknown -z value: " + StringRef(Arg->getValue())); } @@ -386,31 +396,30 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr) { if (Args.hasArg(OPT_v) || Args.hasArg(OPT_version)) message(getLLDVersion() + " (compatible with GNU linkers)"); - // The behavior of -v or --version is a bit strange, but this is - // needed for compatibility with GNU linkers. - if (Args.hasArg(OPT_v) && !Args.hasArg(OPT_INPUT)) - return; - if (Args.hasArg(OPT_version)) - return; - 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. Expected<std::unique_ptr<TarWriter>> ErrOrWriter = TarWriter::create(Path, path::stem(Path)); if (ErrOrWriter) { - Tar = ErrOrWriter->get(); + Tar = std::move(*ErrOrWriter); Tar->append("response.txt", createResponseFile(Args)); Tar->append("version.txt", getLLDVersion() + "\n"); - make<std::unique_ptr<TarWriter>>(std::move(*ErrOrWriter)); } else { - error(Twine("--reproduce: failed to open ") + Path + ": " + - toString(ErrOrWriter.takeError())); + error("--reproduce: " + toString(ErrOrWriter.takeError())); } } readConfigs(Args); checkZOptions(Args); + + // The behavior of -v or --version is a bit strange, but this is + // needed for compatibility with GNU linkers. + if (Args.hasArg(OPT_v) && !Args.hasArg(OPT_INPUT)) + return; + if (Args.hasArg(OPT_version)) + return; + initLLVM(); createFiles(Args); if (errorCount()) @@ -418,7 +427,7 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr) { inferMachineType(); setConfigs(Args); - checkOptions(Args); + checkOptions(); if (errorCount()) return; @@ -448,9 +457,6 @@ static std::string getRpath(opt::InputArgList &Args) { // Determines what we should do if there are remaining unresolved // symbols after the name resolution. static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &Args) { - if (Args.hasArg(OPT_relocatable)) - return UnresolvedPolicy::IgnoreAll; - UnresolvedPolicy ErrorOrWarn = Args.hasFlag(OPT_error_unresolved_symbols, OPT_warn_unresolved_symbols, true) ? UnresolvedPolicy::ReportError @@ -497,14 +503,11 @@ static Target2Policy getTarget2(opt::InputArgList &Args) { } static bool isOutputFormatBinary(opt::InputArgList &Args) { - if (auto *Arg = Args.getLastArg(OPT_oformat)) { - StringRef S = Arg->getValue(); - if (S == "binary") - return true; - if (S.startswith("elf")) - return false; + StringRef S = Args.getLastArgValue(OPT_oformat, "elf"); + if (S == "binary") + return true; + if (!S.startswith("elf")) error("unknown --oformat value: " + S); - } return false; } @@ -645,38 +648,56 @@ static std::pair<bool, bool> getPackDynRelocs(opt::InputArgList &Args) { static void readCallGraph(MemoryBufferRef MB) { // Build a map from symbol name to section - DenseMap<StringRef, const Symbol *> SymbolNameToSymbol; + DenseMap<StringRef, Symbol *> Map; for (InputFile *File : ObjectFiles) for (Symbol *Sym : File->getSymbols()) - SymbolNameToSymbol[Sym->getName()] = Sym; + Map[Sym->getName()] = Sym; + + auto FindSection = [&](StringRef Name) -> InputSectionBase * { + Symbol *Sym = Map.lookup(Name); + if (!Sym) { + if (Config->WarnSymbolOrdering) + warn(MB.getBufferIdentifier() + ": no such symbol: " + Name); + return nullptr; + } + maybeWarnUnorderableSymbol(Sym); + + if (Defined *DR = dyn_cast_or_null<Defined>(Sym)) + return dyn_cast_or_null<InputSectionBase>(DR->Section); + return nullptr; + }; - for (StringRef L : args::getLines(MB)) { + for (StringRef Line : args::getLines(MB)) { SmallVector<StringRef, 3> Fields; - L.split(Fields, ' '); + Line.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 (Fields.size() != 3 || !to_integer(Fields[2], Count)) { + error(MB.getBufferIdentifier() + ": parse error"); + return; + } + + if (InputSectionBase *From = FindSection(Fields[0])) + if (InputSectionBase *To = FindSection(Fields[1])) + Config->CallGraphProfile[std::make_pair(From, To)] += Count; + } +} + +template <class ELFT> static void readCallGraphsFromObjectFiles() { + for (auto File : ObjectFiles) { + auto *Obj = cast<ObjFile<ELFT>>(File); + + for (const Elf_CGProfile_Impl<ELFT> &CGPE : Obj->CGProfile) { + auto *FromSym = dyn_cast<Defined>(&Obj->getSymbol(CGPE.cgp_from)); + auto *ToSym = dyn_cast<Defined>(&Obj->getSymbol(CGPE.cgp_to)); + if (!FromSym || !ToSym) + continue; + + auto *From = dyn_cast_or_null<InputSectionBase>(FromSym->Section); + auto *To = dyn_cast_or_null<InputSectionBase>(ToSym->Section); + if (From && To) + Config->CallGraphProfile[{From, To}] += CGPE.cgp_weight; } - 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; } } @@ -753,7 +774,10 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { Config->DynamicLinker = getDynamicLinker(Args); Config->EhFrameHdr = Args.hasFlag(OPT_eh_frame_hdr, OPT_no_eh_frame_hdr, false); + Config->EmitLLVM = Args.hasArg(OPT_plugin_opt_emit_llvm, false); Config->EmitRelocs = Args.hasArg(OPT_emit_relocs); + Config->CallGraphProfileSort = Args.hasFlag( + OPT_call_graph_profile_sort, OPT_no_call_graph_profile_sort, true); Config->EnableNewDtags = Args.hasFlag(OPT_enable_new_dtags, OPT_disable_new_dtags, true); Config->Entry = Args.getLastArgValue(OPT_entry); @@ -808,6 +832,7 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { Config->SingleRoRx = Args.hasArg(OPT_no_rosegment); Config->SoName = Args.getLastArgValue(OPT_soname); Config->SortSection = getSortSection(Args); + Config->SplitStackAdjustSize = args::getInteger(Args, OPT_split_stack_adjust_size, 16384); Config->Strip = getStrip(Args); Config->Sysroot = Args.getLastArgValue(OPT_sysroot); Config->Target1Rel = Args.hasFlag(OPT_target1_rel, OPT_target1_abs, false); @@ -837,15 +862,20 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { 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->WarnIfuncTextrel = + Args.hasFlag(OPT_warn_ifunc_textrel, OPT_no_warn_ifunc_textrel, 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->ZGlobal = hasZOption(Args, "global"); Config->ZHazardplt = hasZOption(Args, "hazardplt"); Config->ZInitfirst = hasZOption(Args, "initfirst"); + Config->ZInterpose = hasZOption(Args, "interpose"); Config->ZKeepTextSectionPrefix = getZFlag( Args, "keep-text-section-prefix", "nokeep-text-section-prefix", false); + Config->ZNodefaultlib = hasZOption(Args, "nodefaultlib"); Config->ZNodelete = hasZOption(Args, "nodelete"); Config->ZNodlopen = hasZOption(Args, "nodlopen"); Config->ZNow = getZFlag(Args, "now", "lazy", false); @@ -876,6 +906,9 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { if (Config->ThinLTOJobs == 0) error("--thinlto-jobs: number of threads must be > 0"); + if (Config->SplitStackAdjustSize < 0) + error("--split-stack-adjust-size: size must be >= 0"); + // Parse ELF{32,64}{LE,BE} and CPU type. if (auto *Arg = Args.getLastArg(OPT_m)) { StringRef S = Arg->getValue(); @@ -964,22 +997,17 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { // This function initialize such members. See Config.h for the details // of these values. static void setConfigs(opt::InputArgList &Args) { - ELFKind Kind = Config->EKind; - uint16_t Machine = Config->EMachine; + ELFKind K = Config->EKind; + uint16_t M = Config->EMachine; 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->Is64 = (K == ELF64LEKind || K == ELF64BEKind); + Config->IsLE = (K == ELF32LEKind || K == ELF64LEKind); + Config->Endianness = Config->IsLE ? endianness::little : endianness::big; + Config->IsMips64EL = (K == ELF64LEKind && M == EM_MIPS); 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. @@ -993,8 +1021,9 @@ static void setConfigs(opt::InputArgList &Args) { // 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; + Config->IsRela = M == EM_AARCH64 || M == EM_AMDGPU || M == EM_HEXAGON || + M == EM_PPC || M == EM_PPC64 || M == EM_RISCV || + M == EM_X86_64; // If the output uses REL relocations we must store the dynamic relocation // addends to the output sections. We also store addends for RELA relocations @@ -1004,10 +1033,13 @@ static void setConfigs(opt::InputArgList &Args) { Config->WriteAddends = Args.hasFlag(OPT_apply_dynamic_relocs, OPT_no_apply_dynamic_relocs, false) || !Config->IsRela; + + Config->TocOptimize = + Args.hasFlag(OPT_toc_optimize, OPT_no_toc_optimize, M == EM_PPC64); } // Returns a value of "-format" option. -static bool getBinaryOption(StringRef S) { +static bool isFormatBinary(StringRef S) { if (S == "binary") return true; if (S == "elf" || S == "default") @@ -1034,7 +1066,10 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) { StringRef From; StringRef To; std::tie(From, To) = StringRef(Arg->getValue()).split('='); - readDefsym(From, MemoryBufferRef(To, "-defsym")); + if (From.empty() || To.empty()) + error("-defsym: syntax error: " + StringRef(Arg->getValue())); + else + readDefsym(From, MemoryBufferRef(To, "-defsym")); break; } case OPT_script: @@ -1049,7 +1084,7 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) { Config->AsNeeded = true; break; case OPT_format: - InBinary = getBinaryOption(Arg->getValue()); + Config->FormatBinary = isFormatBinary(Arg->getValue()); break; case OPT_no_as_needed: Config->AsNeeded = false; @@ -1220,33 +1255,34 @@ template <class ELFT> static void handleUndefined(StringRef Name) { 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; +template <class ELFT> static void handleLibcall(StringRef Name) { + Symbol *Sym = Symtab->find(Name); + if (!Sym || !Sym->isLazy()) + return; + + MemoryBufferRef MB; + if (auto *LO = dyn_cast<LazyObject>(Sym)) + MB = LO->File->MB; + else + MB = cast<LazyArchive>(Sym)->getMemberBuffer(); + + if (isBitcode(MB)) + Symtab->fetchLazy<ELFT>(Sym); } -// 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() { +// 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. +template <class ELFT> static void demoteSharedSymbols() { 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; + if (auto *S = dyn_cast<SharedSymbol>(Sym)) { + if (!S->getFile<ELFT>().IsNeeded) { + bool Used = S->Used; + replaceSymbol<Undefined>(S, nullptr, S->getName(), STB_WEAK, S->StOther, + S->Type); + S->Used = Used; + } } } } @@ -1315,6 +1351,85 @@ static void findKeepUniqueSections(opt::InputArgList &Args) { } } +template <class ELFT> static Symbol *addUndefined(StringRef Name) { + return Symtab->addUndefined<ELFT>(Name, STB_GLOBAL, STV_DEFAULT, 0, false, + nullptr); +} + +// The --wrap option is a feature to rename symbols so that you can write +// wrappers for existing functions. If you pass `-wrap=foo`, all +// occurrences of symbol `foo` are resolved to `wrap_foo` (so, you are +// expected to write `wrap_foo` function as a wrapper). The original +// symbol becomes accessible as `real_foo`, so you can call that from your +// wrapper. +// +// This data structure is instantiated for each -wrap option. +struct WrappedSymbol { + Symbol *Sym; + Symbol *Real; + Symbol *Wrap; +}; + +// Handles -wrap option. +// +// This function instantiates wrapper symbols. At this point, they seem +// like they are not being used at all, so we explicitly set some flags so +// that LTO won't eliminate them. +template <class ELFT> +static std::vector<WrappedSymbol> addWrappedSymbols(opt::InputArgList &Args) { + std::vector<WrappedSymbol> V; + DenseSet<StringRef> Seen; + + for (auto *Arg : Args.filtered(OPT_wrap)) { + StringRef Name = Arg->getValue(); + if (!Seen.insert(Name).second) + continue; + + Symbol *Sym = Symtab->find(Name); + if (!Sym) + continue; + + Symbol *Real = addUndefined<ELFT>(Saver.save("__real_" + Name)); + Symbol *Wrap = addUndefined<ELFT>(Saver.save("__wrap_" + Name)); + V.push_back({Sym, Real, Wrap}); + + // We want to tell LTO not to inline symbols to be overwritten + // because LTO doesn't know the final symbol contents after renaming. + Real->CanInline = false; + Sym->CanInline = false; + + // Tell LTO not to eliminate these symbols. + Sym->IsUsedInRegularObj = true; + Wrap->IsUsedInRegularObj = true; + } + return V; +} + +// Do renaming for -wrap by updating pointers to symbols. +// +// When this function is executed, only InputFiles and symbol table +// contain pointers to symbol objects. We visit them to replace pointers, +// so that wrapped symbols are swapped as instructed by the command line. +template <class ELFT> static void wrapSymbols(ArrayRef<WrappedSymbol> Wrapped) { + DenseMap<Symbol *, Symbol *> Map; + for (const WrappedSymbol &W : Wrapped) { + Map[W.Sym] = W.Wrap; + Map[W.Real] = W.Sym; + } + + // Update pointers in input files. + parallelForEach(ObjectFiles, [&](InputFile *File) { + std::vector<Symbol *> &Syms = File->getMutableSymbols(); + for (size_t I = 0, E = Syms.size(); I != E; ++I) + if (Symbol *S = Map.lookup(Syms[I])) + Syms[I] = S; + }); + + // Update pointers in the symbol table. + for (const WrappedSymbol &W : Wrapped) + Symtab->wrap(W.Sym, W.Real, W.Wrap); +} + static const char *LibcallRoutineNames[] = { #define HANDLE_LIBCALL(code, name) name, #include "llvm/IR/RuntimeLibcalls.def" @@ -1325,6 +1440,8 @@ static const char *LibcallRoutineNames[] = { // all linker scripts have already been parsed. template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) { Target = getTarget(); + InX<ELFT>::VerSym = nullptr; + InX<ELFT>::VerNeed = nullptr; Config->MaxPageSize = getMaxPageSize(Args); Config->ImageBase = getImageBase(Args); @@ -1380,8 +1497,8 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) { // Some symbols (such as __ehdr_start) are defined lazily only when there // are undefined symbols for them, so we add these to trigger that logic. - for (StringRef Sym : Script->ReferencedSymbols) - Symtab->addUndefined<ELFT>(Sym); + for (StringRef Name : Script->ReferencedSymbols) + addUndefined<ELFT>(Name); // Handle the `--undefined <sym>` options. for (StringRef S : Config->Undefined) @@ -1396,11 +1513,20 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) { // 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. + // However, adding all libcall symbols to the link can have undesired + // consequences. For example, the libgcc implementation of + // __sync_val_compare_and_swap_8 on 32-bit ARM pulls in an .init_array entry + // that aborts the program if the Linux kernel does not support 64-bit + // atomics, which would prevent the program from running even if it does not + // use 64-bit atomics. + // + // Therefore, we only add libcall symbols to the link before LTO if we have + // to, i.e. if the symbol's definition is in bitcode. Any other required + // libcall symbols will be added to the link after LTO when we add the LTO + // object file to the link. if (!BitcodeFiles.empty()) for (const char *S : LibcallRoutineNames) - handleUndefined<ELFT>(S); + handleLibcall<ELFT>(S); // Return if there were name resolution errors. if (errorCount()) @@ -1424,6 +1550,9 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) { Out::ElfHeader = make<OutputSection>("", 0, SHF_ALLOC); Out::ElfHeader->Size = sizeof(typename ELFT::Ehdr); + // Create wrapped symbols for -wrap option. + std::vector<WrappedSymbol> Wrapped = addWrappedSymbols<ELFT>(Args); + // We need to create some reserved symbols such as _end. Create them. if (!Config->Relocatable) addReservedSymbols(); @@ -1436,12 +1565,11 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) { if (!Config->Relocatable) Symtab->scanVersionScript(); - // Create wrapped symbols for -wrap option. - 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. + // + // 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. Symtab->addCombinedLTOObject<ELFT>(); if (errorCount()) return; @@ -1452,8 +1580,15 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) { if (Config->ThinLTOIndexOnly) return; + // Likewise, --plugin-opt=emit-llvm is an option to make LTO create + // an output file in bitcode and exit, so that you can just get a + // combined bitcode file. + if (Config->EmitLLVM) + return; + // Apply symbol renames for -wrap. - Symtab->applySymbolWrap(); + if (!Wrapped.empty()) + wrapSymbols<ELFT>(Wrapped); // Now that we have a complete list of input files. // Beyond this point, no new files are added. @@ -1481,27 +1616,19 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) { // supports them. if (Config->ARMHasBlx == false) warn("lld uses blx instruction, no object with architecture supporting " - "feature detected."); - if (Config->ARMJ1J2BranchEncoding == false) - warn("lld uses extended branch encoding, no object with architecture " - "supporting feature detected."); - if (Config->ARMHasMovtMovw == false) - warn("lld may use movt/movw, no object with architecture supporting " - "feature detected."); + "feature detected"); } // This adds a .comment section containing a version string. We have to add it - // before decompressAndMergeSections because the .comment section is a - // mergeable section. + // before mergeSections because the .comment section is a mergeable section. if (!Config->Relocatable) InputSections.push_back(createCommentSection()); // Do size optimizations: garbage collection, merging of SHF_MERGE sections // and identical code folding. - decompressSections(); splitSections<ELFT>(); markLive<ELFT>(); - demoteSymbols<ELFT>(); + demoteSharedSymbols<ELFT>(); mergeSections(); if (Config->ICF != ICFLevel::None) { findKeepUniqueSections<ELFT>(Args); @@ -1509,9 +1636,12 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) { } // 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); + if (Config->CallGraphProfileSort) { + if (auto *Arg = Args.getLastArg(OPT_call_graph_ordering_file)) + if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue())) + readCallGraph(*Buffer); + readCallGraphsFromObjectFiles<ELFT>(); + } // Write the result to the file. writeResult<ELFT>(); diff --git a/ELF/Driver.h b/ELF/Driver.h index 99e194d9b66c..81d7f608e588 100644 --- a/ELF/Driver.h +++ b/ELF/Driver.h @@ -42,9 +42,6 @@ private: // True if we are in --start-lib and --end-lib. bool InLib = false; - // True if we are in -format=binary and -format=elf. - bool InBinary = false; - std::vector<InputFile *> Files; }; diff --git a/ELF/DriverUtils.cpp b/ELF/DriverUtils.cpp index 698e06edfe63..e51d02e38da1 100644 --- a/ELF/DriverUtils.cpp +++ b/ELF/DriverUtils.cpp @@ -139,8 +139,9 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) { } void elf::printHelp() { - ELFOptTable().PrintHelp(outs(), Config->ProgName.data(), "lld", - false /*ShowHidden*/, true /*ShowAllAliases*/); + ELFOptTable().PrintHelp( + outs(), (Config->ProgName + " [options] file...").str().c_str(), "lld", + false /*ShowHidden*/, true /*ShowAllAliases*/); outs() << "\n"; // Scripts generated by Libtool versions up to at least 2.4.6 (the most diff --git a/ELF/EhFrame.cpp b/ELF/EhFrame.cpp index 20b32c0a96e6..95d444bdc2a1 100644 --- a/ELF/EhFrame.cpp +++ b/ELF/EhFrame.cpp @@ -44,7 +44,7 @@ public: private: template <class P> void failOn(const P *Loc, const Twine &Msg) { fatal("corrupted .eh_frame: " + Msg + "\n>>> defined in " + - IS->getObjMsg((const uint8_t *)Loc - IS->Data.data())); + IS->getObjMsg((const uint8_t *)Loc - IS->data().data())); } uint8_t readByte(); @@ -59,7 +59,7 @@ private: } size_t elf::readEhRecordSize(InputSectionBase *S, size_t Off) { - return EhReader(S, S->Data.slice(Off)).readEhRecordSize(); + return EhReader(S, S->data().slice(Off)).readEhRecordSize(); } // .eh_frame section is a sequence of records. Each record starts with diff --git a/ELF/ICF.cpp b/ELF/ICF.cpp index 075938bd16b9..e917ae76a689 100644 --- a/ELF/ICF.cpp +++ b/ELF/ICF.cpp @@ -252,7 +252,10 @@ bool ICF<ELFT>::constantEq(const InputSection *SecA, ArrayRef<RelTy> RA, auto *DA = dyn_cast<Defined>(&SA); auto *DB = dyn_cast<Defined>(&SB); - if (!DA || !DB) + + // Placeholder symbols generated by linker scripts look the same now but + // may have different values later. + if (!DA || !DB || DA->ScriptDefined || DB->ScriptDefined) return false; // Relocations referring to absolute symbols are constant-equal if their @@ -298,7 +301,7 @@ bool ICF<ELFT>::constantEq(const InputSection *SecA, ArrayRef<RelTy> RA, template <class ELFT> bool ICF<ELFT>::equalsConstant(const InputSection *A, const InputSection *B) { if (A->NumRelocations != B->NumRelocations || A->Flags != B->Flags || - A->getSize() != B->getSize() || A->Data != B->Data) + A->getSize() != B->getSize() || A->data() != B->data()) return false; // If two sections have different output sections, we cannot merge them. @@ -420,6 +423,21 @@ void ICF<ELFT>::forEachClass(llvm::function_ref<void(size_t, size_t)> Fn) { ++Cnt; } +// Combine the hashes of the sections referenced by the given section into its +// hash. +template <class ELFT, class RelTy> +static void combineRelocHashes(InputSection *IS, ArrayRef<RelTy> Rels) { + uint32_t Hash = IS->Class[1]; + for (RelTy Rel : Rels) { + Symbol &S = IS->template getFile<ELFT>()->getRelocTargetSym(Rel); + if (auto *D = dyn_cast<Defined>(&S)) + if (auto *RelSec = dyn_cast_or_null<InputSection>(D->Section)) + Hash ^= RelSec->Class[1]; + } + // Set MSB to 1 to avoid collisions with non-hash IDs. + IS->Class[0] = Hash | (1U << 31); +} + static void print(const Twine &S) { if (Config->PrintIcfSections) message(S); @@ -435,8 +453,14 @@ 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] = xxHash64(S->Data) | (1U << 31); + S->Class[1] = xxHash64(S->data()); + }); + + parallelForEach(Sections, [&](InputSection *S) { + if (S->AreRelocsRela) + combineRelocHashes<ELFT>(S, S->template relas<ELFT>()); + else + combineRelocHashes<ELFT>(S, S->template rels<ELFT>()); }); // From now on, sections in Sections vector are ordered so that sections diff --git a/ELF/InputFiles.cpp b/ELF/InputFiles.cpp index 0eb605a556ae..e4d1dec7cbcb 100644 --- a/ELF/InputFiles.cpp +++ b/ELF/InputFiles.cpp @@ -46,7 +46,7 @@ std::vector<LazyObjFile *> elf::LazyObjFiles; std::vector<InputFile *> elf::ObjectFiles; std::vector<InputFile *> elf::SharedFiles; -TarWriter *elf::Tar; +std::unique_ptr<TarWriter> elf::Tar; InputFile::InputFile(Kind K, MemoryBufferRef M) : MB(M), GroupId(NextGroupId), FileKind(K) { @@ -125,11 +125,7 @@ std::string InputFile::getSrcMsg(const Symbol &Sym, InputSectionBase &Sec, template <class ELFT> void ObjFile<ELFT>::initializeDwarf() { Dwarf = llvm::make_unique<DWARFContext>(make_unique<LLDDwarfObj<ELFT>>(this)); - const DWARFObject &Obj = Dwarf->getDWARFObj(); - DWARFDataExtractor LineData(Obj, Obj.getLineSection(), Config->IsLE, - Config->Wordsize); - - for (std::unique_ptr<DWARFCompileUnit> &CU : Dwarf->compile_units()) { + for (std::unique_ptr<DWARFUnit> &CU : Dwarf->compile_units()) { auto Report = [](Error Err) { handleAllErrors(std::move(Err), [](ErrorInfoBase &Info) { warn(Info.message()); }); @@ -416,6 +412,11 @@ void ObjFile<ELFT>::initializeSections( continue; const Elf_Shdr &Sec = ObjSections[I]; + if (Sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE) + CGProfile = check( + this->getObj().template getSectionContentsAsArray<Elf_CGProfile>( + &Sec)); + // SHF_EXCLUDE'ed sections are discarded by the linker. However, // if -r is given, we'll let the final link discard such sections. // This is compatible with GNU. @@ -442,6 +443,10 @@ void ObjFile<ELFT>::initializeSections( bool IsNew = ComdatGroups.insert(CachedHashStringRef(Signature)).second; this->Sections[I] = &InputSection::Discarded; + // We only support GRP_COMDAT type of group. Get the all entries of the + // section here to let getShtGroupEntries to check the type early for us. + ArrayRef<Elf_Word> Entries = getShtGroupEntries(Sec); + // If it is a new section group, we want to keep group members. // Group leader sections, which contain indices of group members, are // discarded because they are useless beyond this point. The only @@ -454,7 +459,7 @@ void ObjFile<ELFT>::initializeSections( } // Otherwise, discard group members. - for (uint32_t SecIndex : getShtGroupEntries(Sec)) { + for (uint32_t SecIndex : Entries) { if (SecIndex >= Size) fatal(toString(this) + ": invalid section index in group: " + Twine(SecIndex)); @@ -478,11 +483,13 @@ void ObjFile<ELFT>::initializeSections( // .ARM.exidx sections have a reverse dependency on the InputSection they // have a SHF_LINK_ORDER dependency, this is identified by the sh_link. if (Sec.sh_flags & SHF_LINK_ORDER) { - if (Sec.sh_link >= this->Sections.size()) + InputSectionBase *LinkSec = nullptr; + if (Sec.sh_link < this->Sections.size()) + LinkSec = this->Sections[Sec.sh_link]; + if (!LinkSec) fatal(toString(this) + ": invalid sh_link index: " + Twine(Sec.sh_link)); - InputSectionBase *LinkSec = this->Sections[Sec.sh_link]; InputSection *IS = cast<InputSection>(this->Sections[I]); LinkSec->DependentSections.push_back(IS); if (!isa<InputSection>(LinkSec)) @@ -598,7 +605,7 @@ InputSectionBase *ObjFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) { // as a given section. static InputSection *toRegularSection(MergeInputSection *Sec) { return make<InputSection>(Sec->File, Sec->Flags, Sec->Type, Sec->Alignment, - Sec->Data, Sec->Name); + Sec->data(), Sec->Name); } template <class ELFT> @@ -618,9 +625,9 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) { // FIXME: Retain the first attribute section we see. The eglibc ARM // dynamic loaders require the presence of an attribute section for dlopen // to work. In a full implementation we would merge all attribute sections. - if (InX::ARMAttributes == nullptr) { - InX::ARMAttributes = make<InputSection>(*this, Sec, Name); - return InX::ARMAttributes; + if (In.ARMAttributes == nullptr) { + In.ARMAttributes = make<InputSection>(*this, Sec, Name); + return In.ARMAttributes; } return &InputSection::Discarded; } @@ -638,8 +645,16 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) { // This section contains relocation information. // If -r is given, we do not interpret or apply relocation // but just copy relocation sections to output. - if (Config->Relocatable) - return make<InputSection>(*this, Sec, Name); + if (Config->Relocatable) { + InputSection *RelocSec = make<InputSection>(*this, Sec, Name); + // We want to add a dependency to target, similar like we do for + // -emit-relocs below. This is useful for the case when linker script + // contains the "/DISCARD/". It is perhaps uncommon to use a script with + // -r, but we faced it in the Linux kernel and have to handle such case + // and not to crash. + Target->DependentSections.push_back(RelocSec); + return RelocSec; + } if (Target->FirstRelocation) fatal(toString(this) + @@ -704,7 +719,7 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) { // for split stack will include a .note.GNU-split-stack section. if (Name == ".note.GNU-split-stack") { if (Config->Relocatable) { - error("Cannot mix split-stack and non-split-stack in a relocatable link"); + error("cannot mix split-stack and non-split-stack in a relocatable link"); return &InputSection::Discarded; } this->SplitStack = true; @@ -806,7 +821,7 @@ template <class ELFT> Symbol *ObjFile<ELFT>::createSymbol(const Elf_Sym *Sym) { if (Sec == &InputSection::Discarded) return Symtab->addUndefined<ELFT>(Name, Binding, StOther, Type, /*CanOmitFromDynSym=*/false, this); - return Symtab->addRegular(Name, StOther, Type, Value, Size, Binding, Sec, + return Symtab->addDefined(Name, StOther, Type, Value, Size, Binding, Sec, this); } } @@ -940,8 +955,7 @@ std::vector<const typename ELFT::Verdef *> SharedFile<ELFT>::parseVerdefs() { auto *CurVerdef = reinterpret_cast<const Elf_Verdef *>(Verdef); Verdef += CurVerdef->vd_next; unsigned VerdefIndex = CurVerdef->vd_ndx; - if (Verdefs.size() <= VerdefIndex) - Verdefs.resize(VerdefIndex + 1); + Verdefs.resize(VerdefIndex + 1); Verdefs[VerdefIndex] = CurVerdef; } @@ -993,25 +1007,25 @@ template <class ELFT> void SharedFile<ELFT>::parseRest() { 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()) { - 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. + StringRef Name = CHECK(Sym.getName(this->StringTable), this); if (Sym.getBinding() == STB_LOCAL) { warn("found local symbol '" + Name + "' in global part of symbol table in file " + toString(this)); continue; } + if (Sym.isUndefined()) { + Symbol *S = Symtab->addUndefined<ELFT>(Name, Sym.getBinding(), + Sym.st_other, Sym.getType(), + /*CanOmitFromDynSym=*/false, this); + S->ExportDynamic = true; + continue; + } + // 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. @@ -1054,6 +1068,9 @@ static uint8_t getBitcodeMachineKind(StringRef Path, const Triple &T) { switch (T.getArch()) { case Triple::aarch64: return EM_AARCH64; + case Triple::amdgcn: + case Triple::r600: + return EM_AMDGPU; case Triple::arm: case Triple::thumb: return EM_ARM; @@ -1064,9 +1081,12 @@ static uint8_t getBitcodeMachineKind(StringRef Path, const Triple &T) { case Triple::mips64: case Triple::mips64el: return EM_MIPS; + case Triple::msp430: + return EM_MSP430; case Triple::ppc: return EM_PPC; case Triple::ppc64: + case Triple::ppc64le: return EM_PPC64; case Triple::x86: return T.isOSIAMCU() ? EM_IAMCU : EM_386; @@ -1178,7 +1198,7 @@ static ELFKind getELFKind(MemoryBufferRef MB) { } void BinaryFile::parse() { - ArrayRef<uint8_t> Data = toArrayRef(MB.getBuffer()); + ArrayRef<uint8_t> Data = arrayRefFromStringRef(MB.getBuffer()); auto *Section = make<InputSection>(this, SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, 8, Data, ".data"); Sections.push_back(Section); @@ -1192,11 +1212,11 @@ void BinaryFile::parse() { if (!isAlnum(S[I])) S[I] = '_'; - Symtab->addRegular(Saver.save(S + "_start"), STV_DEFAULT, STT_OBJECT, 0, 0, + Symtab->addDefined(Saver.save(S + "_start"), STV_DEFAULT, STT_OBJECT, 0, 0, STB_GLOBAL, Section, nullptr); - Symtab->addRegular(Saver.save(S + "_end"), STV_DEFAULT, STT_OBJECT, + Symtab->addDefined(Saver.save(S + "_end"), STV_DEFAULT, STT_OBJECT, Data.size(), 0, STB_GLOBAL, Section, nullptr); - Symtab->addRegular(Saver.save(S + "_size"), STV_DEFAULT, STT_OBJECT, + Symtab->addDefined(Saver.save(S + "_size"), STV_DEFAULT, STT_OBJECT, Data.size(), 0, STB_GLOBAL, nullptr, nullptr); } @@ -1262,25 +1282,11 @@ template <class ELFT> void LazyObjFile::parse() { return; } - switch (getELFKind(this->MB)) { - case ELF32LEKind: - addElfSymbols<ELF32LE>(); - return; - case ELF32BEKind: - addElfSymbols<ELF32BE>(); + if (getELFKind(this->MB) != Config->EKind) { + error("incompatible file: " + this->MB.getBufferIdentifier()); return; - case ELF64LEKind: - addElfSymbols<ELF64LE>(); - return; - case ELF64BEKind: - addElfSymbols<ELF64BE>(); - return; - default: - llvm_unreachable("getELFKind"); } -} -template <class ELFT> void LazyObjFile::addElfSymbols() { ELFFile<ELFT> Obj = check(ELFFile<ELFT>::create(MB.getBuffer())); ArrayRef<typename ELFT::Shdr> Sections = CHECK(Obj.sections(), this); @@ -1305,12 +1311,9 @@ std::string elf::replaceThinLTOSuffix(StringRef Path) { StringRef Suffix = Config->ThinLTOObjectSuffixReplace.first; StringRef Repl = Config->ThinLTOObjectSuffixReplace.second; - 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(); + if (Path.consume_back(Suffix)) + return (Path + Repl).str(); + return Path; } template void ArchiveFile::parse<ELF32LE>(); diff --git a/ELF/InputFiles.h b/ELF/InputFiles.h index 0db3203b0ba2..5094ddd804a5 100644 --- a/ELF/InputFiles.h +++ b/ELF/InputFiles.h @@ -50,7 +50,7 @@ class Symbol; // If -reproduce option is given, all input files are written // to this tar archive. -extern llvm::TarWriter *Tar; +extern std::unique_ptr<llvm::TarWriter> Tar; // Opens a given file. llvm::Optional<MemoryBufferRef> readFile(StringRef Path); @@ -86,7 +86,9 @@ public: // Returns object file symbols. It is a runtime error to call this // function on files of other types. - ArrayRef<Symbol *> getSymbols() { + ArrayRef<Symbol *> getSymbols() { return getMutableSymbols(); } + + std::vector<Symbol *> &getMutableSymbols() { assert(FileKind == BinaryKind || FileKind == ObjKind || FileKind == BitcodeKind); return Symbols; @@ -169,6 +171,7 @@ template <class ELFT> class ObjFile : public ELFFileBase<ELFT> { typedef typename ELFT::Sym Elf_Sym; typedef typename ELFT::Shdr Elf_Shdr; typedef typename ELFT::Word Elf_Word; + typedef typename ELFT::CGProfile Elf_CGProfile; StringRef getShtGroupSignature(ArrayRef<Elf_Shdr> Sections, const Elf_Shdr &Sec); @@ -218,6 +221,9 @@ public: // Pointer to this input file's .llvm_addrsig section, if it has one. const Elf_Shdr *AddrsigSec = nullptr; + // SHT_LLVM_CALL_GRAPH_PROFILE table + ArrayRef<Elf_CGProfile> CGProfile; + private: void initializeSections(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups); @@ -272,8 +278,6 @@ public: bool AddedToLink = false; private: - template <class ELFT> void addElfSymbols(); - uint64_t OffsetInArchive; }; diff --git a/ELF/InputSection.cpp b/ELF/InputSection.cpp index 54fb57cf9888..839bff7011eb 100644 --- a/ELF/InputSection.cpp +++ b/ELF/InputSection.cpp @@ -21,7 +21,6 @@ #include "Thunks.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" -#include "llvm/Object/Decompressor.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Compression.h" #include "llvm/Support/Endian.h" @@ -64,11 +63,11 @@ InputSectionBase::InputSectionBase(InputFile *File, uint64_t Flags, StringRef Name, Kind SectionKind) : SectionBase(SectionKind, Name, Flags, Entsize, Alignment, Type, Info, Link), - File(File), Data(Data) { + File(File), RawData(Data) { // In order to reduce memory allocation, we assume that mergeable // sections are smaller than 4 GiB, which is not an unreasonable // assumption as of 2017. - if (SectionKind == SectionBase::Merge && Data.size() > UINT32_MAX) + if (SectionKind == SectionBase::Merge && RawData.size() > UINT32_MAX) error(toString(this) + ": section too large"); NumRelocations = 0; @@ -80,6 +79,17 @@ InputSectionBase::InputSectionBase(InputFile *File, uint64_t Flags, if (!isPowerOf2_64(V)) fatal(toString(File) + ": section sh_addralign is not a power of 2"); this->Alignment = V; + + // In ELF, each section can be compressed by zlib, and if compressed, + // section name may be mangled by appending "z" (e.g. ".zdebug_info"). + // If that's the case, demangle section name so that we can handle a + // section as if it weren't compressed. + if ((Flags & SHF_COMPRESSED) || Name.startswith(".zdebug")) { + if (!zlib::isAvailable()) + error(toString(File) + ": contains a compressed section, " + + "but zlib is not available"); + parseCompressedHeader(); + } } // Drop SHF_GROUP bit unless we are producing a re-linkable object file. @@ -128,13 +138,25 @@ InputSectionBase::InputSectionBase(ObjFile<ELFT> &File, size_t InputSectionBase::getSize() const { if (auto *S = dyn_cast<SyntheticSection>(this)) return S->getSize(); + if (UncompressedSize >= 0) + return UncompressedSize; + return RawData.size(); +} + +void InputSectionBase::uncompress() const { + size_t Size = UncompressedSize; + UncompressedBuf.reset(new char[Size]); - return Data.size(); + if (Error E = + zlib::uncompress(toStringRef(RawData), UncompressedBuf.get(), Size)) + fatal(toString(this) + + ": uncompress failed: " + llvm::toString(std::move(E))); + RawData = makeArrayRef((uint8_t *)UncompressedBuf.get(), Size); } uint64_t InputSectionBase::getOffsetInFile() const { const uint8_t *FileStart = (const uint8_t *)File->MB.getBufferStart(); - const uint8_t *SecStart = Data.begin(); + const uint8_t *SecStart = data().begin(); return SecStart - FileStart; } @@ -180,34 +202,70 @@ OutputSection *SectionBase::getOutputSection() { return Sec ? Sec->getParent() : nullptr; } -// Decompress section contents if required. Note that this function -// is called from parallelForEach, so it must be thread-safe. -void InputSectionBase::maybeDecompress() { - if (DecompressBuf) - return; - if (!(Flags & SHF_COMPRESSED) && !Name.startswith(".zdebug")) - return; +// When a section is compressed, `RawData` consists with a header followed +// by zlib-compressed data. This function parses a header to initialize +// `UncompressedSize` member and remove the header from `RawData`. +void InputSectionBase::parseCompressedHeader() { + typedef typename ELF64LE::Chdr Chdr64; + typedef typename ELF32LE::Chdr Chdr32; - // Decompress a section. - Decompressor Dec = check(Decompressor::create(Name, toStringRef(Data), - Config->IsLE, Config->Is64)); + // Old-style header + if (Name.startswith(".zdebug")) { + if (!toStringRef(RawData).startswith("ZLIB")) { + error(toString(this) + ": corrupted compressed section header"); + return; + } + RawData = RawData.slice(4); - size_t Size = Dec.getDecompressedSize(); - 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))); + if (RawData.size() < 8) { + error(toString(this) + ": corrupted compressed section header"); + return; + } + + UncompressedSize = read64be(RawData.data()); + RawData = RawData.slice(8); + + // Restore the original section name. + // (e.g. ".zdebug_info" -> ".debug_info") + Name = Saver.save("." + Name.substr(2)); + return; + } - Data = makeArrayRef((uint8_t *)DecompressBuf.get(), Size); + assert(Flags & SHF_COMPRESSED); 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); + // New-style 64-bit header + if (Config->Is64) { + if (RawData.size() < sizeof(Chdr64)) { + error(toString(this) + ": corrupted compressed section"); + return; + } + + auto *Hdr = reinterpret_cast<const Chdr64 *>(RawData.data()); + if (Hdr->ch_type != ELFCOMPRESS_ZLIB) { + error(toString(this) + ": unsupported compression type"); + return; + } + + UncompressedSize = Hdr->ch_size; + RawData = RawData.slice(sizeof(*Hdr)); + return; } + + // New-style 32-bit header + if (RawData.size() < sizeof(Chdr32)) { + error(toString(this) + ": corrupted compressed section"); + return; + } + + auto *Hdr = reinterpret_cast<const Chdr32 *>(RawData.data()); + if (Hdr->ch_type != ELFCOMPRESS_ZLIB) { + error(toString(this) + ": unsupported compression type"); + return; + } + + UncompressedSize = Hdr->ch_size; + RawData = RawData.slice(sizeof(*Hdr)); } InputSection *InputSectionBase::getLinkOrderDep() const { @@ -230,14 +288,17 @@ Defined *InputSectionBase::getEnclosingFunction(uint64_t Offset) { // Returns a source location string. Used to construct an error message. template <class ELFT> std::string InputSectionBase::getLocation(uint64_t Offset) { + std::string SecAndOffset = (Name + "+0x" + utohexstr(Offset)).str(); + // We don't have file for synthetic sections. if (getFile<ELFT>() == nullptr) - return (Config->OutputFile + ":(" + Name + "+0x" + utohexstr(Offset) + ")") + return (Config->OutputFile + ":(" + SecAndOffset + ")") .str(); // First check if we can get desired values from debugging information. if (Optional<DILineInfo> Info = getFile<ELFT>()->getDILineInfo(this, Offset)) - return Info->FileName + ":" + std::to_string(Info->Line); + return Info->FileName + ":" + std::to_string(Info->Line) + ":(" + + SecAndOffset + ")"; // File->SourceFile contains STT_FILE symbol that contains a // source file name. If it's missing, we use an object file name. @@ -246,10 +307,10 @@ std::string InputSectionBase::getLocation(uint64_t Offset) { SrcFile = toString(File); if (Defined *D = getEnclosingFunction<ELFT>(Offset)) - return SrcFile + ":(function " + toString(*D) + ")"; + return SrcFile + ":(function " + toString(*D) + ": " + SecAndOffset + ")"; // If there's no symbol, print out the offset in the section. - return (SrcFile + ":(" + Name + "+0x" + utohexstr(Offset) + ")").str(); + return (SrcFile + ":(" + SecAndOffset + ")"); } // This function is intended to be used for constructing an error message. @@ -259,9 +320,6 @@ std::string InputSectionBase::getLocation(uint64_t Offset) { // // Returns an empty string if there's no way to get line info. std::string InputSectionBase::getSrcMsg(const Symbol &Sym, uint64_t Offset) { - // Synthetic sections don't have input files. - if (!File) - return ""; return File->getSrcMsg(Sym, *this, Offset); } @@ -275,9 +333,6 @@ std::string InputSectionBase::getSrcMsg(const Symbol &Sym, uint64_t Offset) { // // path/to/foo.o:(function bar) in archive path/to/bar.a std::string InputSectionBase::getObjMsg(uint64_t Off) { - // Synthetic sections don't have input files. - if (!File) - return ("<internal>:(" + Name + "+0x" + utohexstr(Off) + ")").str(); std::string Filename = File->getName(); std::string Archive; @@ -362,7 +417,7 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) { // 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->getVA(Rel.r_offset); - P->setSymbolAndType(InX::SymTab->getSymbolIndex(&Sym), Type, + P->setSymbolAndType(In.SymTab->getSymbolIndex(&Sym), Type, Config->IsMips64EL); if (Sym.Type == STT_SECTION) { @@ -380,14 +435,14 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) { error("STT_SECTION symbol should be defined"); continue; } - SectionBase *Section = D->Section; - if (Section == &InputSection::Discarded) { + SectionBase *Section = D->Section->Repl; + if (!Section->Live) { P->setSymbolAndType(0, 0, false); continue; } int64_t Addend = getAddend<ELFT>(Rel); - const uint8_t *BufLoc = Sec->Data.begin() + Rel.r_offset; + const uint8_t *BufLoc = Sec->data().begin() + Rel.r_offset; if (!RelTy::IsRela) Addend = Target->getImplicitAddend(BufLoc, Type); @@ -487,6 +542,62 @@ static uint64_t getARMStaticBase(const Symbol &Sym) { return OS->PtLoad->FirstSec->Addr; } +// For R_RISCV_PC_INDIRECT (R_RISCV_PCREL_LO12_{I,S}), the symbol actually +// points the corresponding R_RISCV_PCREL_HI20 relocation, and the target VA +// is calculated using PCREL_HI20's symbol. +// +// This function returns the R_RISCV_PCREL_HI20 relocation from +// R_RISCV_PCREL_LO12's symbol and addend. +static Relocation *getRISCVPCRelHi20(const Symbol *Sym, uint64_t Addend) { + const Defined *D = cast<Defined>(Sym); + InputSection *IS = cast<InputSection>(D->Section); + + if (Addend != 0) + warn("Non-zero addend in R_RISCV_PCREL_LO12 relocation to " + + IS->getObjMsg(D->Value) + " is ignored"); + + // Relocations are sorted by offset, so we can use std::equal_range to do + // binary search. + auto Range = std::equal_range(IS->Relocations.begin(), IS->Relocations.end(), + D->Value, RelocationOffsetComparator{}); + for (auto It = std::get<0>(Range); It != std::get<1>(Range); ++It) + if (isRelExprOneOf<R_PC>(It->Expr)) + return &*It; + + error("R_RISCV_PCREL_LO12 relocation points to " + IS->getObjMsg(D->Value) + + " without an associated R_RISCV_PCREL_HI20 relocation"); + return nullptr; +} + +// A TLS symbol's virtual address is relative to the TLS segment. Add a +// target-specific adjustment to produce a thread-pointer-relative offset. +static int64_t getTlsTpOffset() { + switch (Config->EMachine) { + case EM_ARM: + case EM_AARCH64: + // Variant 1. The thread pointer points to a TCB with a fixed 2-word size, + // followed by a variable amount of alignment padding, followed by the TLS + // segment. + // + // NB: While the ARM/AArch64 ABI formally has a 2-word TCB size, lld + // effectively increases the TCB size to 8 words for Android compatibility. + // It accomplishes this by increasing the segment's alignment. + return alignTo(Config->Wordsize * 2, Out::TlsPhdr->p_align); + case EM_386: + case EM_X86_64: + // Variant 2. The TLS segment is located just before the thread pointer. + return -Out::TlsPhdr->p_memsz; + case EM_PPC64: + // The thread pointer points to a fixed offset from the start of the + // executable's TLS segment. An offset of 0x7000 allows a signed 16-bit + // offset to reach 0x1000 of TCB/thread-library data and 0xf000 of the + // program's TLS segment. + return -0x7000; + default: + llvm_unreachable("unhandled Config->EMachine"); + } +} + static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A, uint64_t P, const Symbol &Sym, RelExpr Expr) { switch (Expr) { @@ -501,38 +612,37 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A, case R_ARM_SBREL: return Sym.getVA(A) - getARMStaticBase(Sym); case R_GOT: + case R_GOT_PLT: case R_RELAX_TLS_GD_TO_IE_ABS: return Sym.getGotVA() + A; case R_GOTONLY_PC: - return InX::Got->getVA() + A - P; + return In.Got->getVA() + A - P; case R_GOTONLY_PC_FROM_END: - return InX::Got->getVA() + A - P + InX::Got->getSize(); + return In.Got->getVA() + A - P + In.Got->getSize(); case R_GOTREL: - return Sym.getVA(A) - InX::Got->getVA(); + return Sym.getVA(A) - In.Got->getVA(); case R_GOTREL_FROM_END: - return Sym.getVA(A) - InX::Got->getVA() - InX::Got->getSize(); + return Sym.getVA(A) - In.Got->getVA() - In.Got->getSize(); case R_GOT_FROM_END: case R_RELAX_TLS_GD_TO_IE_END: - return Sym.getGotOffset() + A - InX::Got->getSize(); + return Sym.getGotOffset() + A - In.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: + case R_AARCH64_GOT_PAGE_PC: + case R_AARCH64_GOT_PAGE_PC_PLT: + case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC: return getAArch64Page(Sym.getGotVA() + A) - getAArch64Page(P); case R_GOT_PC: case R_RELAX_TLS_GD_TO_IE: return Sym.getGotVA() + A - P; - case R_HINT: - case R_NONE: - case R_TLSDESC_CALL: - case R_TLSLD_HINT: - llvm_unreachable("cannot relocate hint relocs"); + case R_HEXAGON_GOT: + return Sym.getGotVA() - In.GotPlt->getVA(); case R_MIPS_GOTREL: - return Sym.getVA(A) - InX::MipsGot->getGp(File); + return Sym.getVA(A) - In.MipsGot->getGp(File); case R_MIPS_GOT_GP: - return InX::MipsGot->getGp(File) + A; + return In.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 @@ -541,7 +651,7 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A, // 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(File) + A - P; + uint64_t V = In.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) @@ -552,31 +662,34 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A, // 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(File, Sym, A) - - InX::MipsGot->getGp(File); + return In.MipsGot->getVA() + In.MipsGot->getPageEntryOffset(File, Sym, A) - + In.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(File, Sym, A) - - InX::MipsGot->getGp(File); + return In.MipsGot->getVA() + In.MipsGot->getSymEntryOffset(File, Sym, A) - + In.MipsGot->getGp(File); case R_MIPS_TLSGD: - return InX::MipsGot->getVA() + InX::MipsGot->getGlobalDynOffset(File, Sym) - - InX::MipsGot->getGp(File); + return In.MipsGot->getVA() + In.MipsGot->getGlobalDynOffset(File, Sym) - + In.MipsGot->getGp(File); case R_MIPS_TLSLD: - return InX::MipsGot->getVA() + InX::MipsGot->getTlsIndexOffset(File) - - InX::MipsGot->getGp(File); - case R_PAGE_PC: - case R_PLT_PAGE_PC: { - uint64_t Dest; - if (Sym.isUndefWeak()) - Dest = getAArch64Page(A); - else - Dest = getAArch64Page(Sym.getVA(A)); - return Dest - getAArch64Page(P); + return In.MipsGot->getVA() + In.MipsGot->getTlsIndexOffset(File) - + In.MipsGot->getGp(File); + case R_AARCH64_PAGE_PC: { + uint64_t Val = Sym.isUndefWeak() ? P + A : Sym.getVA(A); + return getAArch64Page(Val) - getAArch64Page(P); + } + case R_AARCH64_PLT_PAGE_PC: { + uint64_t Val = Sym.isUndefWeak() ? P + A : Sym.getPltVA() + A; + return getAArch64Page(Val) - getAArch64Page(P); + } + case R_RISCV_PC_INDIRECT: { + if (const Relocation *HiRel = getRISCVPCRelHi20(&Sym, A)) + return getRelocTargetVA(File, HiRel->Type, HiRel->Addend, Sym.getVA(), + *HiRel->Sym, HiRel->Expr); + return 0; } case R_PC: { uint64_t Dest; @@ -608,16 +721,12 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A, return 0; // 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); + // point is used for calls where the caller and callee (may) have different + // TOC base pointers and r2 needs to be modified to hold the TOC base for + // the callee. For local calls the caller and callee share the same + // TOC base and so the TOC pointer initialization code should be skipped by + // branching to the local entry point. + return SymVA - P + getPPC64GlobalEntryToLocalEntryOffset(Sym.StOther); } case R_PPC_TOC: return getPPC64TocBase() + A; @@ -634,48 +743,32 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A, // statically to zero. if (Sym.isTls() && Sym.isUndefWeak()) return 0; - - // 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; + return Sym.getVA(A) + getTlsTpOffset(); case R_RELAX_TLS_GD_TO_LE_NEG: case R_NEG_TLS: return Out::TlsPhdr->p_memsz - Sym.getVA(A); case R_SIZE: return Sym.getSize() + A; case R_TLSDESC: - return InX::Got->getGlobalDynAddr(Sym) + A; - case R_TLSDESC_PAGE: - return getAArch64Page(InX::Got->getGlobalDynAddr(Sym) + A) - + return In.Got->getGlobalDynAddr(Sym) + A; + case R_AARCH64_TLSDESC_PAGE: + return getAArch64Page(In.Got->getGlobalDynAddr(Sym) + A) - getAArch64Page(P); case R_TLSGD_GOT: - return InX::Got->getGlobalDynOffset(Sym) + A; + return In.Got->getGlobalDynOffset(Sym) + A; case R_TLSGD_GOT_FROM_END: - return InX::Got->getGlobalDynOffset(Sym) + A - InX::Got->getSize(); + return In.Got->getGlobalDynOffset(Sym) + A - In.Got->getSize(); case R_TLSGD_PC: - return InX::Got->getGlobalDynAddr(Sym) + A - P; + return In.Got->getGlobalDynAddr(Sym) + A - P; case R_TLSLD_GOT_FROM_END: - return InX::Got->getTlsIndexOff() + A - InX::Got->getSize(); + return In.Got->getTlsIndexOff() + A - In.Got->getSize(); case R_TLSLD_GOT: - return InX::Got->getTlsIndexOff() + A; + return In.Got->getTlsIndexOff() + A; case R_TLSLD_PC: - return InX::Got->getTlsIndexVA() + A - P; + return In.Got->getTlsIndexVA() + A - P; + default: + llvm_unreachable("invalid expression"); } - llvm_unreachable("Invalid expression"); } // This function applies relocations to sections without SHF_ALLOC bit. @@ -808,10 +901,10 @@ void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) { case R_RELAX_TLS_GD_TO_LE_NEG: Target->relaxTlsGdToLe(BufLoc, Type, TargetVA); break; + case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC: 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; @@ -848,16 +941,20 @@ static void switchMorestackCallsToMorestackNonSplit( // __morestack inside that function should be switched to // __morestack_non_split. Symbol *MoreStackNonSplit = Symtab->find("__morestack_non_split"); + if (!MoreStackNonSplit) { + error("Mixing split-stack objects requires a definition of " + "__morestack_non_split"); + return; + } // Sort both collections to compare addresses efficiently. - llvm::sort(MorestackCalls.begin(), MorestackCalls.end(), - [](const Relocation *L, const Relocation *R) { - return L->Offset < R->Offset; - }); + llvm::sort(MorestackCalls, [](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; }); + llvm::sort(Functions, [](const Defined *L, const Defined *R) { + return L->Value < R->Value; + }); auto It = MorestackCalls.begin(); for (Defined *F : Functions) { @@ -872,8 +969,8 @@ static void switchMorestackCallsToMorestackNonSplit( } } -static bool enclosingPrologueAdjusted(uint64_t Offset, - const DenseSet<Defined *> &Prologues) { +static bool enclosingPrologueAttempted(uint64_t Offset, + const DenseSet<Defined *> &Prologues) { for (Defined *F : Prologues) if (F->Value <= Offset && Offset < F->Value + F->Size) return true; @@ -889,7 +986,7 @@ void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *Buf, uint8_t *End) { if (!getFile<ELFT>()->SplitStack) return; - DenseSet<Defined *> AdjustedPrologues; + DenseSet<Defined *> Prologues; std::vector<Relocation *> MorestackCalls; for (Relocation &Rel : Relocations) { @@ -898,15 +995,9 @@ void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *Buf, 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")) + if (Rel.Sym->getName().startswith("__morestack")) { + if (Rel.Sym->getName().equals("__morestack")) MorestackCalls.push_back(&Rel); continue; } @@ -914,24 +1005,36 @@ void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *Buf, // 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) + if (Rel.Sym->Type != STT_FUNC) continue; - if (enclosingPrologueAdjusted(Rel.Offset, AdjustedPrologues)) + // If the callee's-file was compiled with split stack, nothing to do. In + // this context, a "Defined" symbol is one "defined by the binary currently + // being produced". So an "undefined" symbol might be provided by a shared + // library. It is not possible to tell how such symbols were compiled, so be + // conservative. + if (Defined *D = dyn_cast<Defined>(Rel.Sym)) + if (InputSection *IS = cast_or_null<InputSection>(D->Section)) + if (!IS || !IS->getFile<ELFT>() || IS->getFile<ELFT>()->SplitStack) + continue; + + if (enclosingPrologueAttempted(Rel.Offset, Prologues)) continue; if (Defined *F = getEnclosingFunction<ELFT>(Rel.Offset)) { - if (Target->adjustPrologueForCrossSplitStack(Buf + F->Value, End)) { - AdjustedPrologues.insert(F); + Prologues.insert(F); + if (Target->adjustPrologueForCrossSplitStack(Buf + getOffset(F->Value), + End, F->StOther)) continue; - } + if (!getFile<ELFT>()->SomeNoSplitStack) + error(lld::toString(this) + ": " + F->getName() + + " (with -fsplit-stack) calls " + Rel.Sym->getName() + + " (without -fsplit-stack), but couldn't adjust its prologue"); } - 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); + + if (Target->NeedsMoreStackNonSplit) + switchMorestackCallsToMorestackNonSplit(Prologues, MorestackCalls); } template <class ELFT> void InputSection::writeTo(uint8_t *Buf) { @@ -960,10 +1063,23 @@ template <class ELFT> void InputSection::writeTo(uint8_t *Buf) { return; } + // If this is a compressed section, uncompress section contents directly + // to the buffer. + if (UncompressedSize >= 0 && !UncompressedBuf) { + size_t Size = UncompressedSize; + if (Error E = zlib::uncompress(toStringRef(RawData), + (char *)(Buf + OutSecOff), Size)) + fatal(toString(this) + + ": uncompress failed: " + llvm::toString(std::move(E))); + uint8_t *BufEnd = Buf + OutSecOff + Size; + relocate<ELFT>(Buf, BufEnd); + return; + } + // Copy section contents from source object file to output file // and then apply relocations. - memcpy(Buf + OutSecOff, Data.data(), Data.size()); - uint8_t *BufEnd = Buf + OutSecOff + Data.size(); + memcpy(Buf + OutSecOff, data().data(), data().size()); + uint8_t *BufEnd = Buf + OutSecOff + data().size(); relocate<ELFT>(Buf, BufEnd); } @@ -1014,7 +1130,7 @@ template <class ELFT> void EhInputSection::split() { template <class ELFT, class RelTy> void EhInputSection::split(ArrayRef<RelTy> Rels) { unsigned RelI = 0; - for (size_t Off = 0, End = Data.size(); Off != End;) { + for (size_t Off = 0, End = data().size(); Off != End;) { size_t Size = readEhRecordSize(this, Off); Pieces.emplace_back(Off, this, Size, getReloc(Off, Size, Rels, RelI)); // The empty record is the end marker. @@ -1094,65 +1210,32 @@ void MergeInputSection::splitIntoPieces() { assert(Pieces.empty()); if (Flags & SHF_STRINGS) - splitStrings(Data, Entsize); + splitStrings(data(), Entsize); else - splitNonStrings(Data, Entsize); - - 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> -static It fastUpperBound(It First, It Last, const T &Value, Compare Comp) { - size_t Size = std::distance(First, Last); - assert(Size != 0); - while (Size != 1) { - size_t H = Size / 2; - const It MI = First + H; - Size -= H; - First = Comp(Value, *MI) ? First : First + H; - } - return Comp(Value, *First) ? First : First + 1; -} - -// 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( - Sec->Pieces.begin(), Sec->Pieces.end(), Offset, - [](const uint64_t &A, const SectionPiece &B) { return A < B.InputOff; }); - --I; - return &*I; + splitNonStrings(data(), Entsize); } 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 (this->data().size() <= Offset) + fatal(toString(this) + ": offset is outside the section"); // 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); + // In that case we need to do a binary search of the original section piece vector. + auto It2 = + llvm::upper_bound(Pieces, Offset, [](uint64_t Offset, SectionPiece P) { + return Offset < P.InputOff; + }); + return &It2[-1]; } // 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::getParentOffset(uint64_t Offset) const { - // Find a string starting at a given offset. - auto It = OffsetMap.find(Offset); - if (It != OffsetMap.end()) - return Pieces[It->second].OutputOff; - // 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 = - *findSectionPiece(const_cast<MergeInputSection *>(this), Offset); + *(const_cast<MergeInputSection *>(this)->getSectionPiece (Offset)); uint64_t Addend = Offset - Piece.InputOff; return Piece.OutputOff + Addend; } diff --git a/ELF/InputSection.h b/ELF/InputSection.h index 4db01e035e32..34f411e87200 100644 --- a/ELF/InputSection.h +++ b/ELF/InputSection.h @@ -115,7 +115,12 @@ public: return cast_or_null<ObjFile<ELFT>>(File); } - ArrayRef<uint8_t> Data; + ArrayRef<uint8_t> data() const { + if (UncompressedSize >= 0 && !UncompressedBuf) + uncompress(); + return RawData; + } + uint64_t getOffsetInFile() const; // True if this section has already been placed to a linker script @@ -169,11 +174,6 @@ public: 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 maybeDecompress(); - // Returns a source location string. Used to construct an error message. template <class ELFT> std::string getLocation(uint64_t Offset); std::string getSrcMsg(const Symbol &Sym, uint64_t Offset); @@ -200,15 +200,21 @@ public: template <typename T> llvm::ArrayRef<T> getDataAs() const { - size_t S = Data.size(); + size_t S = data().size(); assert(S % sizeof(T) == 0); - return llvm::makeArrayRef<T>((const T *)Data.data(), S / sizeof(T)); + return llvm::makeArrayRef<T>((const T *)data().data(), S / sizeof(T)); } -private: - // A pointer that owns decompressed data if a section is compressed by zlib. +protected: + void parseCompressedHeader(); + void uncompress() const; + + mutable ArrayRef<uint8_t> RawData; + + // A pointer that owns uncompressed data if a section is compressed by zlib. // Since the feature is not used often, this is usually a nullptr. - std::unique_ptr<char[]> DecompressBuf; + mutable std::unique_ptr<char[]> UncompressedBuf; + int64_t UncompressedSize = -1; }; // SectionPiece represents a piece of splittable section contents. @@ -247,7 +253,6 @@ public: // 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. @@ -255,8 +260,8 @@ public: llvm::CachedHashStringRef getData(size_t I) const { size_t Begin = Pieces[I].InputOff; size_t End = - (Pieces.size() - 1 == I) ? Data.size() : Pieces[I + 1].InputOff; - return {toStringRef(Data.slice(Begin, End - Begin)), Pieces[I].Hash}; + (Pieces.size() - 1 == I) ? data().size() : Pieces[I + 1].InputOff; + return {toStringRef(data().slice(Begin, End - Begin)), Pieces[I].Hash}; } // Returns the SectionPiece at a given input section offset. @@ -277,7 +282,9 @@ struct EhSectionPiece { unsigned FirstRelocation) : InputOff(Off), Sec(Sec), Size(Size), FirstRelocation(FirstRelocation) {} - ArrayRef<uint8_t> data() { return {Sec->Data.data() + this->InputOff, Size}; } + ArrayRef<uint8_t> data() { + return {Sec->data().data() + this->InputOff, Size}; + } size_t InputOff; ssize_t OutputOff = -1; @@ -353,6 +360,7 @@ private: // The list of all input sections. extern std::vector<InputSectionBase *> InputSections; + } // namespace elf std::string toString(const elf::InputSectionBase *); diff --git a/ELF/LTO.cpp b/ELF/LTO.cpp index ef58932e86cc..ca44581780e4 100644 --- a/ELF/LTO.cpp +++ b/ELF/LTO.cpp @@ -67,9 +67,10 @@ static std::string getThinLTOOutputFile(StringRef ModulePath) { static lto::Config createConfig() { lto::Config C; - // LLD supports the new relocations. + // LLD supports the new relocations and address-significance tables. C.Options = InitTargetOptionsFromCodeGenFlags(); C.Options.RelaxELFRelocations = true; + C.Options.EmitAddrsig = true; // Always emit a section per function/datum with LTO. C.Options.FunctionSections = true; @@ -87,6 +88,7 @@ static lto::Config createConfig() { C.DiagHandler = diagnosticHandler; C.OptLevel = Config->LTOO; C.CPU = GetCPUStr(); + C.MAttrs = GetMAttrs(); // Set up a custom pipeline if we've been asked to. C.OptPipeline = Config->LTONewPmPasses; @@ -101,6 +103,14 @@ static lto::Config createConfig() { C.DebugPassManager = Config->LTODebugPassManager; C.DwoDir = Config->DwoDir; + if (Config->EmitLLVM) { + C.PostInternalizeModuleHook = [](size_t Task, const Module &M) { + if (std::unique_ptr<raw_fd_ostream> OS = openFile(Config->OutputFile)) + WriteBitcodeToFile(M, *OS, false); + return false; + }; + } + if (Config->SaveTemps) checkError(C.addSaveTemps(Config->OutputFile.str() + ".", /*UseInputModulePath*/ true)); @@ -108,18 +118,14 @@ static lto::Config createConfig() { } BitcodeCompiler::BitcodeCompiler() { + // Initialize IndexFile. + if (!Config->ThinLTOIndexOnlyArg.empty()) + IndexFile = openFile(Config->ThinLTOIndexOnlyArg); + // Initialize LTOObj. lto::ThinBackend Backend; - if (Config->ThinLTOIndexOnly) { - StringRef Path = Config->ThinLTOIndexOnlyArg; - if (!Path.empty()) - IndexFile = openFile(Path); - - auto OnIndexWrite = [&](const std::string &Identifier) { - ObjectToIndexFileState[Identifier] = true; - }; - + auto OnIndexWrite = [&](StringRef S) { ThinIndices.erase(S); }; Backend = lto::createWriteIndexesThinBackend( Config->ThinLTOPrefixReplace.first, Config->ThinLTOPrefixReplace.second, Config->ThinLTOEmitImportsFiles, IndexFile.get(), OnIndexWrite); @@ -132,10 +138,10 @@ BitcodeCompiler::BitcodeCompiler() { // Initialize UsedStartStop. for (Symbol *Sym : Symtab->getSymbols()) { - StringRef Name = Sym->getName(); + StringRef S = Sym->getName(); for (StringRef Prefix : {"__start_", "__stop_"}) - if (Name.startswith(Prefix)) - UsedStartStop.insert(Name.substr(Prefix.size())); + if (S.startswith(Prefix)) + UsedStartStop.insert(S.substr(Prefix.size())); } } @@ -151,7 +157,7 @@ void BitcodeCompiler::add(BitcodeFile &F) { bool IsExec = !Config->Shared && !Config->Relocatable; if (Config->ThinLTOIndexOnly) - ObjectToIndexFileState.insert({Obj.getName(), false}); + ThinIndices.insert(Obj.getName()); ArrayRef<Symbol *> Syms = F.getSymbols(); ArrayRef<lto::InputFile::Symbol> ObjSyms = Obj.symbols(); @@ -240,15 +246,11 @@ std::vector<InputFile *> BitcodeCompiler::compile() { 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"); - } + for (StringRef S : ThinIndices) { + std::string Path = getThinLTOOutputFile(S); + openFile(Path + ".thinlto.bc"); + if (Config->ThinLTOEmitImportsFiles) + openFile(Path + ".imports"); } // If LazyObjFile has not been added to link, emit empty index files. diff --git a/ELF/LTO.h b/ELF/LTO.h index 8803078eb1df..a190da3e5996 100644 --- a/ELF/LTO.h +++ b/ELF/LTO.h @@ -55,7 +55,7 @@ private: std::vector<std::unique_ptr<MemoryBuffer>> Files; llvm::DenseSet<StringRef> UsedStartStop; std::unique_ptr<llvm::raw_fd_ostream> IndexFile; - llvm::StringMap<bool> ObjectToIndexFileState; + llvm::DenseSet<StringRef> ThinIndices; }; } // namespace elf } // namespace lld diff --git a/ELF/LinkerScript.cpp b/ELF/LinkerScript.cpp index abdd899da487..fbc025416205 100644 --- a/ELF/LinkerScript.cpp +++ b/ELF/LinkerScript.cpp @@ -116,7 +116,8 @@ void LinkerScript::expandMemoryRegions(uint64_t Size) { if (Ctx->MemRegion) expandMemoryRegion(Ctx->MemRegion, Size, Ctx->MemRegion->Name, Ctx->OutSec->Name); - if (Ctx->LMARegion) + // Only expand the LMARegion if it is different from MemRegion. + if (Ctx->LMARegion && Ctx->MemRegion != Ctx->LMARegion) expandMemoryRegion(Ctx->LMARegion, Size, Ctx->LMARegion->Name, Ctx->OutSec->Name); } @@ -168,7 +169,7 @@ void LinkerScript::addSymbol(SymbolAssignment *Cmd) { // Define a symbol. Symbol *Sym; uint8_t Visibility = Cmd->Hidden ? STV_HIDDEN : STV_DEFAULT; - std::tie(Sym, std::ignore) = Symtab->insert(Cmd->Name, /*Type*/ 0, Visibility, + std::tie(Sym, std::ignore) = Symtab->insert(Cmd->Name, Visibility, /*CanOmitFromDynSym*/ false, /*File*/ nullptr); ExprValue Value = Cmd->Expression(); @@ -201,13 +202,14 @@ static void declareSymbol(SymbolAssignment *Cmd) { // 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, + std::tie(Sym, std::ignore) = Symtab->insert(Cmd->Name, 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; + Sym->ScriptDefined = true; } // This method is used to handle INSERT AFTER statement. Here we rebuild @@ -413,18 +415,16 @@ 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::RelaPlt || S == InX::RelaDyn || - S == InX::RelrDyn) + if (S == In.ShStrTab || S == In.RelaDyn || S == In.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; + if (S == In.GnuHashTab) + In.GnuHashTab = nullptr; + if (S == In.HashTab) + In.HashTab = nullptr; S->Assigned = false; S->Live = false; @@ -700,6 +700,7 @@ uint64_t LinkerScript::advance(uint64_t Size, unsigned Alignment) { } void LinkerScript::output(InputSection *S) { + assert(Ctx->OutSec == S->getParent()); uint64_t Before = advance(0, 1); uint64_t Pos = advance(S->getSize(), S->Alignment); S->OutSecOff = Pos - S->getSize() - Ctx->OutSec->Addr; @@ -750,6 +751,13 @@ MemoryRegion *LinkerScript::findMemoryRegion(OutputSection *Sec) { return nullptr; } +static OutputSection *findFirstSection(PhdrEntry *Load) { + for (OutputSection *Sec : OutputSections) + if (Sec->PtLoad == Load) + return Sec; + return nullptr; +} + // This function assigns offsets to input sections and an output section // for a single sections command (e.g. ".text { *(.text); }"). void LinkerScript::assignOffsets(OutputSection *Sec) { @@ -775,8 +783,11 @@ void LinkerScript::assignOffsets(OutputSection *Sec) { // will set the LMA such that the difference between VMA and LMA for the // section is the same as the preceding output section in the same region // https://sourceware.org/binutils/docs-2.20/ld/Output-Section-LMA.html + // This, however, should only be done by the first "non-header" section + // in the segment. if (PhdrEntry *L = Ctx->OutSec->PtLoad) - L->LMAOffset = Ctx->LMAOffset; + if (Sec == findFirstSection(L)) + L->LMAOffset = Ctx->LMAOffset; // We can call this method multiple times during the creation of // thunks and want to start over calculation each time. @@ -805,21 +816,8 @@ void LinkerScript::assignOffsets(OutputSection *Sec) { // Handle a single input section description command. // It calculates and assigns the offsets for each section and also // updates the output section size. - auto *Cmd = cast<InputSectionDescription>(Base); - for (InputSection *Sec : Cmd->Sections) { - // We tentatively added all synthetic sections at the beginning and - // removed empty ones afterwards (because there is no way to know - // whether they were going be empty or not other than actually running - // linker scripts.) We need to ignore remains of empty sections. - if (auto *S = dyn_cast<SyntheticSection>(Sec)) - if (S->empty()) - continue; - - if (!Sec->Live) - continue; - assert(Ctx->OutSec == Sec->getParent()); + for (InputSection *Sec : cast<InputSectionDescription>(Base)->Sections) output(Sec); - } } } @@ -953,13 +951,6 @@ void LinkerScript::adjustSectionsAfterSorting() { } } -static OutputSection *findFirstSection(PhdrEntry *Load) { - for (OutputSection *Sec : OutputSections) - if (Sec->PtLoad == Load) - return Sec; - 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. diff --git a/ELF/LinkerScript.h b/ELF/LinkerScript.h index 3b790dd4669f..51161981efc8 100644 --- a/ELF/LinkerScript.h +++ b/ELF/LinkerScript.h @@ -30,12 +30,13 @@ namespace lld { namespace elf { class Defined; -class Symbol; -class InputSectionBase; class InputSection; -class OutputSection; class InputSectionBase; +class InputSectionBase; +class OutputSection; class SectionBase; +class Symbol; +class ThunkSection; // This represents an r-value in the linker script. struct ExprValue { @@ -145,7 +146,9 @@ struct MemoryRegion { // Also it may be surrounded with SORT() command, so contains sorting rules. struct SectionPattern { SectionPattern(StringMatcher &&Pat1, StringMatcher &&Pat2) - : ExcludedFilePat(Pat1), SectionPat(Pat2) {} + : ExcludedFilePat(Pat1), SectionPat(Pat2), + SortOuter(SortSectionPolicy::Default), + SortInner(SortSectionPolicy::Default) {} StringMatcher ExcludedFilePat; StringMatcher SectionPat; @@ -153,7 +156,6 @@ struct SectionPattern { SortSectionPolicy SortInner; }; -class ThunkSection; struct InputSectionDescription : BaseCommand { InputSectionDescription(StringRef FilePattern) : BaseCommand(InputSectionKind), FilePat(FilePattern) {} diff --git a/ELF/MapFile.cpp b/ELF/MapFile.cpp index 54fddfb7b299..b0dc6203008d 100644 --- a/ELF/MapFile.cpp +++ b/ELF/MapFile.cpp @@ -126,7 +126,7 @@ static void printEhFrame(raw_ostream &OS, OutputSection *OSec) { }; // Gather section pieces. - for (const CieRecord *Rec : InX::EhFrame->getCieRecords()) { + for (const CieRecord *Rec : In.EhFrame->getCieRecords()) { Add(*Rec->Cie); for (const EhSectionPiece *Fde : Rec->Fdes) Add(*Fde); @@ -163,17 +163,18 @@ void elf::writeMapFile() { OS << right_justify("VMA", W) << ' ' << right_justify("LMA", W) << " Size Align Out In Symbol\n"; + OutputSection* OSec = nullptr; 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); + uint64_t LMA = OSec ? OSec->getLMA() + Cmd->Addr - OSec->getVA(0) : 0; + writeHeader(OS, Cmd->Addr, LMA, Cmd->Size, 1); OS << Cmd->CommandString << '\n'; continue; } - auto *OSec = cast<OutputSection>(Base); + OSec = cast<OutputSection>(Base); writeHeader(OS, OSec->Addr, OSec->getLMA(), OSec->Size, OSec->Alignment); OS << OSec->Name << '\n'; @@ -181,7 +182,7 @@ void elf::writeMapFile() { for (BaseCommand *Base : OSec->SectionCommands) { if (auto *ISD = dyn_cast<InputSectionDescription>(Base)) { for (InputSection *IS : ISD->Sections) { - if (IS == InX::EhFrame) { + if (IS == In.EhFrame) { printEhFrame(OS, OSec); continue; } diff --git a/ELF/MarkLive.cpp b/ELF/MarkLive.cpp index a8371e212c3e..8d0ec091c327 100644 --- a/ELF/MarkLive.cpp +++ b/ELF/MarkLive.cpp @@ -45,7 +45,7 @@ using namespace lld::elf; template <class ELFT> static typename ELFT::uint getAddend(InputSectionBase &Sec, const typename ELFT::Rel &Rel) { - return Target->getImplicitAddend(Sec.Data.begin() + Rel.r_offset, + return Target->getImplicitAddend(Sec.data().begin() + Rel.r_offset, Rel.getType(Config->IsMips64EL)); } @@ -250,9 +250,10 @@ template <class ELFT> static void doGcSections() { if (Sec->Flags & SHF_LINK_ORDER) continue; - if (isReserved<ELFT>(Sec) || Script->shouldKeep(Sec)) + + if (isReserved<ELFT>(Sec) || Script->shouldKeep(Sec)) { Enqueue(Sec, 0); - else if (isValidCIdentifier(Sec->Name)) { + } else if (isValidCIdentifier(Sec->Name)) { CNamedSections[Saver.save("__start_" + Sec->Name)].push_back(Sec); CNamedSections[Saver.save("__stop_" + Sec->Name)].push_back(Sec); } @@ -267,10 +268,16 @@ template <class ELFT> static void doGcSections() { // input sections. This function make some or all of them on // so that they are emitted to the output file. template <class ELFT> void elf::markLive() { - // If -gc-sections is missing, no sections are removed. if (!Config->GcSections) { + // If -gc-sections is missing, no sections are removed. for (InputSectionBase *Sec : InputSections) Sec->Live = true; + + // If a DSO defines a symbol referenced in a regular object, it is needed. + for (Symbol *Sym : Symtab->getSymbols()) + if (auto *S = dyn_cast<SharedSymbol>(Sym)) + if (S->IsUsedInRegularObj && !S->isWeak()) + S->getFile<ELFT>().IsNeeded = true; return; } diff --git a/ELF/Options.td b/ELF/Options.td index 04a4f8ffbe28..e43a21b923d3 100644 --- a/ELF/Options.td +++ b/ELF/Options.td @@ -42,6 +42,12 @@ defm compress_debug_sections: defm defsym: Eq<"defsym", "Define a symbol alias">, MetaVarName<"<symbol>=<value>">; +defm split_stack_adjust_size + : Eq<"split-stack-adjust-size", + "Specify adjustment to stack size when a split-stack function calls a " + "non-split-stack function">, + MetaVarName<"<value>">; + defm library_path: Eq<"library-path", "Add a directory to the library search path">, MetaVarName<"<dir>">; @@ -68,6 +74,10 @@ defm as_needed: B<"as-needed", defm call_graph_ordering_file: Eq<"call-graph-ordering-file", "Layout sections to optimize the given callgraph">; +defm call_graph_profile_sort: B<"call-graph-profile-sort", + "Reorder sections with call graph profile (default)", + "Do not reorder sections with call graph profile">; + // -chroot doesn't have a help text because it is an internal option. def chroot: Separate<["--", "-"], "chroot">; @@ -132,7 +142,7 @@ def error_unresolved_symbols: F<"error-unresolved-symbols">, 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 unreadable", "Mark executable sections readable (default)">; defm export_dynamic: B<"export-dynamic", @@ -315,6 +325,10 @@ defm threads: B<"threads", "Run the linker multi-threaded (default)", "Do not run the linker multi-threaded">; +defm toc_optimize : B<"toc-optimize", + "(PowerPC64) Enable TOC related optimizations (default)", + "(PowerPC64) Disable TOC related optimizations">; + def trace: F<"trace">, HelpText<"Print the names of the input files">; defm trace_symbol: Eq<"trace-symbol", "Trace references to symbols">; @@ -348,6 +362,10 @@ defm warn_common: B<"warn-common", "Warn about duplicate common symbols", "Do not warn about duplicate common symbols (default)">; +defm warn_ifunc_textrel: B<"warn-ifunc-textrel", + "Warn about using ifunc symbols with text relocations", + "Do not warn about using ifunc symbols with text relocations (default)">; + 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">; @@ -440,6 +458,7 @@ def: F<"plugin-opt=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 plugin_opt_emit_llvm: F<"plugin-opt=emit-llvm">; 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=">; diff --git a/ELF/OutputSections.cpp b/ELF/OutputSections.cpp index 8253b18b486c..c1442c078736 100644 --- a/ELF/OutputSections.cpp +++ b/ELF/OutputSections.cpp @@ -25,6 +25,7 @@ using namespace llvm; using namespace llvm::dwarf; using namespace llvm::object; +using namespace llvm::support::endian; using namespace llvm::ELF; using namespace lld; @@ -32,7 +33,6 @@ using namespace lld::elf; uint8_t Out::First; PhdrEntry *Out::TlsPhdr; -OutputSection *Out::DebugInfo; OutputSection *Out::ElfHeader; OutputSection *Out::ProgramHeaders; OutputSection *Out::PreinitArray; @@ -95,7 +95,7 @@ void OutputSection::addSection(InputSection *IS) { Flags = IS->Flags; } else { // Otherwise, check if new type or flags are compatible with existing ones. - unsigned Mask = SHF_ALLOC | SHF_TLS | SHF_LINK_ORDER; + unsigned Mask = 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 + @@ -171,11 +171,12 @@ void OutputSection::sort(llvm::function_ref<int(InputSectionBase *S)> Order) { // Fill [Buf, Buf + Size) with Filler. // This is used for linker script "=fillexp" command. -static void fill(uint8_t *Buf, size_t Size, uint32_t Filler) { +static void fill(uint8_t *Buf, size_t Size, + const std::array<uint8_t, 4> &Filler) { size_t I = 0; for (; I + 4 < Size; I += 4) - memcpy(Buf + I, &Filler, 4); - memcpy(Buf + I, &Filler, Size - I); + memcpy(Buf + I, Filler.data(), 4); + memcpy(Buf + I, Filler.data(), Size - I); } // Compress section contents if this section contains debug info. @@ -236,8 +237,9 @@ template <class ELFT> void OutputSection::writeTo(uint8_t *Buf) { // Write leading padding. std::vector<InputSection *> Sections = getInputSections(this); - uint32_t Filler = getFiller(); - if (Filler) + std::array<uint8_t, 4> Filler = getFiller(); + bool NonZeroFiller = read32(Filler.data()) != 0; + if (NonZeroFiller) fill(Buf, Sections.empty() ? Size : Sections[0]->OutSecOff, Filler); parallelForEachN(0, Sections.size(), [&](size_t I) { @@ -245,7 +247,7 @@ template <class ELFT> void OutputSection::writeTo(uint8_t *Buf) { IS->writeTo<ELFT>(Buf); // Fill gaps between sections. - if (Filler) { + if (NonZeroFiller) { uint8_t *Start = Buf + IS->OutSecOff + IS->getSize(); uint8_t *End; if (I + 1 == Sections.size()) @@ -270,13 +272,13 @@ static void finalizeShtGroup(OutputSection *OS, // sh_link field for SHT_GROUP sections should contain the section index of // the symbol table. - OS->Link = InX::SymTab->getParent()->SectionIndex; + OS->Link = In.SymTab->getParent()->SectionIndex; // sh_info then contain index of an entry in symbol table section which // provides signature of the section group. ObjFile<ELFT> *Obj = Section->getFile<ELFT>(); ArrayRef<Symbol *> Symbols = Obj->getSymbols(); - OS->Info = InX::SymTab->getSymbolIndex(Symbols[Section->Info]); + OS->Info = In.SymTab->getSymbolIndex(Symbols[Section->Info]); } template <class ELFT> void OutputSection::finalize() { @@ -308,7 +310,7 @@ template <class ELFT> void OutputSection::finalize() { if (isa<SyntheticSection>(First)) return; - Link = InX::SymTab->getParent()->SectionIndex; + Link = In.SymTab->getParent()->SectionIndex; // sh_info for SHT_REL[A] sections should contain the section header index of // the section to which the relocation applies. InputSectionBase *S = First->getRelocatedSection(); @@ -406,12 +408,12 @@ void OutputSection::sortInitFini() { sort([](InputSectionBase *S) { return getPriority(S->Name); }); } -uint32_t OutputSection::getFiller() { +std::array<uint8_t, 4> OutputSection::getFiller() { if (Filler) return *Filler; if (Flags & SHF_EXECINSTR) return Target->TrapInstr; - return 0; + return {0, 0, 0, 0}; } template void OutputSection::writeHeaderTo<ELF32LE>(ELF32LE::Shdr *Shdr); diff --git a/ELF/OutputSections.h b/ELF/OutputSections.h index efb6aabe9743..113bf6836926 100644 --- a/ELF/OutputSections.h +++ b/ELF/OutputSections.h @@ -17,6 +17,7 @@ #include "lld/Common/LLVM.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Object/ELF.h" +#include <array> namespace lld { namespace elf { @@ -94,7 +95,7 @@ public: Expr SubalignExpr; std::vector<BaseCommand *> SectionCommands; std::vector<StringRef> Phdrs; - llvm::Optional<uint32_t> Filler; + llvm::Optional<std::array<uint8_t, 4>> Filler; ConstraintKind Constraint = ConstraintKind::NoConstraint; std::string Location; std::string MemoryRegionName; @@ -117,7 +118,7 @@ private: std::vector<uint8_t> ZDebugHeader; llvm::SmallVector<char, 1> CompressedData; - uint32_t getFiller(); + std::array<uint8_t, 4> getFiller(); }; int getPriority(StringRef S); @@ -130,7 +131,6 @@ std::vector<InputSection *> getInputSections(OutputSection* OS); struct Out { static uint8_t First; static PhdrEntry *TlsPhdr; - static OutputSection *DebugInfo; static OutputSection *ElfHeader; static OutputSection *ProgramHeaders; static OutputSection *PreinitArray; diff --git a/ELF/Relocations.cpp b/ELF/Relocations.cpp index 467219ad0542..812468896f0d 100644 --- a/ELF/Relocations.cpp +++ b/ELF/Relocations.cpp @@ -50,6 +50,7 @@ #include "SyntheticSections.h" #include "Target.h" #include "Thunks.h" +#include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "lld/Common/Strings.h" #include "llvm/ADT/SmallSet.h" @@ -65,6 +66,14 @@ using namespace llvm::support::endian; using namespace lld; using namespace lld::elf; +static Optional<std::string> getLinkerScriptLocation(const Symbol &Sym) { + for (BaseCommand *Base : Script->SectionCommands) + if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) + if (Cmd->Sym == &Sym) + return Cmd->Location; + return None; +} + // Construct a message in the following format. // // >>> defined in /home/alice/src/foo.o @@ -72,8 +81,13 @@ using namespace lld::elf; // >>> /home/alice/src/bar.o:(.text+0x1) static std::string getLocation(InputSectionBase &S, const Symbol &Sym, uint64_t Off) { - std::string Msg = - "\n>>> defined in " + toString(Sym.File) + "\n>>> referenced by "; + std::string Msg = "\n>>> defined in "; + if (Sym.File) + Msg += toString(Sym.File); + else if (Optional<std::string> Loc = getLinkerScriptLocation(Sym)) + Msg += *Loc; + + Msg += "\n>>> referenced by "; std::string Src = S.getSrcMsg(Sym, Off); if (!Src.empty()) Msg += Src + "\n>>> "; @@ -90,12 +104,12 @@ static unsigned handleMipsTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C, uint64_t Offset, int64_t Addend, RelExpr Expr) { if (Expr == R_MIPS_TLSLD) { - InX::MipsGot->addTlsIndex(*C.File); + In.MipsGot->addTlsIndex(*C.File); C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return 1; } if (Expr == R_MIPS_TLSGD) { - InX::MipsGot->addDynTlsEntry(*C.File, Sym); + In.MipsGot->addDynTlsEntry(*C.File, Sym); C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return 1; } @@ -128,17 +142,17 @@ 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, Dest); + In.RelaDyn->addReloc(Type, In.Got, Off, Dest); else - InX::Got->Relocations.push_back({R_ABS, Type, Off, 0, Dest}); + In.Got->Relocations.push_back({R_ABS, Type, Off, 0, Dest}); }; // Local Dynamic is for access to module local TLS variables, while still // being suitable for being dynamically loaded via dlopen. // GOT[e0] is the module index, with a special value of 0 for the current // module. GOT[e1] is unused. There only needs to be one module index entry. - if (Expr == R_TLSLD_PC && InX::Got->addTlsIndex()) { - AddTlsReloc(InX::Got->getTlsIndexOff(), Target->TlsModuleIndexRel, + if (Expr == R_TLSLD_PC && In.Got->addTlsIndex()) { + AddTlsReloc(In.Got->getTlsIndexOff(), Target->TlsModuleIndexRel, NeedDynId ? nullptr : &Sym, NeedDynId); C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return 1; @@ -148,8 +162,8 @@ static unsigned handleARMTlsRelocation(RelType Type, Symbol &Sym, // the module index and offset of symbol in TLS block we can fill these in // using static GOT relocations. if (Expr == R_TLSGD_PC) { - if (InX::Got->addDynTlsEntry(Sym)) { - uint64_t Off = InX::Got->getGlobalDynOffset(Sym); + if (In.Got->addDynTlsEntry(Sym)) { + uint64_t Off = In.Got->getGlobalDynOffset(Sym); AddTlsReloc(Off, Target->TlsModuleIndexRel, &Sym, NeedDynId); AddTlsReloc(Off + Config->Wordsize, Target->TlsOffsetRel, &Sym, NeedDynOff); @@ -165,9 +179,6 @@ template <class ELFT> static unsigned handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C, typename ELFT::uint Offset, int64_t Addend, RelExpr Expr) { - if (!(C.Flags & SHF_ALLOC)) - return 0; - if (!Sym.isTls()) return 0; @@ -176,12 +187,12 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C, if (Config->EMachine == EM_MIPS) return handleMipsTlsRelocation(Type, Sym, C, Offset, Addend, Expr); - if (isRelExprOneOf<R_TLSDESC, R_TLSDESC_PAGE, R_TLSDESC_CALL>(Expr) && + if (isRelExprOneOf<R_TLSDESC, R_AARCH64_TLSDESC_PAGE, R_TLSDESC_CALL>(Expr) && Config->Shared) { - if (InX::Got->addDynTlsEntry(Sym)) { - uint64_t Off = InX::Got->getGlobalDynOffset(Sym); - InX::RelaDyn->addReloc( - {Target->TlsDescRel, InX::Got, Off, !Sym.IsPreemptible, &Sym, 0}); + if (In.Got->addDynTlsEntry(Sym)) { + uint64_t Off = In.Got->getGlobalDynOffset(Sym); + In.RelaDyn->addReloc( + {Target->TlsDescRel, In.Got, Off, !Sym.IsPreemptible, &Sym, 0}); } if (Expr != R_TLSDESC_CALL) C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); @@ -199,9 +210,9 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C, } if (Expr == R_TLSLD_HINT) return 1; - if (InX::Got->addTlsIndex()) - InX::RelaDyn->addReloc(Target->TlsModuleIndexRel, InX::Got, - InX::Got->getTlsIndexOff(), nullptr); + if (In.Got->addTlsIndex()) + In.RelaDyn->addReloc(Target->TlsModuleIndexRel, In.Got, + In.Got->getTlsIndexOff(), nullptr); C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return 1; } @@ -223,29 +234,29 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C, return 1; } if (!Sym.isInGot()) { - InX::Got->addEntry(Sym); + In.Got->addEntry(Sym); uint64_t Off = Sym.getGotOffset(); - InX::Got->Relocations.push_back({R_ABS, Target->TlsOffsetRel, Off, 0, &Sym}); + In.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 (isRelExprOneOf<R_TLSDESC, R_AARCH64_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, &Sym); + if (In.Got->addDynTlsEntry(Sym)) { + uint64_t Off = In.Got->getGlobalDynOffset(Sym); + In.RelaDyn->addReloc(Target->TlsModuleIndexRel, In.Got, Off, &Sym); // If the symbol is preemptible we need the dynamic linker to write // the offset too. uint64_t OffsetOff = Off + Config->Wordsize; if (Sym.IsPreemptible) - InX::RelaDyn->addReloc(Target->TlsOffsetRel, InX::Got, OffsetOff, - &Sym); + In.RelaDyn->addReloc(Target->TlsOffsetRel, In.Got, OffsetOff, &Sym); else - InX::Got->Relocations.push_back( + In.Got->Relocations.push_back( {R_ABS, Target->TlsOffsetRel, OffsetOff, 0, &Sym}); } C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); @@ -259,9 +270,9 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C, {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_IE), Type, Offset, Addend, &Sym}); if (!Sym.isInGot()) { - InX::Got->addEntry(Sym); - InX::RelaDyn->addReloc(Target->TlsGotRel, InX::Got, Sym.getGotOffset(), - &Sym); + In.Got->addEntry(Sym); + In.RelaDyn->addReloc(Target->TlsGotRel, In.Got, Sym.getGotOffset(), + &Sym); } } else { C.Relocations.push_back( @@ -273,13 +284,14 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C, // Initial-Exec relocs can be relaxed to Local-Exec if the symbol is locally // defined. - if (isRelExprOneOf<R_GOT, R_GOT_FROM_END, R_GOT_PC, R_GOT_PAGE_PC>(Expr) && + if (isRelExprOneOf<R_GOT, R_GOT_FROM_END, R_GOT_PC, R_AARCH64_GOT_PAGE_PC, + R_GOT_OFF, R_TLSIE_HINT>(Expr) && !Config->Shared && !Sym.IsPreemptible) { C.Relocations.push_back({R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Sym}); return 1; } - if (Expr == R_TLSDESC_CALL) + if (Expr == R_TLSIE_HINT) return 1; return 0; } @@ -325,23 +337,25 @@ 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_CALL_PLT, R_PLT, R_PLT_PAGE_PC>(Expr); + return isRelExprOneOf<R_PLT_PC, R_PPC_CALL_PLT, R_PLT, R_AARCH64_PLT_PAGE_PC, + R_GOT_PLT, R_AARCH64_GOT_PAGE_PC_PLT>(Expr); } // Returns true if Expr refers a GOT entry. Note that this function // returns false for TLS variables even though they need GOT, because // TLS variables uses GOT differently than the regular variables. static bool needsGot(RelExpr Expr) { - return isRelExprOneOf<R_GOT, R_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOT_OFF, - R_MIPS_GOT_OFF32, R_GOT_PAGE_PC, R_GOT_PC, - R_GOT_FROM_END>(Expr); + return isRelExprOneOf<R_GOT, R_GOT_OFF, R_HEXAGON_GOT, R_MIPS_GOT_LOCAL_PAGE, + R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_AARCH64_GOT_PAGE_PC, + R_AARCH64_GOT_PAGE_PC_PLT, R_GOT_PC, R_GOT_FROM_END, + R_GOT_PLT>(Expr); } // True if this expression is of the form Sym - X, where X is a position in the // 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_PPC_CALL, R_PPC_CALL_PLT, R_PAGE_PC, + R_PPC_CALL, R_PPC_CALL_PLT, R_AARCH64_PAGE_PC, R_RELAX_GOT_PC>(Expr); } @@ -357,18 +371,19 @@ 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_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)) + if (isRelExprOneOf<R_GOT_FROM_END, R_GOT_OFF, R_HEXAGON_GOT, 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_AARCH64_GOT_PAGE_PC, R_AARCH64_GOT_PAGE_PC_PLT, 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_AARCH64_TLSDESC_PAGE, R_HINT, + R_TLSLD_HINT, R_TLSIE_HINT>(E)) return true; // These never do, except if the entire file is position dependent or if // only the low bits are used. - if (E == R_GOT || E == R_PLT || E == R_TLSDESC) + if (E == R_GOT || E == R_GOT_PLT || E == R_PLT || E == R_TLSDESC) return Target->usesOnlyLowPageBits(Type) || !Config->Pic; if (Sym.IsPreemptible) @@ -414,10 +429,14 @@ static RelExpr toPlt(RelExpr Expr) { return R_PPC_CALL_PLT; case R_PC: return R_PLT_PC; - case R_PAGE_PC: - return R_PLT_PAGE_PC; + case R_AARCH64_PAGE_PC: + return R_AARCH64_PLT_PAGE_PC; + case R_AARCH64_GOT_PAGE_PC: + return R_AARCH64_GOT_PAGE_PC_PLT; case R_ABS: return R_PLT; + case R_GOT: + return R_GOT_PLT; default: return Expr; } @@ -466,7 +485,7 @@ static SmallSet<SharedSymbol *, 4> getSymbolsAt(SharedSymbol &SS) { 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.getType() == STT_TLS || S.st_value != SS.Value) continue; StringRef Name = check(S.getName(File.getStringTable())); Symbol *Sym = Symtab->find(Name); @@ -489,6 +508,7 @@ static void replaceWithDefined(Symbol &Sym, SectionBase *Sec, uint64_t Value, Sym.PltIndex = Old.PltIndex; Sym.GotIndex = Old.GotIndex; Sym.VerdefIndex = Old.VerdefIndex; + Sym.PPC64BranchltIndex = Old.PPC64BranchltIndex; Sym.IsPreemptible = true; Sym.ExportDynamic = true; Sym.IsUsedInRegularObj = true; @@ -549,9 +569,9 @@ template <class ELFT> static void addCopyRelSymbol(SharedSymbol &SS) { BssSection *Sec = make<BssSection>(IsReadOnly ? ".bss.rel.ro" : ".bss", SymSize, SS.Alignment); if (IsReadOnly) - InX::BssRelRo->getParent()->addSection(Sec); + In.BssRelRo->getParent()->addSection(Sec); else - InX::Bss->getParent()->addSection(Sec); + In.Bss->getParent()->addSection(Sec); // 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 @@ -559,7 +579,7 @@ template <class ELFT> static void addCopyRelSymbol(SharedSymbol &SS) { for (SharedSymbol *Sym : getSymbolsAt<ELFT>(SS)) replaceWithDefined(*Sym, Sec, 0, Sym->Size); - InX::RelaDyn->addReloc(Target->CopyRel, Sec, 0, &SS); + In.RelaDyn->addReloc(Target->CopyRel, Sec, 0, &SS); } // MIPS has an odd notion of "paired" relocations to calculate addends. @@ -583,7 +603,7 @@ static int64_t computeMipsAddend(const RelTy &Rel, const RelTy *End, if (PairTy == R_MIPS_NONE) return 0; - const uint8_t *Buf = Sec.Data.data(); + const uint8_t *Buf = Sec.data().data(); uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL); // To make things worse, paired relocations might not be contiguous in @@ -611,7 +631,7 @@ static int64_t computeAddend(const RelTy &Rel, const RelTy *End, if (RelTy::IsRela) { Addend = getAddend<ELFT>(Rel); } else { - const uint8_t *Buf = Sec.Data.data(); + const uint8_t *Buf = Sec.data().data(); Addend = Target->getImplicitAddend(Buf + Rel.r_offset, Type); } @@ -627,9 +647,6 @@ static int64_t computeAddend(const RelTy &Rel, const RelTy *End, // Returns true if this function printed out an error message. static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec, uint64_t Offset) { - if (Config->UnresolvedSymbols == UnresolvedPolicy::IgnoreAll) - return false; - if (Sym.isLocal() || !Sym.isUndefined() || Sym.isWeak()) return false; @@ -646,6 +663,10 @@ static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec, Msg += Src + "\n>>> "; Msg += Sec.getObjMsg(Offset); + if (Sym.getName().startswith("_ZTV")) + Msg += "\nthe vtable symbol may be undefined because the class is missing " + "its key function (see https://lld.llvm.org/missingkeyfunction)"; + if ((Config->UnresolvedSymbols == UnresolvedPolicy::Warn && CanBeExternal) || Config->NoinhibitExec) { warn(Msg); @@ -700,7 +721,7 @@ public: while (I != Pieces.size() && Pieces[I].InputOff + Pieces[I].Size <= Off) ++I; if (I == Pieces.size()) - return Off; + fatal(".eh_frame: relocation is not in any piece"); // Pieces must be contiguous, so there must be no holes in between. assert(Pieces[I].InputOff <= Off && "Relocation not in any piece"); @@ -726,13 +747,13 @@ static void addRelativeReloc(InputSectionBase *IS, uint64_t OffsetInSec, // 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) { + if (In.RelrDyn && IS->Alignment >= 2 && OffsetInSec % 2 == 0) { IS->Relocations.push_back({Expr, Type, OffsetInSec, Addend, Sym}); - InX::RelrDyn->Relocs.push_back({IS, OffsetInSec}); + In.RelrDyn->Relocs.push_back({IS, OffsetInSec}); return; } - InX::RelaDyn->addReloc(Target->RelativeRel, IS, OffsetInSec, Sym, Addend, - Expr, Type); + In.RelaDyn->addReloc(Target->RelativeRel, IS, OffsetInSec, Sym, Addend, Expr, + Type); } template <class ELFT, class GotPltSection> @@ -745,9 +766,16 @@ static void addPltEntry(PltSection *Plt, GotPltSection *GotPlt, } template <class ELFT> static void addGotEntry(Symbol &Sym) { - InX::Got->addEntry(Sym); + In.Got->addEntry(Sym); + + RelExpr Expr; + if (Sym.isTls()) + Expr = R_TLS; + else if (Sym.isGnuIFunc()) + Expr = R_PLT; + else + Expr = R_ABS; - RelExpr Expr = Sym.isTls() ? R_TLS : R_ABS; uint64_t Off = Sym.getGotOffset(); // If a GOT slot value can be calculated at link-time, which is now, @@ -760,19 +788,19 @@ template <class ELFT> static void addGotEntry(Symbol &Sym) { bool IsLinkTimeConstant = !Sym.IsPreemptible && (!Config->Pic || isAbsolute(Sym)); if (IsLinkTimeConstant) { - InX::Got->Relocations.push_back({Expr, Target->GotRel, Off, 0, &Sym}); + In.Got->Relocations.push_back({Expr, Target->GotRel, Off, 0, &Sym}); return; } // Otherwise, we emit a dynamic relocation to .rel[a].dyn so that // the GOT slot will be fixed at load-time. if (!Sym.isTls() && !Sym.IsPreemptible && Config->Pic && !isAbsolute(Sym)) { - addRelativeReloc(InX::Got, Off, &Sym, 0, R_ABS, Target->GotRel); + addRelativeReloc(In.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); + In.RelaDyn->addReloc(Sym.isTls() ? Target->TlsGotRel : Target->GotRel, In.Got, + Off, &Sym, 0, Sym.IsPreemptible ? R_ADDEND : R_ABS, + Target->GotRel); } // Return true if we can define a symbol in the executable that @@ -825,7 +853,7 @@ static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type, 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); + In.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 @@ -843,7 +871,7 @@ static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type, // 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(*Sec.File, Sym, Addend, Expr); + In.MipsGot->addEntry(*Sec.File, Sym, Addend, Expr); return; } } @@ -930,10 +958,9 @@ static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type, "' cannot be preempted; recompile with -fPIE" + getLocation(Sec, Sym, Offset)); if (!Sym.isInPlt()) - addPltEntry<ELFT>(InX::Plt, InX::GotPlt, InX::RelaPlt, Target->PltRel, - Sym); + addPltEntry<ELFT>(In.Plt, In.GotPlt, In.RelaPlt, Target->PltRel, Sym); if (!Sym.isDefined()) - replaceWithDefined(Sym, InX::Plt, Sym.getPltOffset(), 0); + replaceWithDefined(Sym, In.Plt, getPltEntryOffset(Sym.PltIndex), 0); Sym.NeedsPltAddr = true; Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return; @@ -967,7 +994,7 @@ static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I, if (maybeReportUndefined(Sym, Sec, Rel.r_offset)) return; - const uint8_t *RelocatedAddr = Sec.Data.begin() + Rel.r_offset; + 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. @@ -985,18 +1012,28 @@ static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I, // 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()) + if (Sym.isGnuIFunc()) { + if (!Config->ZText && Config->WarnIfuncTextrel) { + warn("using ifunc symbols when text relocations are allowed may produce " + "a binary that will segfault, if the object file is linked with " + "old version of glibc (glibc 2.28 and earlier). If this applies to " + "you, consider recompiling the object files without -fPIC and " + "without -Wl,-z,notext option. Use -no-warn-ifunc-textrel to " + "turn off this warning." + + getLocation(Sec, Sym, Offset)); + } Expr = toPlt(Expr); - else if (!Sym.IsPreemptible && Expr == R_GOT_PC && !isAbsoluteValue(Sym)) + } else if (!Sym.IsPreemptible && Expr == R_GOT_PC && !isAbsoluteValue(Sym)) { Expr = Target->adjustRelaxExpr(Type, RelocatedAddr, Expr); - else if (!Sym.IsPreemptible) + } 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; + In.Got->HasGotOffRel = true; // Read an addend. int64_t Addend = computeAddend<ELFT>(Rel, End, Sec, Expr, Sym.isLocal()); @@ -1012,11 +1049,10 @@ static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I, // 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, + addPltEntry<ELFT>(In.Iplt, In.IgotPlt, In.RelaIplt, Target->IRelativeRel, Sym); + else + addPltEntry<ELFT>(In.Plt, In.GotPlt, In.RelaPlt, Target->PltRel, Sym); } // Create a GOT slot if a relocation needs GOT. @@ -1029,7 +1065,7 @@ static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I, // 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); + In.MipsGot->addEntry(*Sec.File, Sym, Addend, Expr); } else if (!Sym.isInGot()) { addGotEntry<ELFT>(Sym); } @@ -1047,6 +1083,11 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) { for (auto I = Rels.begin(), End = Rels.end(); I != End;) scanReloc<ELFT>(Sec, GetOffset, I, End); + + // Sort relocations by offset to binary search for R_RISCV_PCREL_HI20 + if (Config->EMachine == EM_RISCV) + std::stable_sort(Sec.Relocations.begin(), Sec.Relocations.end(), + RelocationOffsetComparator{}); } template <class ELFT> void elf::scanRelocations(InputSectionBase &S) { @@ -1056,6 +1097,43 @@ template <class ELFT> void elf::scanRelocations(InputSectionBase &S) { scanRelocs<ELFT>(S, S.rels<ELFT>()); } +static bool mergeCmp(const InputSection *A, const InputSection *B) { + // std::merge requires a strict weak ordering. + if (A->OutSecOff < B->OutSecOff) + return true; + + if (A->OutSecOff == B->OutSecOff) { + auto *TA = dyn_cast<ThunkSection>(A); + auto *TB = dyn_cast<ThunkSection>(B); + + // Check if Thunk is immediately before any specific Target + // InputSection for example Mips LA25 Thunks. + if (TA && TA->getTargetInputSection() == B) + return true; + + // Place Thunk Sections without specific targets before + // non-Thunk Sections. + if (TA && !TB && !TA->getTargetInputSection()) + return true; + } + + return false; +} + +// Call Fn on every executable InputSection accessed via the linker script +// InputSectionDescription::Sections. +static void forEachInputSectionDescription( + ArrayRef<OutputSection *> OutputSections, + llvm::function_ref<void(OutputSection *, InputSectionDescription *)> Fn) { + for (OutputSection *OS : OutputSections) { + if (!(OS->Flags & SHF_ALLOC) || !(OS->Flags & SHF_EXECINSTR)) + continue; + for (BaseCommand *BC : OS->SectionCommands) + if (auto *ISD = dyn_cast<InputSectionDescription>(BC)) + Fn(OS, ISD); + } +} + // Thunk Implementation // // Thunks (sometimes called stubs, veneers or branch islands) are small pieces @@ -1158,6 +1236,7 @@ void ThunkCreator::mergeThunks(ArrayRef<OutputSection *> OutputSections) { [](const std::pair<ThunkSection *, uint32_t> &TS) { return TS.first->getSize() == 0; }); + // ISD->ThunkSections contains all created ThunkSections, including // those inserted in previous passes. Extract the Thunks created this // pass and order them in ascending OutSecOff. @@ -1173,27 +1252,11 @@ void ThunkCreator::mergeThunks(ArrayRef<OutputSection *> OutputSections) { // Merge sorted vectors of Thunks and InputSections by OutSecOff std::vector<InputSection *> Tmp; Tmp.reserve(ISD->Sections.size() + NewThunks.size()); - auto MergeCmp = [](const InputSection *A, const InputSection *B) { - // std::merge requires a strict weak ordering. - if (A->OutSecOff < B->OutSecOff) - return true; - if (A->OutSecOff == B->OutSecOff) { - auto *TA = dyn_cast<ThunkSection>(A); - auto *TB = dyn_cast<ThunkSection>(B); - // Check if Thunk is immediately before any specific Target - // InputSection for example Mips LA25 Thunks. - if (TA && TA->getTargetInputSection() == B) - return true; - if (TA && !TB && !TA->getTargetInputSection()) - // Place Thunk Sections without specific targets before - // non-Thunk Sections. - return true; - } - return false; - }; + std::merge(ISD->Sections.begin(), ISD->Sections.end(), NewThunks.begin(), NewThunks.end(), std::back_inserter(Tmp), - MergeCmp); + mergeCmp); + ISD->Sections = std::move(Tmp); }); } @@ -1237,20 +1300,23 @@ ThunkSection *ThunkCreator::getISThunkSec(InputSection *IS) { // Find InputSectionRange within Target Output Section (TOS) that the // InputSection (IS) that we need to precede is in. OutputSection *TOS = IS->getParent(); - for (BaseCommand *BC : TOS->SectionCommands) - if (auto *ISD = dyn_cast<InputSectionDescription>(BC)) { - if (ISD->Sections.empty()) - continue; - InputSection *first = ISD->Sections.front(); - InputSection *last = ISD->Sections.back(); - if (IS->OutSecOff >= first->OutSecOff && - IS->OutSecOff <= last->OutSecOff) { - TS = addThunkSection(TOS, ISD, IS->OutSecOff); - ThunkedSections[IS] = TS; - break; - } - } - return TS; + for (BaseCommand *BC : TOS->SectionCommands) { + auto *ISD = dyn_cast<InputSectionDescription>(BC); + if (!ISD || ISD->Sections.empty()) + continue; + + InputSection *First = ISD->Sections.front(); + InputSection *Last = ISD->Sections.back(); + + if (IS->OutSecOff < First->OutSecOff || Last->OutSecOff < IS->OutSecOff) + continue; + + TS = addThunkSection(TOS, ISD, IS->OutSecOff); + ThunkedSections[IS] = TS; + return TS; + } + + return nullptr; } // Create one or more ThunkSections per OS that can be used to place Thunks. @@ -1271,26 +1337,29 @@ ThunkSection *ThunkCreator::getISThunkSec(InputSection *IS) { // allow for the creation of a short thunk. void ThunkCreator::createInitialThunkSections( ArrayRef<OutputSection *> OutputSections) { + uint32_t ThunkSectionSpacing = Target->getThunkSectionSpacing(); + 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; + if (ISDEnd - ISDBegin > ThunkSectionSpacing * 2) + LastThunkLowerBound = ISDEnd - ThunkSectionSpacing; uint32_t ISLimit; uint32_t PrevISLimit = ISDBegin; - uint32_t ThunkUpperBound = ISDBegin + Target->ThunkSectionSpacing; + uint32_t ThunkUpperBound = ISDBegin + ThunkSectionSpacing; for (const InputSection *IS : ISD->Sections) { ISLimit = IS->OutSecOff + IS->getSize(); if (ISLimit > ThunkUpperBound) { addThunkSection(OS, ISD, PrevISLimit); - ThunkUpperBound = PrevISLimit + Target->ThunkSectionSpacing; + ThunkUpperBound = PrevISLimit + ThunkSectionSpacing; } if (ISLimit > LastThunkLowerBound) break; @@ -1304,13 +1373,14 @@ ThunkSection *ThunkCreator::addThunkSection(OutputSection *OS, InputSectionDescription *ISD, uint64_t Off) { auto *TS = make<ThunkSection>(OS, Off); - ISD->ThunkSections.push_back(std::make_pair(TS, Pass)); + ISD->ThunkSections.push_back({TS, Pass}); return TS; } std::pair<Thunk *, bool> ThunkCreator::getThunk(Symbol &Sym, RelType Type, uint64_t Src) { 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)) @@ -1318,40 +1388,28 @@ std::pair<Thunk *, bool> ThunkCreator::getThunk(Symbol &Sym, RelType Type, 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); + for (Thunk *T : *ThunkVec) + if (T->isCompatibleWith(Type) && + Target->inBranchRange(Type, Src, T->getThunkTargetSym()->getVA())) + return std::make_pair(T, false); + // No existing compatible Thunk in range, create a new one Thunk *T = addThunk(Type, Sym); ThunkVec->push_back(T); return std::make_pair(T, true); } -// Call Fn on every executable InputSection accessed via the linker script -// InputSectionDescription::Sections. -void ThunkCreator::forEachInputSectionDescription( - ArrayRef<OutputSection *> OutputSections, - llvm::function_ref<void(OutputSection *, InputSectionDescription *)> Fn) { - for (OutputSection *OS : OutputSections) { - if (!(OS->Flags & SHF_ALLOC) || !(OS->Flags & SHF_EXECINSTR)) - continue; - for (BaseCommand *BC : OS->SectionCommands) - if (auto *ISD = dyn_cast<InputSectionDescription>(BC)) - Fn(OS, ISD); - } -} - // Return true if the relocation target is an in range Thunk. // Return false if the relocation is not to a Thunk. If the relocation target // was originally to a Thunk, but is no longer in range we revert the // relocation back to its original non-Thunk target. bool ThunkCreator::normalizeExistingThunk(Relocation &Rel, uint64_t Src) { - if (Thunk *ET = Thunks.lookup(Rel.Sym)) { + if (Thunk *T = Thunks.lookup(Rel.Sym)) { if (Target->inBranchRange(Rel.Type, Src, Rel.Sym->getVA())) return true; - Rel.Sym = &ET->Destination; + Rel.Sym = &T->Destination; if (Rel.Sym->isInPlt()) Rel.Expr = toPlt(Rel.Expr); } @@ -1385,11 +1443,13 @@ bool ThunkCreator::normalizeExistingThunk(Relocation &Rel, uint64_t Src) { // relocation out of range error. bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) { bool AddressesChanged = false; - if (Pass == 0 && Target->ThunkSectionSpacing) + + if (Pass == 0 && Target->getThunkSectionSpacing()) createInitialThunkSections(OutputSections); - else if (Pass == 10) - // With Thunk Size much smaller than branch range we expect to - // converge quickly; if we get to 10 something has gone wrong. + + // With Thunk Size much smaller than branch range we expect to + // converge quickly; if we get to 10 something has gone wrong. + if (Pass == 10) fatal("thunk creation not converged"); // Create all the Thunks and insert them into synthetic ThunkSections. The @@ -1412,9 +1472,11 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) { if (!Target->needsThunk(Rel.Expr, Rel.Type, IS->File, Src, *Rel.Sym)) continue; + Thunk *T; bool IsNew; std::tie(T, IsNew) = getThunk(*Rel.Sym, Rel.Type, Src); + if (IsNew) { // Find or create a ThunkSection for the new Thunk ThunkSection *TS; @@ -1425,13 +1487,16 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) { TS->addThunk(T); Thunks[T->getThunkTargetSym()] = T; } + // Redirect relocation to Thunk, we never go via the PLT to a Thunk 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(); diff --git a/ELF/Relocations.h b/ELF/Relocations.h index a4125111c4fe..d00e68bd36e6 100644 --- a/ELF/Relocations.h +++ b/ELF/Relocations.h @@ -33,16 +33,28 @@ enum RelExpr { R_INVALID, R_ABS, R_ADDEND, + R_AARCH64_GOT_PAGE_PC, + // The expression is used for IFUNC support. Describes PC-relative + // address of the memory page of GOT entry. This entry is used for + // a redirection to IPLT. + R_AARCH64_GOT_PAGE_PC_PLT, + R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC, + R_AARCH64_PAGE_PC, + R_AARCH64_PLT_PAGE_PC, + R_AARCH64_TLSDESC_PAGE, R_ARM_SBREL, R_GOT, + // The expression is used for IFUNC support. Evaluates to GOT entry, + // containing redirection to the IPLT. + R_GOT_PLT, R_GOTONLY_PC, R_GOTONLY_PC_FROM_END, R_GOTREL, R_GOTREL_FROM_END, R_GOT_FROM_E |