diff --git a/CMakeLists.txt b/CMakeLists.txt index 7fcb1a748ffc..e2ab0e35f1ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -221,3 +221,4 @@ endif() add_subdirectory(docs) add_subdirectory(COFF) add_subdirectory(ELF) + diff --git a/COFF/Chunks.h b/COFF/Chunks.h index 44d7f31afc67..f7412517765c 100644 --- a/COFF/Chunks.h +++ b/COFF/Chunks.h @@ -201,7 +201,7 @@ private: // Used for ICF (Identical COMDAT Folding) void replace(SectionChunk *Other); - uint32_t Color[2] = {0, 0}; + uint32_t Class[2] = {0, 0}; // Sym points to a section symbol if this is a COMDAT chunk. DefinedRegular *Sym = nullptr; diff --git a/COFF/ICF.cpp b/COFF/ICF.cpp index fe59de6efa54..9a43f2bd43f5 100644 --- a/COFF/ICF.cpp +++ b/COFF/ICF.cpp @@ -49,10 +49,10 @@ private: size_t findBoundary(size_t Begin, size_t End); - void forEachColorRange(size_t Begin, size_t End, + void forEachClassRange(size_t Begin, size_t End, std::function Fn); - void forEachColor(std::function Fn); + void forEachClass(std::function Fn); std::vector Chunks; int Cnt = 0; @@ -85,7 +85,7 @@ bool ICF::isEligible(SectionChunk *C) { return C->isCOMDAT() && C->isLive() && Global && Executable && !Writable; } -// Split a range into smaller ranges by recoloring sections +// Split an equivalence class into smaller classes. void ICF::segregate(size_t Begin, size_t End, bool Constant) { while (Begin < End) { // Divide [Begin, End) into two. Let Mid be the start index of the @@ -101,7 +101,7 @@ void ICF::segregate(size_t Begin, size_t End, bool Constant) { // Split [Begin, End) into [Begin, Mid) and [Mid, End). uint32_t Id = NextId++; for (size_t I = Begin; I < Mid; ++I) - Chunks[I]->Color[(Cnt + 1) % 2] = Id; + Chunks[I]->Class[(Cnt + 1) % 2] = Id; // If we created a group, we need to iterate the main loop again. if (Mid != End) @@ -130,7 +130,7 @@ bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) { if (auto *D1 = dyn_cast(B1)) if (auto *D2 = dyn_cast(B2)) return D1->getValue() == D2->getValue() && - D1->getChunk()->Color[Cnt % 2] == D2->getChunk()->Color[Cnt % 2]; + D1->getChunk()->Class[Cnt % 2] == D2->getChunk()->Class[Cnt % 2]; return false; }; if (!std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq)) @@ -155,7 +155,7 @@ bool ICF::equalsVariable(const SectionChunk *A, const SectionChunk *B) { return true; if (auto *D1 = dyn_cast(B1)) if (auto *D2 = dyn_cast(B2)) - return D1->getChunk()->Color[Cnt % 2] == D2->getChunk()->Color[Cnt % 2]; + return D1->getChunk()->Class[Cnt % 2] == D2->getChunk()->Class[Cnt % 2]; return false; }; return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq); @@ -163,12 +163,12 @@ bool ICF::equalsVariable(const SectionChunk *A, const SectionChunk *B) { size_t ICF::findBoundary(size_t Begin, size_t End) { for (size_t I = Begin + 1; I < End; ++I) - if (Chunks[Begin]->Color[Cnt % 2] != Chunks[I]->Color[Cnt % 2]) + if (Chunks[Begin]->Class[Cnt % 2] != Chunks[I]->Class[Cnt % 2]) return I; return End; } -void ICF::forEachColorRange(size_t Begin, size_t End, +void ICF::forEachClassRange(size_t Begin, size_t End, std::function Fn) { if (Begin > 0) Begin = findBoundary(Begin - 1, End); @@ -180,12 +180,12 @@ void ICF::forEachColorRange(size_t Begin, size_t End, } } -// Call Fn on each color group. -void ICF::forEachColor(std::function Fn) { +// Call Fn on each class group. +void ICF::forEachClass(std::function Fn) { // If the number of sections are too small to use threading, // call Fn sequentially. if (Chunks.size() < 1024) { - forEachColorRange(0, Chunks.size(), Fn); + forEachClassRange(0, Chunks.size(), Fn); return; } @@ -193,9 +193,9 @@ void ICF::forEachColor(std::function Fn) { size_t NumShards = 256; size_t Step = Chunks.size() / NumShards; parallel_for(size_t(0), NumShards, [&](size_t I) { - forEachColorRange(I * Step, (I + 1) * Step, Fn); + forEachClassRange(I * Step, (I + 1) * Step, Fn); }); - forEachColorRange(Step * NumShards, Chunks.size(), Fn); + forEachClassRange(Step * NumShards, Chunks.size(), Fn); } // Merge identical COMDAT sections. @@ -209,11 +209,11 @@ void ICF::run(const std::vector &Vec) { continue; if (isEligible(SC)) { - // Set MSB to 1 to avoid collisions with non-hash colors. - SC->Color[0] = getHash(SC) | (1 << 31); + // Set MSB to 1 to avoid collisions with non-hash classs. + SC->Class[0] = getHash(SC) | (1 << 31); Chunks.push_back(SC); } else { - SC->Color[0] = NextId++; + SC->Class[0] = NextId++; } } @@ -224,25 +224,25 @@ void ICF::run(const std::vector &Vec) { // the same group are consecutive in the vector. std::stable_sort(Chunks.begin(), Chunks.end(), [](SectionChunk *A, SectionChunk *B) { - return A->Color[0] < B->Color[0]; + return A->Class[0] < B->Class[0]; }); // Compare static contents and assign unique IDs for each static content. - forEachColor([&](size_t Begin, size_t End) { segregate(Begin, End, true); }); + forEachClass([&](size_t Begin, size_t End) { segregate(Begin, End, true); }); ++Cnt; // Split groups by comparing relocations until convergence is obtained. do { Repeat = false; - forEachColor( + forEachClass( [&](size_t Begin, size_t End) { segregate(Begin, End, false); }); ++Cnt; } while (Repeat); log("ICF needed " + Twine(Cnt) + " iterations"); - // Merge sections in the same colors. - forEachColor([&](size_t Begin, size_t End) { + // Merge sections in the same classs. + forEachClass([&](size_t Begin, size_t End) { if (End - Begin == 1) return; diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp index 20411a703e24..61b0c64de3a8 100644 --- a/COFF/PDB.cpp +++ b/COFF/PDB.cpp @@ -133,7 +133,7 @@ static void dumpDebugT(ScopedPrinter &W, ObjectFile *File) { if (Data.empty()) return; - TypeDatabase TDB; + TypeDatabase TDB(0); TypeDumpVisitor TDV(TDB, &W, false); // Use a default implementation that does not follow type servers and instead // just dumps the contents of the TypeServer2 record. @@ -154,7 +154,7 @@ static void dumpDebugS(ScopedPrinter &W, ObjectFile *File) { if (auto EC = Reader.readArray(Symbols, Reader.getLength())) fatal(EC, "StreamReader.readArray failed"); - TypeDatabase TDB; + TypeDatabase TDB(0); CVSymbolDumper SymbolDumper(W, TDB, nullptr, false); if (auto EC = SymbolDumper.dump(Symbols)) fatal(EC, "CVSymbolDumper::dump failed"); diff --git a/ELF/Config.h b/ELF/Config.h index 1ace4aa26fdb..0321c84e7106 100644 --- a/ELF/Config.h +++ b/ELF/Config.h @@ -73,6 +73,7 @@ struct VersionDefinition { // Most fields are initialized by the driver. struct Configuration { InputFile *FirstElf = nullptr; + bool HasStaticTlsModel = false; uint8_t OSABI = 0; llvm::CachePruningPolicy ThinLTOCachePolicy; llvm::StringMap SectionStartMap; @@ -99,7 +100,6 @@ struct Configuration { std::vector VersionScriptLocals; std::vector BuildIdVector; bool AllowMultipleDefinition; - bool ArchiveWithoutSymbolsSeen = false; bool AsNeeded = false; bool Bsymbolic; bool BsymbolicFunctions; diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp index 6a71eb3ee490..c2cfe3c4129e 100644 --- a/ELF/Driver.cpp +++ b/ELF/Driver.cpp @@ -123,13 +123,13 @@ static std::tuple parseEmulation(StringRef Emul) { // Returns slices of MB by parsing MB as an archive file. // Each slice consists of a member file in the archive. -std::vector -static getArchiveMembers(MemoryBufferRef MB) { +std::vector> static getArchiveMembers( + MemoryBufferRef MB) { std::unique_ptr File = check(Archive::create(MB), MB.getBufferIdentifier() + ": failed to parse archive"); - std::vector V; + std::vector> V; Error Err = Error::success(); for (const ErrorOr &COrErr : File->children(Err)) { Archive::Child C = @@ -139,7 +139,7 @@ static getArchiveMembers(MemoryBufferRef MB) { check(C.getMemoryBufferRef(), MB.getBufferIdentifier() + ": could not get the buffer for a child of the archive"); - V.push_back(MBRef); + V.push_back(std::make_pair(MBRef, C.getChildOffset())); } if (Err) fatal(MB.getBufferIdentifier() + ": Archive::children failed: " + @@ -152,8 +152,7 @@ static getArchiveMembers(MemoryBufferRef MB) { return V; } -// Opens and parses a file. Path has to be resolved already. -// Newly created memory buffers are owned by this driver. +// Opens a file and create a file object. Path has to be resolved already. void LinkerDriver::addFile(StringRef Path, bool WithLOption) { using namespace sys::fs; @@ -171,14 +170,31 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) { case file_magic::unknown: readLinkerScript(MBRef); return; - case file_magic::archive: + case file_magic::archive: { + // Handle -whole-archive. if (InWholeArchive) { - for (MemoryBufferRef MB : getArchiveMembers(MBRef)) - Files.push_back(createObjectFile(MB, Path)); + for (const auto &P : getArchiveMembers(MBRef)) + Files.push_back(createObjectFile(P.first, Path, P.second)); return; } - Files.push_back(make(MBRef)); + + std::unique_ptr File = + check(Archive::create(MBRef), Path + ": failed to parse archive"); + + // If an archive file has no symbol table, it is likely that a user + // is attempting LTO and using a default ar command that doesn't + // understand the LLVM bitcode file. It is a pretty common error, so + // we'll handle it as if it had a symbol table. + if (!File->hasSymbolTable()) { + for (const auto &P : getArchiveMembers(MBRef)) + Files.push_back(make(P.first, Path, P.second)); + return; + } + + // Handle the regular case. + Files.push_back(make(std::move(File))); return; + } case file_magic::elf_shared_object: if (Config->Relocatable) { error("attempted static link of dynamic object " + Path); @@ -199,7 +215,7 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) { return; default: if (InLib) - Files.push_back(make(MBRef)); + Files.push_back(make(MBRef, "", 0)); else Files.push_back(createObjectFile(MBRef)); } diff --git a/ELF/InputFiles.cpp b/ELF/InputFiles.cpp index 260a78ebbf8e..5f94fc9338a4 100644 --- a/ELF/InputFiles.cpp +++ b/ELF/InputFiles.cpp @@ -596,17 +596,13 @@ SymbolBody *elf::ObjectFile::createSymbolBody(const Elf_Sym *Sym) { } } +ArchiveFile::ArchiveFile(std::unique_ptr &&File) + : InputFile(ArchiveKind, File->getMemoryBufferRef()), + File(std::move(File)) {} + template void ArchiveFile::parse() { - File = check(Archive::create(MB), - MB.getBufferIdentifier() + ": failed to parse archive"); - - // Read the symbol table to construct Lazy objects. - for (const Archive::Symbol &Sym : File->symbols()) { + for (const Archive::Symbol &Sym : File->symbols()) Symtab::X->addLazyArchive(this, Sym); - } - - if (File->symbols().begin() == File->symbols().end()) - Config->ArchiveWithoutSymbolsSeen = true; } // Returns a buffer pointing to a member file containing a given symbol. @@ -981,6 +977,13 @@ MemoryBufferRef LazyObjectFile::getBuffer() { return MB; } +InputFile *LazyObjectFile::fetch() { + MemoryBufferRef MBRef = getBuffer(); + if (MBRef.getBuffer().empty()) + return nullptr; + return createObjectFile(MBRef, ArchiveName, OffsetInArchive); +} + template void LazyObjectFile::parse() { for (StringRef Sym : getSymbols()) Symtab::X->addLazyObject(Sym, *this); diff --git a/ELF/InputFiles.h b/ELF/InputFiles.h index d0a45a4a98cf..6daf26649859 100644 --- a/ELF/InputFiles.h +++ b/ELF/InputFiles.h @@ -219,7 +219,11 @@ private: // archive file semantics. class LazyObjectFile : public InputFile { public: - explicit LazyObjectFile(MemoryBufferRef M) : InputFile(LazyObjectKind, M) {} + LazyObjectFile(MemoryBufferRef M, StringRef ArchiveName, + uint64_t OffsetInArchive) + : InputFile(LazyObjectKind, M), OffsetInArchive(OffsetInArchive) { + this->ArchiveName = ArchiveName; + } static bool classof(const InputFile *F) { return F->kind() == LazyObjectKind; @@ -227,6 +231,7 @@ public: template void parse(); MemoryBufferRef getBuffer(); + InputFile *fetch(); private: std::vector getSymbols(); @@ -234,12 +239,13 @@ private: std::vector getBitcodeSymbols(); bool Seen = false; + uint64_t OffsetInArchive; }; // An ArchiveFile object represents a .a file. class ArchiveFile : public InputFile { public: - explicit ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {} + explicit ArchiveFile(std::unique_ptr &&File); static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; } template void parse(); diff --git a/ELF/LinkerScript.cpp b/ELF/LinkerScript.cpp index 3f872c65897f..d7858e173c7b 100644 --- a/ELF/LinkerScript.cpp +++ b/ELF/LinkerScript.cpp @@ -406,27 +406,22 @@ void LinkerScript::processCommands(OutputSectionFactory &Factory) { } // Add input sections to an output section. - unsigned Pos = 0; - for (InputSectionBase *S : V) { - // The actual offset will be computed during - // assignAddresses. For now, use the index as a very crude - // approximation so that it is at least easy for other code to - // know the section order. - cast(S)->OutSecOff = Pos++; + for (InputSectionBase *S : V) Factory.addInputSec(S, Cmd->Name, Cmd->Sec); + if (OutputSection *Sec = Cmd->Sec) { + assert(Sec->SectionIndex == INT_MAX); + Sec->SectionIndex = I; } } } CurOutSec = nullptr; } -void LinkerScript::fabricateDefaultCommands(bool AllocateHeader) { +void LinkerScript::fabricateDefaultCommands() { std::vector Commands; // Define start address - uint64_t StartAddr = Config->ImageBase; - if (AllocateHeader) - StartAddr += elf::getHeaderSize(); + uint64_t StartAddr = Config->ImageBase + elf::getHeaderSize(); // The Sections with -T
have been sorted in order of ascending // address. We must lower StartAddr if the lowest -T
as @@ -488,6 +483,11 @@ void LinkerScript::addOrphanSections(OutputSectionFactory &Factory) { } else { auto *Cmd = cast(*I); Factory.addInputSec(S, Name, Cmd->Sec); + if (OutputSection *Sec = Cmd->Sec) { + unsigned Index = std::distance(Opt.Commands.begin(), I); + assert(Sec->SectionIndex == INT_MAX || Sec->SectionIndex == Index); + Sec->SectionIndex = Index; + } auto *ISD = make(""); ISD->Sections.push_back(S); Cmd->Commands.push_back(ISD); @@ -495,17 +495,22 @@ void LinkerScript::addOrphanSections(OutputSectionFactory &Factory) { } } -static bool isTbss(OutputSection *Sec) { - return (Sec->Flags & SHF_TLS) && Sec->Type == SHT_NOBITS; +uint64_t LinkerScript::advance(uint64_t Size, unsigned Align) { + bool IsTbss = (CurOutSec->Flags & SHF_TLS) && CurOutSec->Type == SHT_NOBITS; + uint64_t Start = IsTbss ? Dot + ThreadBssOffset : Dot; + Start = alignTo(Start, Align); + uint64_t End = Start + Size; + + if (IsTbss) + ThreadBssOffset = End - Dot; + else + Dot = End; + return End; } void LinkerScript::output(InputSection *S) { - bool IsTbss = isTbss(CurOutSec); - - uint64_t Pos = IsTbss ? Dot + ThreadBssOffset : Dot; - Pos = alignTo(Pos, S->Alignment); - S->OutSecOff = Pos - CurOutSec->Addr; - Pos += S->getSize(); + uint64_t Pos = advance(S->getSize(), S->Alignment); + S->OutSecOff = Pos - S->getSize() - CurOutSec->Addr; // Update output section size after adding each section. This is so that // SIZEOF works correctly in the case below: @@ -524,11 +529,6 @@ void LinkerScript::output(InputSection *S) { " bytes"); } } - - if (IsTbss) - ThreadBssOffset = Pos - Dot; - else - Dot = Pos; } void LinkerScript::switchTo(OutputSection *Sec) { @@ -536,9 +536,7 @@ void LinkerScript::switchTo(OutputSection *Sec) { return; CurOutSec = Sec; - - Dot = alignTo(Dot, CurOutSec->Alignment); - CurOutSec->Addr = isTbss(CurOutSec) ? Dot + ThreadBssOffset : Dot; + CurOutSec->Addr = advance(0, CurOutSec->Alignment); // If neither AT nor AT> is specified for an allocatable section, the linker // will set the LMA such that the difference between VMA and LMA for the @@ -643,6 +641,11 @@ void LinkerScript::assignOffsets(OutputSectionCommand *Cmd) { Dot = CurMemRegion->Offset; switchTo(Sec); + // We do not support custom layout for compressed debug sectons. + // At this point we already know their size and have compressed content. + if (CurOutSec->Flags & SHF_COMPRESSED) + return; + for (BaseCommand *C : Cmd->Commands) process(*C); } @@ -678,8 +681,9 @@ void LinkerScript::adjustSectionsBeforeSorting() { // consequeces and gives us a section to put the symbol in. uint64_t Flags = SHF_ALLOC; uint32_t Type = SHT_PROGBITS; - for (BaseCommand *Base : Opt.Commands) { - auto *Cmd = dyn_cast(Base); + + for (int I = 0, E = Opt.Commands.size(); I != E; ++I) { + auto *Cmd = dyn_cast(Opt.Commands[I]); if (!Cmd) continue; if (OutputSection *Sec = Cmd->Sec) { @@ -692,6 +696,7 @@ void LinkerScript::adjustSectionsBeforeSorting() { continue; auto *OutSec = make(Cmd->Name, Type, Flags); + OutSec->SectionIndex = I; OutputSections->push_back(OutSec); Cmd->Sec = OutSec; } @@ -894,6 +899,48 @@ void LinkerScript::synchronize() { } } +static bool allocateHeaders(std::vector &Phdrs, + ArrayRef OutputSections, + uint64_t Min) { + auto FirstPTLoad = + std::find_if(Phdrs.begin(), Phdrs.end(), + [](const PhdrEntry &E) { return E.p_type == PT_LOAD; }); + if (FirstPTLoad == Phdrs.end()) + return false; + + uint64_t HeaderSize = getHeaderSize(); + if (HeaderSize <= Min || Script->hasPhdrsCommands()) { + Min = alignDown(Min - HeaderSize, Config->MaxPageSize); + Out::ElfHeader->Addr = Min; + Out::ProgramHeaders->Addr = Min + Out::ElfHeader->Size; + return true; + } + + assert(FirstPTLoad->First == Out::ElfHeader); + OutputSection *ActualFirst = nullptr; + for (OutputSection *Sec : OutputSections) { + if (Sec->FirstInPtLoad == Out::ElfHeader) { + ActualFirst = Sec; + break; + } + } + if (ActualFirst) { + for (OutputSection *Sec : OutputSections) + if (Sec->FirstInPtLoad == Out::ElfHeader) + Sec->FirstInPtLoad = ActualFirst; + FirstPTLoad->First = ActualFirst; + } else { + Phdrs.erase(FirstPTLoad); + } + + auto PhdrI = std::find_if(Phdrs.begin(), Phdrs.end(), [](const PhdrEntry &E) { + return E.p_type == PT_PHDR; + }); + if (PhdrI != Phdrs.end()) + Phdrs.erase(PhdrI); + return false; +} + void LinkerScript::assignAddresses(std::vector &Phdrs) { // Assign addresses as instructed by linker script SECTIONS sub-commands. Dot = 0; @@ -994,12 +1041,17 @@ static void writeInt(uint8_t *Buf, uint64_t Data, uint64_t Size) { llvm_unreachable("unsupported Size argument"); } -void LinkerScript::writeDataBytes(StringRef Name, uint8_t *Buf) { - int I = getSectionIndex(Name); - if (I == INT_MAX) +void LinkerScript::writeDataBytes(OutputSection *Sec, uint8_t *Buf) { + auto I = std::find_if(Opt.Commands.begin(), Opt.Commands.end(), + [=](BaseCommand *Base) { + if (auto *Cmd = dyn_cast(Base)) + if (Cmd->Sec == Sec) + return true; + return false; + }); + if (I == Opt.Commands.end()) return; - - auto *Cmd = dyn_cast(Opt.Commands[I]); + auto *Cmd = cast(*I); for (BaseCommand *Base : Cmd->Commands) if (auto *Data = dyn_cast(Base)) writeInt(Buf + Data->Offset, Data->Expression().getValue(), Data->Size); @@ -1013,18 +1065,6 @@ bool LinkerScript::hasLMA(StringRef Name) { return false; } -// Returns the index of the given section name in linker script -// SECTIONS commands. Sections are laid out as the same order as they -// were in the script. If a given name did not appear in the script, -// it returns INT_MAX, so that it will be laid out at end of file. -int LinkerScript::getSectionIndex(StringRef Name) { - for (int I = 0, E = Opt.Commands.size(); I != E; ++I) - if (auto *Cmd = dyn_cast(Opt.Commands[I])) - if (Cmd->Name == Name) - return I; - return INT_MAX; -} - ExprValue LinkerScript::getSymbolValue(const Twine &Loc, StringRef S) { if (S == ".") return {CurOutSec, Dot - CurOutSec->Addr}; diff --git a/ELF/LinkerScript.h b/ELF/LinkerScript.h index dd96d335a660..7bcd21c87602 100644 --- a/ELF/LinkerScript.h +++ b/ELF/LinkerScript.h @@ -228,6 +228,7 @@ protected: MemoryRegion *findMemoryRegion(OutputSectionCommand *Cmd); void switchTo(OutputSection *Sec); + uint64_t advance(uint64_t Size, unsigned Align); void output(InputSection *Sec); void process(BaseCommand &Base); @@ -252,7 +253,7 @@ public: bool isDefined(StringRef S); std::vector *OutputSections; - void fabricateDefaultCommands(bool AllocateHeader); + void fabricateDefaultCommands(); void addOrphanSections(OutputSectionFactory &Factory); void removeEmptyCommands(); void adjustSectionsBeforeSorting(); @@ -269,9 +270,8 @@ public: void processNonSectionCommands(); void synchronize(); void assignAddresses(std::vector &Phdrs); - int getSectionIndex(StringRef Name); - void writeDataBytes(StringRef Name, uint8_t *Buf); + void writeDataBytes(OutputSection *Sec, uint8_t *Buf); void addSymbol(SymbolAssignment *Cmd); void processCommands(OutputSectionFactory &Factory); diff --git a/ELF/Options.td b/ELF/Options.td index 8863912c179c..65a0e72d2320 100644 --- a/ELF/Options.td +++ b/ELF/Options.td @@ -290,6 +290,7 @@ def alias_L__library_path: J<"library-path=">, Alias; def alias_define_common_d: Flag<["-"], "d">, Alias; def alias_define_common_dc: F<"dc">, Alias; def alias_define_common_dp: F<"dp">, Alias; +def alias_defsym: S<"defsym">, Alias; def alias_discard_all_x: Flag<["-"], "x">, Alias; def alias_discard_locals_X: Flag<["-"], "X">, Alias; def alias_dynamic_list: J<"dynamic-list=">, Alias; diff --git a/ELF/OutputSections.cpp b/ELF/OutputSections.cpp index 839f68f2da55..cb9c57657af3 100644 --- a/ELF/OutputSections.cpp +++ b/ELF/OutputSections.cpp @@ -68,7 +68,8 @@ void OutputSection::writeHeaderTo(typename ELFT::Shdr *Shdr) { OutputSection::OutputSection(StringRef Name, uint32_t Type, uint64_t Flags) : SectionBase(Output, Name, Flags, /*Entsize*/ 0, /*Alignment*/ 1, Type, /*Info*/ 0, - /*Link*/ 0) {} + /*Link*/ 0), + SectionIndex(INT_MAX) {} static bool compareByFilePosition(InputSection *A, InputSection *B) { // Synthetic doesn't have link order dependecy, stable_sort will keep it last @@ -139,12 +140,24 @@ template void OutputSection::finalize() { this->Info = S->OutSec->SectionIndex; } +static uint64_t updateOffset(uint64_t Off, InputSection *S) { + Off = alignTo(Off, S->Alignment); + S->OutSecOff = Off; + return Off + S->getSize(); +} + void OutputSection::addSection(InputSection *S) { assert(S->Live); Sections.push_back(S); S->OutSec = this; this->updateAlignment(S->Alignment); + // The actual offsets will be computed by assignAddresses. For now, use + // crude approximation so that it is at least easy for other code to know the + // section order. It is also used to calculate the output section size early + // for compressed debug sections. + this->Size = updateOffset(Size, S); + // If this section contains a table of fixed-size entries, sh_entsize // holds the element size. Consequently, if this contains two or more // input sections, all of them must have the same sh_entsize. However, @@ -159,11 +172,8 @@ void OutputSection::addSection(InputSection *S) { // and scan relocations to setup sections' offsets. void OutputSection::assignOffsets() { uint64_t Off = 0; - for (InputSection *S : Sections) { - Off = alignTo(Off, S->Alignment); - S->OutSecOff = Off; - Off += S->getSize(); - } + for (InputSection *S : Sections) + Off = updateOffset(Off, S); this->Size = Off; } @@ -305,7 +315,7 @@ template void OutputSection::writeTo(uint8_t *Buf) { // Linker scripts may have BYTE()-family commands with which you // can write arbitrary bytes to the output. Process them if any. - Script->writeDataBytes(Name, Buf); + Script->writeDataBytes(this, Buf); } static uint64_t getOutFlags(InputSectionBase *S) { diff --git a/ELF/Relocations.cpp b/ELF/Relocations.cpp index baef0a2f2257..f5db931e9755 100644 --- a/ELF/Relocations.cpp +++ b/ELF/Relocations.cpp @@ -233,7 +233,7 @@ handleTlsRelocation(uint32_t Type, SymbolBody &Body, InputSectionBase &C, } // Local-Dynamic relocs can be relaxed to Local-Exec. - if (Target->isTlsLocalDynamicRel(Type) && !Config->Shared) { + if (isRelExprOneOf(Expr) && !Config->Shared) { C.Relocations.push_back( {R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Body}); return 1; @@ -282,7 +282,8 @@ handleTlsRelocation(uint32_t Type, SymbolBody &Body, InputSectionBase &C, // Initial-Exec relocs can be relaxed to Local-Exec if the symbol is locally // defined. - if (Target->isTlsInitialExecRel(Type) && !Config->Shared && !IsPreemptible) { + if (isRelExprOneOf(Expr) && + !Config->Shared && !IsPreemptible) { C.Relocations.push_back( {R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Body}); return 1; @@ -694,17 +695,6 @@ static void reportUndefined(SymbolBody &Sym, InputSectionBase &S, warn(Msg); } else { error(Msg); - - if (Config->ArchiveWithoutSymbolsSeen) { - message("At least one archive listed no symbols in its index." - " This can happen when creating archives with a version" - " of ar that does not understand the object files in" - " the archive. For example, if you are using LLVM" - " bitcode objects (such as created by -flto), you may" - " need to use llvm-ar or GNU ar with a plugin."); - // Reset to false so that we print the message only once. - Config->ArchiveWithoutSymbolsSeen = false; - } } } diff --git a/ELF/SymbolTable.cpp b/ELF/SymbolTable.cpp index 30f1c3653f50..ed8a790c9599 100644 --- a/ELF/SymbolTable.cpp +++ b/ELF/SymbolTable.cpp @@ -540,13 +540,10 @@ void SymbolTable::addLazyObject(StringRef Name, LazyObjectFile &Obj) { return; // See comment for addLazyArchive above. - if (S->isWeak()) { + if (S->isWeak()) replaceBody(S, Name, Obj, S->body()->Type); - } else { - MemoryBufferRef MBRef = Obj.getBuffer(); - if (!MBRef.getBuffer().empty()) - addFile(createObjectFile(MBRef)); - } + else if (InputFile *F = Obj.fetch()) + addFile(F); } // Process undefined (-u) flags by loading lazy symbols named by those flags. diff --git a/ELF/Symbols.cpp b/ELF/Symbols.cpp index 01caa6daa5ac..2090b33e8cd6 100644 --- a/ELF/Symbols.cpp +++ b/ELF/Symbols.cpp @@ -327,12 +327,7 @@ InputFile *LazyArchive::fetch() { return createObjectFile(MBInfo.first, file()->getName(), MBInfo.second); } -InputFile *LazyObject::fetch() { - MemoryBufferRef MBRef = file()->getBuffer(); - if (MBRef.getBuffer().empty()) - return nullptr; - return createObjectFile(MBRef); -} +InputFile *LazyObject::fetch() { return file()->fetch(); } uint8_t Symbol::computeBinding() const { if (Config->Relocatable) diff --git a/ELF/SyntheticSections.cpp b/ELF/SyntheticSections.cpp index a271d31048f5..9c585e41e9f0 100644 --- a/ELF/SyntheticSections.cpp +++ b/ELF/SyntheticSections.cpp @@ -1038,6 +1038,15 @@ template void DynamicSection::addEntries() { if (!Config->SoName.empty()) add({DT_SONAME, In::DynStrTab->addString(Config->SoName)}); + if (!Config->Shared && !Config->Relocatable) + add({DT_DEBUG, (uint64_t)0}); +} + +// Add remaining entries to complete .dynamic contents. +template void DynamicSection::finalizeContents() { + if (this->Size) + return; // Already finalized. + // Set DT_FLAGS and DT_FLAGS_1. uint32_t DtFlags = 0; uint32_t DtFlags1 = 0; @@ -1055,21 +1064,14 @@ template void DynamicSection::addEntries() { DtFlags |= DF_ORIGIN; DtFlags1 |= DF_1_ORIGIN; } + if (Config->HasStaticTlsModel) + DtFlags |= DF_STATIC_TLS; if (DtFlags) add({DT_FLAGS, DtFlags}); if (DtFlags1) add({DT_FLAGS_1, DtFlags1}); - if (!Config->Shared && !Config->Relocatable) - add({DT_DEBUG, (uint64_t)0}); -} - -// Add remaining entries to complete .dynamic contents. -template void DynamicSection::finalizeContents() { - if (this->Size) - return; // Already finalized. - this->Link = In::DynStrTab->OutSec->SectionIndex; if (In::RelaDyn->OutSec->Size > 0) { bool IsRela = Config->IsRela; diff --git a/ELF/Target.cpp b/ELF/Target.cpp index 921505ae4b61..4643c1a919aa 100644 --- a/ELF/Target.cpp +++ b/ELF/Target.cpp @@ -124,8 +124,6 @@ public: int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override; void writeGotPltHeader(uint8_t *Buf) const override; uint32_t getDynRel(uint32_t Type) const override; - bool isTlsLocalDynamicRel(uint32_t Type) const override; - bool isTlsInitialExecRel(uint32_t Type) const override; void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override; void writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const override; void writePltHeader(uint8_t *Buf) const override; @@ -147,8 +145,6 @@ public: RelExpr getRelExpr(uint32_t Type, const SymbolBody &S, const uint8_t *Loc) const override; bool isPicRel(uint32_t Type) const override; - bool isTlsLocalDynamicRel(uint32_t Type) const override; - bool isTlsInitialExecRel(uint32_t Type) const override; void writeGotPltHeader(uint8_t *Buf) const override; void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override; void writePltHeader(uint8_t *Buf) const override; @@ -193,7 +189,6 @@ public: RelExpr getRelExpr(uint32_t Type, const SymbolBody &S, const uint8_t *Loc) const override; bool isPicRel(uint32_t Type) const override; - bool isTlsInitialExecRel(uint32_t Type) const override; void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override; void writePltHeader(uint8_t *Buf) const override; void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, @@ -303,10 +298,6 @@ bool TargetInfo::needsThunk(RelExpr Expr, uint32_t RelocType, return false; } -bool TargetInfo::isTlsInitialExecRel(uint32_t Type) const { return false; } - -bool TargetInfo::isTlsLocalDynamicRel(uint32_t Type) const { return false; } - void TargetInfo::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const { writeGotPlt(Buf, S); } @@ -360,6 +351,15 @@ X86TargetInfo::X86TargetInfo() { RelExpr X86TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S, const uint8_t *Loc) const { + // There are 4 different TLS variable models with varying degrees of + // flexibility and performance. LocalExec and InitialExec models are fast but + // less-flexible models. They cannot be used for dlopen(). If they are in use, + // we set DF_STATIC_TLS in the ELF header so that the runtime can reject such + // DSOs. + if (Type == R_386_TLS_LE || Type == R_386_TLS_LE_32 || Type == R_386_TLS_IE || + Type == R_386_TLS_GOTIE) + Config->HasStaticTlsModel = true; + switch (Type) { case R_386_8: case R_386_16: @@ -451,14 +451,6 @@ uint32_t X86TargetInfo::getDynRel(uint32_t Type) const { return Type; } -bool X86TargetInfo::isTlsLocalDynamicRel(uint32_t Type) const { - return Type == R_386_TLS_LDO_32 || Type == R_386_TLS_LDM; -} - -bool X86TargetInfo::isTlsInitialExecRel(uint32_t Type) const { - return Type == R_386_TLS_IE || Type == R_386_TLS_GOTIE; -} - void X86TargetInfo::writePltHeader(uint8_t *Buf) const { if (Config->Pic) { const uint8_t V[] = { @@ -771,17 +763,6 @@ bool X86_64TargetInfo::isPicRel(uint32_t Type) const { return Type != R_X86_64_PC32 && Type != R_X86_64_32; } -template -bool X86_64TargetInfo::isTlsInitialExecRel(uint32_t Type) const { - return Type == R_X86_64_GOTTPOFF; -} - -template -bool X86_64TargetInfo::isTlsLocalDynamicRel(uint32_t Type) const { - return Type == R_X86_64_DTPOFF32 || Type == R_X86_64_DTPOFF64 || - Type == R_X86_64_TLSLD; -} - template void X86_64TargetInfo::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const { @@ -1383,11 +1364,6 @@ bool AArch64TargetInfo::usesOnlyLowPageBits(uint32_t Type) const { } } -bool AArch64TargetInfo::isTlsInitialExecRel(uint32_t Type) const { - return Type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 || - Type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC; -} - bool AArch64TargetInfo::isPicRel(uint32_t Type) const { return Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64; } diff --git a/ELF/Target.h b/ELF/Target.h index 4b88626050b3..f4f366219d86 100644 --- a/ELF/Target.h +++ b/ELF/Target.h @@ -23,8 +23,6 @@ class SymbolBody; class TargetInfo { public: - virtual bool isTlsInitialExecRel(uint32_t Type) const; - virtual bool isTlsLocalDynamicRel(uint32_t Type) const; virtual bool isPicRel(uint32_t Type) const { return true; } virtual uint32_t getDynRel(uint32_t Type) const { return Type; } virtual void writeGotPltHeader(uint8_t *Buf) const {} diff --git a/ELF/Writer.cpp b/ELF/Writer.cpp index 3de2596af27c..7f00e37ce7b0 100644 --- a/ELF/Writer.cpp +++ b/ELF/Writer.cpp @@ -62,7 +62,6 @@ private: void assignFileOffsets(); void assignFileOffsetsBinary(); void setPhdrs(); - void fixHeaders(); void fixSectionAlignments(); void fixPredefinedSymbols(); void openFile(); @@ -86,7 +85,6 @@ private: uint64_t FileSize; uint64_t SectionHeaderOff; - bool AllocateHeader = true; }; } // anonymous namespace @@ -252,7 +250,7 @@ template void Writer::run() { } else { if (!Script->Opt.HasSections) { fixSectionAlignments(); - Script->fabricateDefaultCommands(AllocateHeader); + Script->fabricateDefaultCommands(); } Script->synchronize(); Script->assignAddresses(Phdrs); @@ -747,15 +745,12 @@ static bool compareSectionsNonScript(const OutputSection *A, // Output section ordering is determined by this function. template static bool compareSections(const OutputSection *A, const OutputSection *B) { - // For now, put sections mentioned in a linker script first. - int AIndex = Script->getSectionIndex(A->Name); - int BIndex = Script->getSectionIndex(B->Name); - bool AInScript = AIndex != INT_MAX; - bool BInScript = BIndex != INT_MAX; - if (AInScript != BInScript) - return AInScript; - // If both are in the script, use that order. - if (AInScript) + // For now, put sections mentioned in a linker script + // first. Sections not on linker script will have a SectionIndex of + // INT_MAX. + int AIndex = A->SectionIndex; + int BIndex = B->SectionIndex; + if (AIndex != BIndex) return AIndex < BIndex; return compareSectionsNonScript(A, B); @@ -1021,9 +1016,8 @@ template void Writer::sortSections() { auto I = OutputSections.begin(); auto E = OutputSections.end(); auto NonScriptI = - std::find_if(OutputSections.begin(), E, [](OutputSection *S) { - return Script->getSectionIndex(S->Name) == INT_MAX; - }); + std::find_if(OutputSections.begin(), E, + [](OutputSection *S) { return S->SectionIndex == INT_MAX; }); while (NonScriptI != E) { auto BestPos = std::max_element( I, NonScriptI, [&](OutputSection *&A, OutputSection *&B) { @@ -1176,7 +1170,7 @@ template void Writer::finalizeSections() { if (!Config->Relocatable && !Config->OFormatBinary) { Phdrs = Script->hasPhdrsCommands() ? Script->createPhdrs() : createPhdrs(); addPtArmExid(Phdrs); - fixHeaders(); + Out::ProgramHeaders->Size = sizeof(Elf_Phdr) * Phdrs.size(); } // Dynamic section must be the last one in this list and dynamic @@ -1321,6 +1315,11 @@ template std::vector Writer::createPhdrs() { // Add the first PT_LOAD segment for regular output sections. uint64_t Flags = computeFlags(PF_R); PhdrEntry *Load = AddHdr(PT_LOAD, Flags); + + // Add the headers. We will remove them if they don't fit. + Load->add(Out::ElfHeader); + Load->add(Out::ProgramHeaders); + for (OutputSection *Sec : OutputSections) { if (!(Sec->Flags & SHF_ALLOC)) break; @@ -1447,64 +1446,6 @@ template void Writer::fixSectionAlignments() { } } -bool elf::allocateHeaders(std::vector &Phdrs, - ArrayRef OutputSections, - uint64_t Min) { - auto FirstPTLoad = - std::find_if(Phdrs.begin(), Phdrs.end(), - [](const PhdrEntry &E) { return E.p_type == PT_LOAD; }); - if (FirstPTLoad == Phdrs.end()) - return false; - - uint64_t HeaderSize = getHeaderSize(); - if (HeaderSize > Min) { - auto PhdrI = - std::find_if(Phdrs.begin(), Phdrs.end(), - [](const PhdrEntry &E) { return E.p_type == PT_PHDR; }); - if (PhdrI != Phdrs.end()) - Phdrs.erase(PhdrI); - return false; - } - Min = alignDown(Min - HeaderSize, Config->MaxPageSize); - - if (!Script->Opt.HasSections) - Config->ImageBase = Min = std::min(Min, Config->ImageBase); - - Out::ElfHeader->Addr = Min; - Out::ProgramHeaders->Addr = Min + Out::ElfHeader->Size; - - if (Script->hasPhdrsCommands()) - return true; - - if (FirstPTLoad->First) - for (OutputSection *Sec : OutputSections) - if (Sec->FirstInPtLoad == FirstPTLoad->First) - Sec->FirstInPtLoad = Out::ElfHeader; - FirstPTLoad->First = Out::ElfHeader; - if (!FirstPTLoad->Last) - FirstPTLoad->Last = Out::ProgramHeaders; - return true; -} - -// We should set file offsets and VAs for elf header and program headers -// sections. These are special, we do not include them into output sections -// list, but have them to simplify the code. -template void Writer::fixHeaders() { - Out::ProgramHeaders->Size = sizeof(Elf_Phdr) * Phdrs.size(); - // If the script has SECTIONS, assignAddresses will compute the values. - if (Script->Opt.HasSections) - return; - - // When -T
option is specified, lower the base to make room for those - // sections. - uint64_t Min = -1; - if (!Config->SectionStartMap.empty()) - for (const auto &P : Config->SectionStartMap) - Min = std::min(Min, P.second); - - AllocateHeader = allocateHeaders(Phdrs, OutputSections, Min); -} - // Adjusts the file alignment for a given output section and returns // its new file offset. The file offset must be the same with its // virtual address (modulo the page size) so that the loader can load diff --git a/ELF/Writer.h b/ELF/Writer.h index a669e42ef205..8b965f7beddb 100644 --- a/ELF/Writer.h +++ b/ELF/Writer.h @@ -49,9 +49,6 @@ struct PhdrEntry { llvm::StringRef getOutputSectionName(llvm::StringRef Name); -bool allocateHeaders(std::vector &, llvm::ArrayRef, - uint64_t Min); - template uint32_t getMipsEFlags(); uint8_t getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag, diff --git a/include/lld/Core/Parallel.h b/include/lld/Core/Parallel.h index 64b4f2ab04d8..58fa87e85c51 100644 --- a/include/lld/Core/Parallel.h +++ b/include/lld/Core/Parallel.h @@ -10,16 +10,12 @@ #ifndef LLD_CORE_PARALLEL_H #define LLD_CORE_PARALLEL_H -#include "lld/Core/Instrumentation.h" #include "lld/Core/LLVM.h" +#include "lld/Core/TaskGroup.h" #include "llvm/Support/MathExtras.h" -#include "llvm/Support/thread.h" +#include "llvm/Config/llvm-config.h" #include -#include -#include -#include -#include #if defined(_MSC_VER) && LLVM_ENABLE_THREADS #include @@ -27,249 +23,84 @@ #endif namespace lld { -/// \brief Allows one or more threads to wait on a potentially unknown number of -/// events. -/// -/// A latch starts at \p count. inc() increments this, and dec() decrements it. -/// All calls to sync() will block while the count is not 0. -/// -/// Calling dec() on a Latch with a count of 0 has undefined behaivor. -class Latch { - uint32_t _count; - mutable std::mutex _condMut; - mutable std::condition_variable _cond; -public: - explicit Latch(uint32_t count = 0) : _count(count) {} - ~Latch() { sync(); } - - void inc() { - std::unique_lock lock(_condMut); - ++_count; - } - - void dec() { - std::unique_lock lock(_condMut); - if (--_count == 0) - _cond.notify_all(); - } - - void sync() const { - std::unique_lock lock(_condMut); - _cond.wait(lock, [&] { - return _count == 0; - }); - } -}; - -// Classes in this namespace are implementation details of this header. -namespace internal { - -/// \brief An abstract class that takes closures and runs them asynchronously. -class Executor { -public: - virtual ~Executor() = default; - virtual void add(std::function func) = 0; -}; - -#if !defined(LLVM_ENABLE_THREADS) || LLVM_ENABLE_THREADS == 0 -class SyncExecutor : public Executor { -public: - virtual void add(std::function func) { - func(); - } -}; - -inline Executor *getDefaultExecutor() { - static SyncExecutor exec; - return &exec; -} -#elif defined(_MSC_VER) -/// \brief An Executor that runs tasks via ConcRT. -class ConcRTExecutor : public Executor { - struct Taskish { - Taskish(std::function task) : _task(task) {} - - std::function _task; - - static void run(void *p) { - Taskish *self = static_cast(p); - self->_task(); - concurrency::Free(self); - } - }; - -public: - virtual void add(std::function func) { - Concurrency::CurrentScheduler::ScheduleTask(Taskish::run, - new (concurrency::Alloc(sizeof(Taskish))) Taskish(func)); - } -}; - -inline Executor *getDefaultExecutor() { - static ConcRTExecutor exec; - return &exec; -} -#else -/// \brief An implementation of an Executor that runs closures on a thread pool -/// in filo order. -class ThreadPoolExecutor : public Executor { -public: - explicit ThreadPoolExecutor(unsigned threadCount = - std::thread::hardware_concurrency()) - : _stop(false), _done(threadCount) { - // Spawn all but one of the threads in another thread as spawning threads - // can take a while. - std::thread([&, threadCount] { - for (size_t i = 1; i < threadCount; ++i) { - std::thread([=] { - work(); - }).detach(); - } - work(); - }).detach(); - } - - ~ThreadPoolExecutor() override { - std::unique_lock lock(_mutex); - _stop = true; - lock.unlock(); - _cond.notify_all(); - // Wait for ~Latch. - } - - void add(std::function f) override { - std::unique_lock lock(_mutex); - _workStack.push(f); - lock.unlock(); - _cond.notify_one(); - } - -private: - void work() { - while (true) { - std::unique_lock lock(_mutex); - _cond.wait(lock, [&] { - return _stop || !_workStack.empty(); - }); - if (_stop) - break; - auto task = _workStack.top(); - _workStack.pop(); - lock.unlock(); - task(); - } - _done.dec(); - } - - std::atomic _stop; - std::stack> _workStack; - std::mutex _mutex; - std::condition_variable _cond; - Latch _done; -}; - -inline Executor *getDefaultExecutor() { - static ThreadPoolExecutor exec; - return &exec; -} -#endif - -} // namespace internal - -/// \brief Allows launching a number of tasks and waiting for them to finish -/// either explicitly via sync() or implicitly on destruction. -class TaskGroup { - Latch _latch; - -public: - void spawn(std::function f) { - _latch.inc(); - internal::getDefaultExecutor()->add([&, f] { - f(); - _latch.dec(); - }); - } - - void sync() const { _latch.sync(); } -}; - -#if !defined(LLVM_ENABLE_THREADS) || LLVM_ENABLE_THREADS == 0 -template +#if !LLVM_ENABLE_THREADS +template void parallel_sort( - RandomAccessIterator start, RandomAccessIterator end, - const Comp &comp = std::less< + RandomAccessIterator Start, RandomAccessIterator End, + const Comparator &Comp = std::less< typename std::iterator_traits::value_type>()) { - std::sort(start, end, comp); + std::sort(Start, End, Comp); } #elif defined(_MSC_VER) // Use ppl parallel_sort on Windows. -template +template void parallel_sort( - RandomAccessIterator start, RandomAccessIterator end, - const Comp &comp = std::less< + RandomAccessIterator Start, RandomAccessIterator End, + const Comparator &Comp = std::less< typename std::iterator_traits::value_type>()) { - concurrency::parallel_sort(start, end, comp); + concurrency::parallel_sort(Start, End, Comp); } #else namespace detail { -const ptrdiff_t minParallelSize = 1024; +const ptrdiff_t MinParallelSize = 1024; /// \brief Inclusive median. -template -RandomAccessIterator medianOf3(RandomAccessIterator start, - RandomAccessIterator end, const Comp &comp) { - RandomAccessIterator mid = start + (std::distance(start, end) / 2); - return comp(*start, *(end - 1)) - ? (comp(*mid, *(end - 1)) ? (comp(*start, *mid) ? mid : start) - : end - 1) - : (comp(*mid, *start) ? (comp(*(end - 1), *mid) ? mid : end - 1) - : start); +template +RandomAccessIterator medianOf3(RandomAccessIterator Start, + RandomAccessIterator End, + const Comparator &Comp) { + RandomAccessIterator Mid = Start + (std::distance(Start, End) / 2); + return Comp(*Start, *(End - 1)) + ? (Comp(*Mid, *(End - 1)) ? (Comp(*Start, *Mid) ? Mid : Start) + : End - 1) + : (Comp(*Mid, *Start) ? (Comp(*(End - 1), *Mid) ? Mid : End - 1) + : Start); } -template -void parallel_quick_sort(RandomAccessIterator start, RandomAccessIterator end, - const Comp &comp, TaskGroup &tg, size_t depth) { +template +void parallel_quick_sort(RandomAccessIterator Start, RandomAccessIterator End, + const Comparator &Comp, TaskGroup &TG, size_t Depth) { // Do a sequential sort for small inputs. - if (std::distance(start, end) < detail::minParallelSize || depth == 0) { - std::sort(start, end, comp); + if (std::distance(Start, End) < detail::MinParallelSize || Depth == 0) { + std::sort(Start, End, Comp); return; } // Partition. - auto pivot = medianOf3(start, end, comp); - // Move pivot to end. - std::swap(*(end - 1), *pivot); - pivot = std::partition(start, end - 1, [&comp, end](decltype(*start) v) { - return comp(v, *(end - 1)); + auto Pivot = medianOf3(Start, End, Comp); + // Move Pivot to End. + std::swap(*(End - 1), *Pivot); + Pivot = std::partition(Start, End - 1, [&Comp, End](decltype(*Start) V) { + return Comp(V, *(End - 1)); }); - // Move pivot to middle of partition. - std::swap(*pivot, *(end - 1)); + // Move Pivot to middle of partition. + std::swap(*Pivot, *(End - 1)); // Recurse. - tg.spawn([=, &comp, &tg] { - parallel_quick_sort(start, pivot, comp, tg, depth - 1); + TG.spawn([=, &Comp, &TG] { + parallel_quick_sort(Start, Pivot, Comp, TG, Depth - 1); }); - parallel_quick_sort(pivot + 1, end, comp, tg, depth - 1); + parallel_quick_sort(Pivot + 1, End, Comp, TG, Depth - 1); } } -template +template void parallel_sort( - RandomAccessIterator start, RandomAccessIterator end, - const Comp &comp = std::less< + RandomAccessIterator Start, RandomAccessIterator End, + const Comparator &Comp = std::less< typename std::iterator_traits::value_type>()) { - TaskGroup tg; - detail::parallel_quick_sort(start, end, comp, tg, - llvm::Log2_64(std::distance(start, end)) + 1); + TaskGroup TG; + detail::parallel_quick_sort(Start, End, Comp, TG, + llvm::Log2_64(std::distance(Start, End)) + 1); } #endif -template void parallel_sort(T *start, T *end) { - parallel_sort(start, end, std::less()); +template void parallel_sort(T *Start, T *End) { + parallel_sort(Start, End, std::less()); } -#if !defined(LLVM_ENABLE_THREADS) || LLVM_ENABLE_THREADS == 0 +#if !LLVM_ENABLE_THREADS template void parallel_for_each(IterTy Begin, IterTy End, FuncTy Fn) { std::for_each(Begin, End, Fn); @@ -302,12 +133,12 @@ void parallel_for_each(IterTy Begin, IterTy End, FuncTy Fn) { if (TaskSize == 0) TaskSize = 1; - TaskGroup Tg; + TaskGroup TG; while (TaskSize <= std::distance(Begin, End)) { - Tg.spawn([=, &Fn] { std::for_each(Begin, Begin + TaskSize, Fn); }); + TG.spawn([=, &Fn] { std::for_each(Begin, Begin + TaskSize, Fn); }); Begin += TaskSize; } - Tg.spawn([=, &Fn] { std::for_each(Begin, End, Fn); }); + TG.spawn([=, &Fn] { std::for_each(Begin, End, Fn); }); } template @@ -316,20 +147,20 @@ void parallel_for(IndexTy Begin, IndexTy End, FuncTy Fn) { if (TaskSize == 0) TaskSize = 1; - TaskGroup Tg; + TaskGroup TG; IndexTy I = Begin; for (; I + TaskSize < End; I += TaskSize) { - Tg.spawn([=, &Fn] { + TG.spawn([=, &Fn] { for (IndexTy J = I, E = I + TaskSize; J != E; ++J) Fn(J); }); } - Tg.spawn([=, &Fn] { + TG.spawn([=, &Fn] { for (IndexTy J = I; J < End; ++J) Fn(J); }); } #endif -} // end namespace lld +} // End namespace lld #endif // LLD_CORE_PARALLEL_H diff --git a/include/lld/Core/TaskGroup.h b/include/lld/Core/TaskGroup.h new file mode 100644 index 000000000000..82e9122f4ae2 --- /dev/null +++ b/include/lld/Core/TaskGroup.h @@ -0,0 +1,65 @@ +//===- lld/Core/TaskGroup.h - Task Group ----------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_CORE_TASKGROUP_H +#define LLD_CORE_TASKGROUP_H + +#include "lld/Core/LLVM.h" + +#include +#include +#include + +namespace lld { +/// \brief Allows one or more threads to wait on a potentially unknown number of +/// events. +/// +/// A latch starts at \p count. inc() increments this, and dec() decrements it. +/// All calls to sync() will block while the count is not 0. +/// +/// Calling dec() on a Latch with a count of 0 has undefined behaivor. +class Latch { + uint32_t _count; + mutable std::mutex _condMut; + mutable std::condition_variable _cond; + +public: + explicit Latch(uint32_t count = 0) : _count(count) {} + ~Latch() { sync(); } + + void inc() { + std::unique_lock lock(_condMut); + ++_count; + } + + void dec() { + std::unique_lock lock(_condMut); + if (--_count == 0) + _cond.notify_all(); + } + + void sync() const { + std::unique_lock lock(_condMut); + _cond.wait(lock, [&] { return _count == 0; }); + } +}; + +/// \brief Allows launching a number of tasks and waiting for them to finish +/// either explicitly via sync() or implicitly on destruction. +class TaskGroup { + Latch _latch; + +public: + void spawn(std::function f); + + void sync() const { _latch.sync(); } +}; +} + +#endif diff --git a/include/lld/Support/Memory.h b/include/lld/Support/Memory.h deleted file mode 100644 index 46db4a39f696..000000000000 --- a/include/lld/Support/Memory.h +++ /dev/null @@ -1,63 +0,0 @@ -//===- Memory.h -------------------------------------------------*- C++ -*-===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines arena allocators. -// -// Almost all large objects, such as files, sections or symbols, are -// used for the entire lifetime of the linker once they are created. -// This usage characteristic makes arena allocator an attractive choice -// where the entire linker is one arena. With an arena, newly created -// objects belong to the arena and freed all at once when everything is done. -// Arena allocators are efficient and easy to understand. -// Most objects are allocated using the arena allocators defined by this file. -// -//===----------------------------------------------------------------------===// - -#ifndef LLD_MEMORY_H -#define LLD_MEMORY_H - -#include "llvm/Support/Allocator.h" -#include "llvm/Support/StringSaver.h" -#include - -namespace lld { - -// Use this arena if your object doesn't have a destructor. -extern llvm::BumpPtrAllocator BAlloc; -extern llvm::StringSaver Saver; - -// These two classes are hack to keep track of all -// SpecificBumpPtrAllocator instances. -struct SpecificAllocBase { - SpecificAllocBase() { Instances.push_back(this); } - virtual ~SpecificAllocBase() = default; - virtual void reset() = 0; - static std::vector Instances; -}; - -template struct SpecificAlloc : public SpecificAllocBase { - void reset() override { Alloc.DestroyAll(); } - llvm::SpecificBumpPtrAllocator Alloc; -}; - -// Use this arena if your object has a destructor. -// Your destructor will be invoked from freeArena(). -template inline T *make(U &&... Args) { - static SpecificAlloc Alloc; - return new (Alloc.Alloc.Allocate()) T(std::forward(Args)...); -} - -inline void freeArena() { - for (SpecificAllocBase *Alloc : SpecificAllocBase::Instances) - Alloc->reset(); - BAlloc.Reset(); -} -} - -#endif diff --git a/lib/Core/CMakeLists.txt b/lib/Core/CMakeLists.txt index bbd9ad48b6df..cdd4e679ffa2 100644 --- a/lib/Core/CMakeLists.txt +++ b/lib/Core/CMakeLists.txt @@ -12,6 +12,7 @@ add_lld_library(lldCore Resolver.cpp SymbolTable.cpp TargetOptionsCommandFlags.cpp + TaskGroup.cpp Writer.cpp ADDITIONAL_HEADER_DIRS @@ -20,6 +21,9 @@ add_lld_library(lldCore LINK_COMPONENTS MC Support + + LINK_LIBS + ${LLVM_PTHREAD_LIB} DEPENDS ${tablegen_deps} diff --git a/lib/Core/TaskGroup.cpp b/lib/Core/TaskGroup.cpp new file mode 100644 index 000000000000..d4de48ce3dc4 --- /dev/null +++ b/lib/Core/TaskGroup.cpp @@ -0,0 +1,141 @@ +//===- lld/Core/TaskGroup.cpp - Task Group --------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lld/Core/TaskGroup.h" +#include "llvm/Config/llvm-config.h" + +#include +#include +#include + +#if defined(_MSC_VER) && LLVM_ENABLE_THREADS +#include +#include +#endif + +using namespace lld; + +namespace { + +/// \brief An abstract class that takes closures and runs them asynchronously. +class Executor { +public: + virtual ~Executor() = default; + virtual void add(std::function func) = 0; + + static Executor *getDefaultExecutor(); +}; + +#if !LLVM_ENABLE_THREADS +class SyncExecutor : public Executor { +public: + virtual void add(std::function F) { F(); } +}; + +Executor *Executor::getDefaultExecutor() { + static SyncExecutor Exec; + return &Exec; +} + +#elif defined(_MSC_VER) +/// \brief An Executor that runs tasks via ConcRT. +class ConcRTExecutor : public Executor { + struct Taskish { + Taskish(std::function Task) : Task(Task) {} + + std::function Task; + + static void run(void *P) { + Taskish *Self = static_cast(P); + Self->Task(); + concurrency::Free(Self); + } + }; + +public: + virtual void add(std::function F) { + Concurrency::CurrentScheduler::ScheduleTask( + Taskish::run, new (concurrency::Alloc(sizeof(Taskish))) Taskish(F)); + } +}; + +Executor *Executor::getDefaultExecutor() { + static ConcRTExecutor exec; + return &exec; +} + +#else +/// \brief An implementation of an Executor that runs closures on a thread pool +/// in filo order. +class ThreadPoolExecutor : public Executor { +public: + explicit ThreadPoolExecutor( + unsigned ThreadCount = std::thread::hardware_concurrency()) + : Done(ThreadCount) { + // Spawn all but one of the threads in another thread as spawning threads + // can take a while. + std::thread([&, ThreadCount] { + for (size_t i = 1; i < ThreadCount; ++i) { + std::thread([=] { work(); }).detach(); + } + work(); + }).detach(); + } + + ~ThreadPoolExecutor() override { + std::unique_lock Lock(Mutex); + Stop = true; + Lock.unlock(); + Cond.notify_all(); + // Wait for ~Latch. + } + + void add(std::function F) override { + std::unique_lock Lock(Mutex); + WorkStack.push(F); + Lock.unlock(); + Cond.notify_one(); + } + +private: + void work() { + while (true) { + std::unique_lock Lock(Mutex); + Cond.wait(Lock, [&] { return Stop || !WorkStack.empty(); }); + if (Stop) + break; + auto Task = WorkStack.top(); + WorkStack.pop(); + Lock.unlock(); + Task(); + } + Done.dec(); + } + + std::atomic Stop{false}; + std::stack> WorkStack; + std::mutex Mutex; + std::condition_variable Cond; + Latch Done; +}; + +Executor *Executor::getDefaultExecutor() { + static ThreadPoolExecutor exec; + return &exec; +} +#endif +} + +void TaskGroup::spawn(std::function f) { + _latch.inc(); + Executor::getDefaultExecutor()->add([&, f] { + f(); + _latch.dec(); + }); +} diff --git a/test/ELF/Inputs/i386-static-tls-model1.s b/test/ELF/Inputs/i386-static-tls-model1.s new file mode 100644 index 000000000000..e7e584c1fcf1 --- /dev/null +++ b/test/ELF/Inputs/i386-static-tls-model1.s @@ -0,0 +1,10 @@ +.section ".tdata", "awT", @progbits +.globl var +var: + +.section .foo, "aw" +.global _start +_start: + movl $var@tpoff, %edx # R_386_TLS_LE_32 + movl %gs:0, %ecx + subl %edx, %eax diff --git a/test/ELF/Inputs/i386-static-tls-model2.s b/test/ELF/Inputs/i386-static-tls-model2.s new file mode 100644 index 000000000000..b28a1458742d --- /dev/null +++ b/test/ELF/Inputs/i386-static-tls-model2.s @@ -0,0 +1,9 @@ +.section ".tdata", "awT", @progbits +.globl var +var: + +.section .foo, "aw" +.global _start +_start: + movl %gs:0, %eax + addl var@gotntpoff(%ebx),%eax # R_386_TLS_GOTIE diff --git a/test/ELF/Inputs/i386-static-tls-model3.s b/test/ELF/Inputs/i386-static-tls-model3.s new file mode 100644 index 000000000000..f92267ecbdd0 --- /dev/null +++ b/test/ELF/Inputs/i386-static-tls-model3.s @@ -0,0 +1,9 @@ +.section ".tdata", "awT", @progbits +.globl var +var: + +.section .foo, "aw" +.global _start +_start: + movl %gs:0, %eax + addl var@indntpoff, %eax #R_386_TLS_IE diff --git a/test/ELF/Inputs/i386-static-tls-model4.s b/test/ELF/Inputs/i386-static-tls-model4.s new file mode 100644 index 000000000000..ffb20def4fab --- /dev/null +++ b/test/ELF/Inputs/i386-static-tls-model4.s @@ -0,0 +1,9 @@ +.section ".tdata", "awT", @progbits +.globl var +var: + +.section .foo, "aw" +.global _start +_start: + movl %gs:0, %eax + leal var@ntpoff(%eax), %eax #R_386_TLS_LE diff --git a/test/ELF/defsym.s b/test/ELF/defsym.s index cafc5142d1a9..778180dc9324 100644 --- a/test/ELF/defsym.s +++ b/test/ELF/defsym.s @@ -4,6 +4,11 @@ # RUN: llvm-readobj -t -s %t | FileCheck %s # RUN: llvm-objdump -d -print-imm-hex %t | FileCheck %s --check-prefix=USE +## Check that we accept --defsym foo2=foo1 form. +# RUN: ld.lld -o %t2 %t.o --defsym foo2=foo1 +# RUN: llvm-readobj -t -s %t2 | FileCheck %s +# RUN: llvm-objdump -d -print-imm-hex %t2 | FileCheck %s --check-prefix=USE + ## In compare with GNU linkers, symbol defined with --defsym does ## not get aliased name in symbol table: # CHECK: Symbol { diff --git a/test/ELF/i386-static-tls-model.s b/test/ELF/i386-static-tls-model.s new file mode 100644 index 000000000000..b2799c4c722f --- /dev/null +++ b/test/ELF/i386-static-tls-model.s @@ -0,0 +1,20 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %S/Inputs/i386-static-tls-model1.s -o %t.o +# RUN: ld.lld %t.o -o %t1 -shared +# RUN: llvm-readobj -dynamic-table %t1 | FileCheck %s + +# RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %S/Inputs/i386-static-tls-model2.s -o %t.o +# RUN: ld.lld %t.o -o %t2 -shared +# RUN: llvm-readobj -dynamic-table %t2 | FileCheck %s + +# RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %S/Inputs/i386-static-tls-model3.s -o %t.o +# RUN: ld.lld %t.o -o %t3 -shared +# RUN: llvm-readobj -dynamic-table %t3 | FileCheck %s + +# RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %S/Inputs/i386-static-tls-model4.s -o %t.o +# RUN: ld.lld %t.o -o %t4 -shared +# RUN: llvm-readobj -dynamic-table %t4 | FileCheck %s + +# CHECK: DynamicSection [ +# CHECK: FLAGS STATIC_TLS diff --git a/test/ELF/i386-tls-ie-shared.s b/test/ELF/i386-tls-ie-shared.s index 8becc3199f95..c6dccf84a216 100644 --- a/test/ELF/i386-tls-ie-shared.s +++ b/test/ELF/i386-tls-ie-shared.s @@ -13,8 +13,8 @@ // GOTRELSHARED-NEXT: SHF_ALLOC // GOTRELSHARED-NEXT: SHF_WRITE // GOTRELSHARED-NEXT: ] -// GOTRELSHARED-NEXT: Address: 0x1058 -// GOTRELSHARED-NEXT: Offset: 0x1058 +// GOTRELSHARED-NEXT: Address: 0x1060 +// GOTRELSHARED-NEXT: Offset: 0x1060 // GOTRELSHARED-NEXT: Size: 16 // GOTRELSHARED-NEXT: Link: 0 // GOTRELSHARED-NEXT: Info: 0 @@ -31,36 +31,36 @@ // GOTRELSHARED-NEXT: 0x202D R_386_RELATIVE - 0x0 // GOTRELSHARED-NEXT: 0x2036 R_386_RELATIVE - 0x0 // GOTRELSHARED-NEXT: 0x203F R_386_RELATIVE - 0x0 -// GOTRELSHARED-NEXT: 0x1058 R_386_TLS_TPOFF tlslocal0 0x0 -// GOTRELSHARED-NEXT: 0x105C R_386_TLS_TPOFF tlslocal1 0x0 -// GOTRELSHARED-NEXT: 0x1060 R_386_TLS_TPOFF tlsshared0 0x0 -// GOTRELSHARED-NEXT: 0x1064 R_386_TLS_TPOFF tlsshared1 0x0 +// GOTRELSHARED-NEXT: 0x1060 R_386_TLS_TPOFF tlslocal0 0x0 +// GOTRELSHARED-NEXT: 0x1064 R_386_TLS_TPOFF tlslocal1 0x0 +// GOTRELSHARED-NEXT: 0x1068 R_386_TLS_TPOFF tlsshared0 0x0 +// GOTRELSHARED-NEXT: 0x106C R_386_TLS_TPOFF tlsshared1 0x0 // GOTRELSHARED-NEXT: } // GOTRELSHARED-NEXT: ] // GOTRELSHARED: 0x6FFFFFFA RELCOUNT 8 // DISASMSHARED: Disassembly of section test: // DISASMSHARED-NEXT: _start: -// (.got)[0] = 0x2050 = 8272 -// (.got)[1] = 0x2054 = 8276 -// (.got)[2] = 0x2058 = 8280 -// (.got)[3] = 0x205C = 8284 -// DISASMSHARED-NEXT: 2000: 8b 0d 58 10 00 00 movl 4184, %ecx -// DISASMSHARED-NEXT: 2006: 65 8b 01 movl %gs:(%ecx), %eax -// DISASMSHARED-NEXT: 2009: a1 58 10 00 00 movl 4184, %eax -// DISASMSHARED-NEXT: 200e: 65 8b 00 movl %gs:(%eax), %eax -// DISASMSHARED-NEXT: 2011: 03 0d 58 10 00 00 addl 4184, %ecx -// DISASMSHARED-NEXT: 2017: 65 8b 01 movl %gs:(%ecx), %eax -// DISASMSHARED-NEXT: 201a: 8b 0d 5c 10 00 00 movl 4188, %ecx -// DISASMSHARED-NEXT: 2020: 65 8b 01 movl %gs:(%ecx), %eax -// DISASMSHARED-NEXT: 2023: a1 5c 10 00 00 movl 4188, %eax -// DISASMSHARED-NEXT: 2028: 65 8b 00 movl %gs:(%eax), %eax -// DISASMSHARED-NEXT: 202b: 03 0d 5c 10 00 00 addl 4188, %ecx -// DISASMSHARED-NEXT: 2031: 65 8b 01 movl %gs:(%ecx), %eax -// DISASMSHARED-NEXT: 2034: 8b 0d 60 10 00 00 movl 4192, %ecx -// DISASMSHARED-NEXT: 203a: 65 8b 01 movl %gs:(%ecx), %eax -// DISASMSHARED-NEXT: 203d: 03 0d 64 10 00 00 addl 4196, %ecx -// DISASMSHARED-NEXT: 2043: 65 8b 01 movl %gs:(%ecx), %eax +// (.got)[0] = 0x1060 = 4192 +// (.got)[1] = 0x1064 = 4196 +// (.got)[2] = 0x1068 = 4200 +// (.got)[3] = 0x106C = 4204 +// DISASMSHARED-NEXT: 2000: {{.*}} movl 4192, %ecx +// DISASMSHARED-NEXT: 2006: {{.*}} movl %gs:(%ecx), %eax +// DISASMSHARED-NEXT: 2009: {{.*}} movl 4192, %eax +// DISASMSHARED-NEXT: 200e: {{.*}} movl %gs:(%eax), %eax +// DISASMSHARED-NEXT: 2011: {{.*}} addl 4192, %ecx +// DISASMSHARED-NEXT: 2017: {{.*}} movl %gs:(%ecx), %eax +// DISASMSHARED-NEXT: 201a: {{.*}} movl 4196, %ecx +// DISASMSHARED-NEXT: 2020: {{.*}} movl %gs:(%ecx), %eax +// DISASMSHARED-NEXT: 2023: {{.*}} movl 4196, %eax +// DISASMSHARED-NEXT: 2028: {{.*}} movl %gs:(%eax), %eax +// DISASMSHARED-NEXT: 202b: {{.*}} addl 4196, %ecx +// DISASMSHARED-NEXT: 2031: {{.*}} movl %gs:(%ecx), %eax +// DISASMSHARED-NEXT: 2034: {{.*}} movl 4200, %ecx +// DISASMSHARED-NEXT: 203a: {{.*}} movl %gs:(%ecx), %eax +// DISASMSHARED-NEXT: 203d: {{.*}} addl 4204, %ecx +// DISASMSHARED-NEXT: 2043: {{.*}} movl %gs:(%ecx), %eax .type tlslocal0,@object .section .tbss,"awT",@nobits diff --git a/test/ELF/linkerscript/Inputs/compress-debug-sections.s b/test/ELF/linkerscript/Inputs/compress-debug-sections.s new file mode 100644 index 000000000000..703be5949e0d --- /dev/null +++ b/test/ELF/linkerscript/Inputs/compress-debug-sections.s @@ -0,0 +1,3 @@ +.section .debug_str + .asciz "CCC" + .asciz "DDD" diff --git a/test/ELF/linkerscript/compress-debug-sections.s b/test/ELF/linkerscript/compress-debug-sections.s new file mode 100644 index 000000000000..6798a217b5ac --- /dev/null +++ b/test/ELF/linkerscript/compress-debug-sections.s @@ -0,0 +1,36 @@ +# REQUIRES: x86, zlib + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \ +# RUN: %S/Inputs/compress-debug-sections.s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t2.o + +## .debug_str section is mergeable. LLD would combine all of them into single +## mergeable synthetic section. We use -O0 here to disable merging, that +## allows to check that input sections has correctly assigned offsets. + +# RUN: echo "SECTIONS { }" > %t.script +# RUN: ld.lld -O0 %t1.o %t2.o %t.script -o %t1 --compress-debug-sections=zlib +# RUN: llvm-dwarfdump %t1 | FileCheck %s +# RUN: llvm-readobj -s %t1 | FileCheck %s --check-prefix=ZLIBFLAGS + +# RUN: echo "SECTIONS { .debug_str 0 : { *(.debug_str) } }" > %t2.script +# RUN: ld.lld -O0 %t1.o %t2.o %t2.script -o %t2 --compress-debug-sections=zlib +# RUN: llvm-dwarfdump %t2 | FileCheck %s +# RUN: llvm-readobj -s %t2 | FileCheck %s --check-prefix=ZLIBFLAGS + +# CHECK: .debug_str contents: +# CHECK-NEXT: CCC +# CHECK-NEXT: DDD +# CHECK-NEXT: AAA +# CHECK-NEXT: BBB + +# ZLIBFLAGS: Section { +# ZLIBFLAGS: Index: +# ZLIBFLAGS: Name: .debug_str +# ZLIBFLAGS-NEXT: Type: SHT_PROGBITS +# ZLIBFLAGS-NEXT: Flags [ +# ZLIBFLAGS-NEXT: SHF_COMPRESSED + +.section .debug_str + .asciz "AAA" + .asciz "BBB" diff --git a/test/ELF/lto/Inputs/duplicated-name.ll b/test/ELF/lto/Inputs/duplicated-name.ll new file mode 100644 index 000000000000..78678c0c5add --- /dev/null +++ b/test/ELF/lto/Inputs/duplicated-name.ll @@ -0,0 +1,6 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define void @f2() { + ret void +} diff --git a/test/ELF/lto/archive-no-index.ll b/test/ELF/lto/archive-no-index.ll index 0dca16ba04fb..48cca0aa0794 100644 --- a/test/ELF/lto/archive-no-index.ll +++ b/test/ELF/lto/archive-no-index.ll @@ -4,29 +4,15 @@ ; encountered an empty archive index and undefined references (to prevent ; noisy false alarms). -; RUN: rm -fr %T/archive-no-index -; RUN: mkdir %T/archive-no-index -; RUN: llvm-as %S/Inputs/archive.ll -o %T/archive-no-index/f.o -; RUN: llvm-ar cr %T/archive-no-index/libf.a -; RUN: llvm-ar qS %T/archive-no-index/libf.a %T/archive-no-index/f.o -; RUN: llvm-as %s -o %t.o -; RUN: not ld.lld -emain -m elf_x86_64 %t.o -o %t %T/archive-no-index/libf.a \ -; RUN: 2>&1 | FileCheck --check-prefix=NOTE %s +; RUN: llvm-as -o %t1.o %s +; RUN: llvm-as -o %t2.o %S/Inputs/archive.ll -; RUN: llvm-ar crs %T/archive-no-index/libfs.a %T/archive-no-index/f.o -; RUN: ld.lld -emain -m elf_x86_64 %t.o -o %t %T/archive-no-index/libf.a \ -; RUN: %T/archive-no-index/libfs.a +; RUN: rm -f %t1.a %t2.a +; RUN: llvm-ar crS %t1.a %t2.o +; RUN: llvm-ar crs %t2.a %t2.o -; RUN: llvm-as %S/Inputs/archive-3.ll -o %T/archive-no-index/foo.o -; RUN: llvm-ar crs %T/archive-no-index/libfoo.a %T/archive-no-index/foo.o -; RUN: not ld.lld -emain -m elf_x86_64 %t.o -o %t %T/archive-no-index/libfoo.a \ -; RUN: 2>&1 | FileCheck --check-prefix=NO-NOTE %s - -; NOTE: undefined symbol: f -; NOTE: archive listed no symbols - -; NO-NOTE: undefined symbol: f -; NO-NOTE-NOT: archive listed no symbols +; RUN: ld.lld -o %t -emain -m elf_x86_64 %t1.o %t1.a +; RUN: ld.lld -o %t -emain -m elf_x86_64 %t1.o %t2.a target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" diff --git a/test/ELF/lto/duplicated-name.ll b/test/ELF/lto/duplicated-name.ll new file mode 100644 index 000000000000..b9e6c54564bc --- /dev/null +++ b/test/ELF/lto/duplicated-name.ll @@ -0,0 +1,15 @@ +; REQUIRES: x86 +; Cretae two archive with the same member name +; RUN: rm -f %t1.a %t2.a +; RUN: opt -module-summary %s -o %t.o +; RUN: llvm-ar rcS %t1.a %t.o +; RUN: opt -module-summary %p/Inputs/duplicated-name.ll -o %t.o +; RUN: llvm-ar rcS %t2.a %t.o +; RUN: ld.lld -m elf_x86_64 -shared -o %t.so -uf1 -uf2 %t1.a %t2.a + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define void @f1() { + ret void +} diff --git a/test/ELF/lto/thin-archivecollision.ll b/test/ELF/lto/thin-archivecollision.ll index 4e07187f36a0..f1dd5ae4d85f 100644 --- a/test/ELF/lto/thin-archivecollision.ll +++ b/test/ELF/lto/thin-archivecollision.ll @@ -1,14 +1,21 @@ ; RUN: opt -module-summary %s -o %t.o -; RUN: opt -module-summary %p/Inputs/thin1.ll -o %t.coll.o -; RUN: llvm-ar rcs %t1.a %t.coll.o -; RUN: opt -module-summary %p/Inputs/thin2.ll -o %t.coll.o -; RUN: llvm-ar rcsc %t2.a %t.coll.o +; RUN: mkdir -p %t1 %t2 +; RUN: opt -module-summary %p/Inputs/thin1.ll -o %t1/t.coll.o +; RUN: opt -module-summary %p/Inputs/thin2.ll -o %t2/t.coll.o -; RUN: ld.lld %t.o %t1.a %t2.a -o %t +; RUN: rm -f %t.a +; RUN: llvm-ar rcs %t.a %t1/t.coll.o %t2/t.coll.o +; RUN: ld.lld %t.o %t.a -o %t +; RUN: llvm-nm %t | FileCheck %s + +; Check without a archive symbol table +; RUN: rm -f %t.a +; RUN: llvm-ar rcS %t.a %t1/t.coll.o %t2/t.coll.o +; RUN: ld.lld %t.o %t.a -o %t ; RUN: llvm-nm %t | FileCheck %s ; Check we handle this case correctly even in presence of --whole-archive. -; RUN: ld.lld %t.o --whole-archive %t1.a %t2.a -o %t +; RUN: ld.lld %t.o --whole-archive %t.a -o %t ; RUN: llvm-nm %t | FileCheck %s ; CHECK: T _start diff --git a/test/ELF/tls-dynamic-i686.s b/test/ELF/tls-dynamic-i686.s index ac88e6eaed31..04fd13822530 100644 --- a/test/ELF/tls-dynamic-i686.s +++ b/test/ELF/tls-dynamic-i686.s @@ -56,8 +56,8 @@ addl tls1@gotntpoff(%ebx),%eax // CHECK-NEXT: SHF_ALLOC // CHECK-NEXT: SHF_WRITE // CHECK-NEXT: ] -// CHECK-NEXT: Address: 0x3068 -// CHECK-NEXT: Offset: 0x3068 +// CHECK-NEXT: Address: 0x3070 +// CHECK-NEXT: Offset: 0x3070 // CHECK-NEXT: Size: 32 // CHECK-NEXT: Link: 0 // CHECK-NEXT: Info: 0 @@ -66,13 +66,13 @@ addl tls1@gotntpoff(%ebx),%eax // CHECK: Relocations [ // CHECK: Section ({{.+}}) .rel.dyn { -// CHECK-NEXT: 0x3078 R_386_TLS_DTPMOD32 - 0x0 -// CHECK-NEXT: 0x3068 R_386_TLS_DTPMOD32 tls0 0x0 -// CHECK-NEXT: 0x306C R_386_TLS_DTPOFF32 tls0 0x0 -// CHECK-NEXT: 0x3080 R_386_TLS_TPOFF tls0 0x0 -// CHECK-NEXT: 0x3070 R_386_TLS_DTPMOD32 tls1 0x0 -// CHECK-NEXT: 0x3074 R_386_TLS_DTPOFF32 tls1 0x0 -// CHECK-NEXT: 0x3084 R_386_TLS_TPOFF tls1 0x0 +// CHECK-NEXT: 0x3080 R_386_TLS_DTPMOD32 - 0x0 +// CHECK-NEXT: 0x3070 R_386_TLS_DTPMOD32 tls0 0x0 +// CHECK-NEXT: 0x3074 R_386_TLS_DTPOFF32 tls0 0x0 +// CHECK-NEXT: 0x3088 R_386_TLS_TPOFF tls0 0x0 +// CHECK-NEXT: 0x3078 R_386_TLS_DTPMOD32 tls1 0x0 +// CHECK-NEXT: 0x307C R_386_TLS_DTPOFF32 tls1 0x0 +// CHECK-NEXT: 0x308C R_386_TLS_TPOFF tls1 0x0 // CHECK-NEXT: } // DIS: Disassembly of section .text: @@ -80,20 +80,20 @@ addl tls1@gotntpoff(%ebx),%eax // General dynamic model: // -32 and -24 are first and second GOT entries offsets. // Each one is a pair of records. -// DIS-NEXT: 1000: 8d 04 1d e0 ff ff ff leal -32(,%ebx), %eax -// DIS-NEXT: 1007: e8 64 00 00 00 calll 100 -// DIS-NEXT: 100c: 8d 04 1d e8 ff ff ff leal -24(,%ebx), %eax -// DIS-NEXT: 1013: e8 58 00 00 00 calll 88 +// DIS-NEXT: 1000: {{.*}} leal -32(,%ebx), %eax +// DIS-NEXT: 1007: {{.*}} calll 100 +// DIS-NEXT: 100c: {{.*}} leal -24(,%ebx), %eax +// DIS-NEXT: 1013: {{.*}} calll 88 // Local dynamic model: // -16 is a local module tls index offset. -// DIS-NEXT: 1018: 8d 83 f0 ff ff ff leal -16(%ebx), %eax -// DIS-NEXT: 101e: e8 4d 00 00 00 calll 77 -// DIS-NEXT: 1023: 8d 90 08 00 00 00 leal 8(%eax), %edx -// DIS-NEXT: 1029: 8d 83 f0 ff ff ff leal -16(%ebx), %eax -// DIS-NEXT: 102f: e8 3c 00 00 00 calll 60 -// DIS-NEXT: 1034: 8d 90 0c 00 00 00 leal 12(%eax), %edx +// DIS-NEXT: 1018: {{.*}} leal -16(%ebx), %eax +// DIS-NEXT: 101e: {{.*}} calll 77 +// DIS-NEXT: 1023: {{.*}} leal 8(%eax), %edx +// DIS-NEXT: 1029: {{.*}} leal -16(%ebx), %eax +// DIS-NEXT: 102f: {{.*}} calll 60 +// DIS-NEXT: 1034: {{.*}} leal 12(%eax), %edx // Initial exec model: -// DIS-NEXT: 103a: 65 a1 00 00 00 00 movl %gs:0, %eax -// DIS-NEXT: 1040: 03 83 f8 ff ff ff addl -8(%ebx), %eax -// DIS-NEXT: 1046: 65 a1 00 00 00 00 movl %gs:0, %eax -// DIS-NEXT: 104c: 03 83 fc ff ff ff addl -4(%ebx), %eax +// DIS-NEXT: 103a: {{.*}} movl %gs:0, %eax +// DIS-NEXT: 1040: {{.*}} addl -8(%ebx), %eax +// DIS-NEXT: 1046: {{.*}} movl %gs:0, %eax +// DIS-NEXT: 104c: {{.*}} addl -4(%ebx), %eax diff --git a/test/ELF/tls-offset.s b/test/ELF/tls-offset.s index 062def4e14fc..75d9af7f5b72 100644 --- a/test/ELF/tls-offset.s +++ b/test/ELF/tls-offset.s @@ -10,7 +10,7 @@ // RUN: .tbss : { *(.tbss) } \ // RUN: .data.rel.ro : { *(.data.rel.ro) } \ // RUN: }" > %t.script - // RUN: ld.lld -T %t.script %t -o %tout2 +// RUN: ld.lld -T %t.script %t -o %tout2 // RUN: echo SCRIPT // RUN: llvm-readobj -s %tout2 | FileCheck %s .global _start @@ -61,6 +61,6 @@ _start: // CHECK-NEXT: SHF_ALLOC // CHECK-NEXT: SHF_WRITE // CHECK-NEXT: ] -// CHECK-NEXT: Address: 0x202010 -// CHECK-NEXT: Offset: 0x2010 +// CHECK-NEXT: Address: 0x202004 +// CHECK-NEXT: Offset: 0x2004 // CHECK-NEXT: Size: 4 diff --git a/test/ELF/tls-opt-iele-i686-nopic.s b/test/ELF/tls-opt-iele-i686-nopic.s index b6608c16551c..a883bce511a6 100644 --- a/test/ELF/tls-opt-iele-i686-nopic.s +++ b/test/ELF/tls-opt-iele-i686-nopic.s @@ -13,8 +13,8 @@ // GOTREL-NEXT: SHF_ALLOC // GOTREL-NEXT: SHF_WRITE // GOTREL-NEXT: ] -// GOTREL-NEXT: Address: 0x12058 -// GOTREL-NEXT: Offset: 0x2058 +// GOTREL-NEXT: Address: 0x12060 +// GOTREL-NEXT: Offset: 0x2060 // GOTREL-NEXT: Size: 8 // GOTREL-NEXT: Link: 0 // GOTREL-NEXT: Info: 0 @@ -23,8 +23,8 @@ // GOTREL-NEXT: } // GOTREL: Relocations [ // GOTREL-NEXT: Section ({{.*}}) .rel.dyn { -// GOTREL-NEXT: 0x12058 R_386_TLS_TPOFF tlsshared0 0x0 -// GOTREL-NEXT: 0x1205C R_386_TLS_TPOFF tlsshared1 0x0 +// GOTREL-NEXT: 0x12060 R_386_TLS_TPOFF tlsshared0 0x0 +// GOTREL-NEXT: 0x12064 R_386_TLS_TPOFF tlsshared1 0x0 // GOTREL-NEXT: } // GOTREL-NEXT: ] @@ -32,24 +32,24 @@ // DISASM-NEXT: _start: // 4294967288 = 0xFFFFFFF8 // 4294967292 = 0xFFFFFFFC -// 73808 = (.got)[0] = 0x12058 -// 73812 = (.got)[1] = 0x1205C -// DISASM-NEXT: 11000: c7 c1 f8 ff ff ff movl $4294967288, %ecx -// DISASM-NEXT: 11006: 65 8b 01 movl %gs:(%ecx), %eax -// DISASM-NEXT: 11009: b8 f8 ff ff ff movl $4294967288, %eax -// DISASM-NEXT: 1100e: 65 8b 00 movl %gs:(%eax), %eax -// DISASM-NEXT: 11011: 81 c1 f8 ff ff ff addl $4294967288, %ecx -// DISASM-NEXT: 11017: 65 8b 01 movl %gs:(%ecx), %eax -// DISASM-NEXT: 1101a: c7 c1 fc ff ff ff movl $4294967292, %ecx -// DISASM-NEXT: 11020: 65 8b 01 movl %gs:(%ecx), %eax -// DISASM-NEXT: 11023: b8 fc ff ff ff movl $4294967292, %eax -// DISASM-NEXT: 11028: 65 8b 00 movl %gs:(%eax), %eax -// DISASM-NEXT: 1102b: 81 c1 fc ff ff ff addl $4294967292, %ecx -// DISASM-NEXT: 11031: 65 8b 01 movl %gs:(%ecx), %eax -// DISASM-NEXT: 11034: 8b 0d 58 20 01 00 movl 73816, %ecx -// DISASM-NEXT: 1103a: 65 8b 01 movl %gs:(%ecx), %eax -// DISASM-NEXT: 1103d: 03 0d 5c 20 01 00 addl 73820, %ecx -// DISASM-NEXT: 11043: 65 8b 01 movl %gs:(%ecx), %eax +// 73824 = (.got)[0] = 0x12060 +// 73828 = (.got)[1] = 0x12064 +// DISASM-NEXT: 11000: {{.*}} movl $4294967288, %ecx +// DISASM-NEXT: 11006: {{.*}} movl %gs:(%ecx), %eax +// DISASM-NEXT: 11009: {{.*}} movl $4294967288, %eax +// DISASM-NEXT: 1100e: {{.*}} movl %gs:(%eax), %eax +// DISASM-NEXT: 11011: {{.*}} addl $4294967288, %ecx +// DISASM-NEXT: 11017: {{.*}} movl %gs:(%ecx), %eax +// DISASM-NEXT: 1101a: {{.*}} movl $4294967292, %ecx +// DISASM-NEXT: 11020: {{.*}} movl %gs:(%ecx), %eax +// DISASM-NEXT: 11023: {{.*}} movl $4294967292, %eax +// DISASM-NEXT: 11028: {{.*}} movl %gs:(%eax), %eax +// DISASM-NEXT: 1102b: {{.*}} addl $4294967292, %ecx +// DISASM-NEXT: 11031: {{.*}} movl %gs:(%ecx), %eax +// DISASM-NEXT: 11034: {{.*}} movl 73824, %ecx +// DISASM-NEXT: 1103a: {{.*}} movl %gs:(%ecx), %eax +// DISASM-NEXT: 1103d: {{.*}} addl 73828, %ecx +// DISASM-NEXT: 11043: {{.*}} movl %gs:(%ecx), %eax .type tlslocal0,@object .section .tbss,"awT",@nobits diff --git a/unittests/CoreTests/CMakeLists.txt b/unittests/CoreTests/CMakeLists.txt index 72e7c443f8cd..9f68f56a6c03 100644 --- a/unittests/CoreTests/CMakeLists.txt +++ b/unittests/CoreTests/CMakeLists.txt @@ -3,5 +3,5 @@ add_lld_unittest(CoreTests ) target_link_libraries(CoreTests - ${LLVM_PTHREAD_LIB} + lldCore ${LLVM_PTHREAD_LIB} )