aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--COFF/Chunks.h2
-rw-r--r--COFF/ICF.cpp42
-rw-r--r--COFF/PDB.cpp4
-rw-r--r--ELF/Config.h2
-rw-r--r--ELF/Driver.cpp38
-rw-r--r--ELF/InputFiles.cpp21
-rw-r--r--ELF/InputFiles.h10
-rw-r--r--ELF/LinkerScript.cpp132
-rw-r--r--ELF/LinkerScript.h6
-rw-r--r--ELF/Options.td1
-rw-r--r--ELF/OutputSections.cpp24
-rw-r--r--ELF/Relocations.cpp16
-rw-r--r--ELF/SymbolTable.cpp9
-rw-r--r--ELF/Symbols.cpp7
-rw-r--r--ELF/SyntheticSections.cpp20
-rw-r--r--ELF/Target.cpp42
-rw-r--r--ELF/Target.h2
-rw-r--r--ELF/Writer.cpp89
-rw-r--r--ELF/Writer.h3
-rw-r--r--include/lld/Core/Parallel.h279
-rw-r--r--include/lld/Core/TaskGroup.h65
-rw-r--r--include/lld/Support/Memory.h63
-rw-r--r--lib/Core/CMakeLists.txt4
-rw-r--r--lib/Core/TaskGroup.cpp141
-rw-r--r--test/ELF/Inputs/i386-static-tls-model1.s10
-rw-r--r--test/ELF/Inputs/i386-static-tls-model2.s9
-rw-r--r--test/ELF/Inputs/i386-static-tls-model3.s9
-rw-r--r--test/ELF/Inputs/i386-static-tls-model4.s9
-rw-r--r--test/ELF/defsym.s5
-rw-r--r--test/ELF/i386-static-tls-model.s20
-rw-r--r--test/ELF/i386-tls-ie-shared.s52
-rw-r--r--test/ELF/linkerscript/Inputs/compress-debug-sections.s3
-rw-r--r--test/ELF/linkerscript/compress-debug-sections.s36
-rw-r--r--test/ELF/lto/Inputs/duplicated-name.ll6
-rw-r--r--test/ELF/lto/archive-no-index.ll28
-rw-r--r--test/ELF/lto/duplicated-name.ll15
-rw-r--r--test/ELF/lto/thin-archivecollision.ll19
-rw-r--r--test/ELF/tls-dynamic-i686.s46
-rw-r--r--test/ELF/tls-offset.s6
-rw-r--r--test/ELF/tls-opt-iele-i686-nopic.s44
-rw-r--r--unittests/CoreTests/CMakeLists.txt2
42 files changed, 704 insertions, 638 deletions
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<void(size_t, size_t)> Fn);
- void forEachColor(std::function<void(size_t, size_t)> Fn);
+ void forEachClass(std::function<void(size_t, size_t)> Fn);
std::vector<SectionChunk *> 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<DefinedRegular>(B1))
if (auto *D2 = dyn_cast<DefinedRegular>(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<DefinedRegular>(B1))
if (auto *D2 = dyn_cast<DefinedRegular>(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<void(size_t, size_t)> 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<void(size_t, size_t)> Fn) {
+// Call Fn on each class group.
+void ICF::forEachClass(std::function<void(size_t, size_t)> 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<void(size_t, size_t)> 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<Chunk *> &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<Chunk *> &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<CVSymbolArray> 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<uint64_t> SectionStartMap;
@@ -99,7 +100,6 @@ struct Configuration {
std::vector<SymbolVersion> VersionScriptLocals;
std::vector<uint8_t> 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<ELFKind, uint16_t, uint8_t> 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<MemoryBufferRef>
-static getArchiveMembers(MemoryBufferRef MB) {
+std::vector<std::pair<MemoryBufferRef, uint64_t>> static getArchiveMembers(
+ MemoryBufferRef MB) {
std::unique_ptr<Archive> File =
check(Archive::create(MB),
MB.getBufferIdentifier() + ": failed to parse archive");
- std::vector<MemoryBufferRef> V;
+ std::vector<std::pair<MemoryBufferRef, uint64_t>> V;
Error Err = Error::success();
for (const ErrorOr<Archive::Child> &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<ArchiveFile>(MBRef));
+
+ std::unique_ptr<Archive> 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<LazyObjectFile>(P.first, Path, P.second));
+ return;
+ }
+
+ // Handle the regular case.
+ Files.push_back(make<ArchiveFile>(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<LazyObjectFile>(MBRef));
+ Files.push_back(make<LazyObjectFile>(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<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
}
}
-template <class ELFT> void ArchiveFile::parse() {
- File = check(Archive::create(MB),
- MB.getBufferIdentifier() + ": failed to parse archive");
+ArchiveFile::ArchiveFile(std::unique_ptr<Archive> &&File)
+ : InputFile(ArchiveKind, File->getMemoryBufferRef()),
+ File(std::move(File)) {}
- // Read the symbol table to construct Lazy objects.
- for (const Archive::Symbol &Sym : File->symbols()) {
+template <class ELFT> void ArchiveFile::parse() {
+ for (const Archive::Symbol &Sym : File->symbols())
Symtab<ELFT>::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 <class ELFT> void LazyObjectFile::parse() {
for (StringRef Sym : getSymbols())
Symtab<ELFT>::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 <class ELFT> void parse();
MemoryBufferRef getBuffer();
+ InputFile *fetch();
private:
std::vector<StringRef> getSymbols();
@@ -234,12 +239,13 @@ private:
std::vector<StringRef> 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<Archive> &&File);
static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
template <class ELFT> 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<InputSection>(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<BaseCommand *> 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<section> have been sorted in order of ascending
// address. We must lower StartAddr if the lowest -T<section address> as
@@ -488,6 +483,11 @@ void LinkerScript::addOrphanSections(OutputSectionFactory &Factory) {
} else {
auto *Cmd = cast<OutputSectionCommand>(*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<InputSectionDescription>("");
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<OutputSectionCommand>(Base);
+
+ for (int I = 0, E = Opt.Commands.size(); I != E; ++I) {
+ auto *Cmd = dyn_cast<OutputSectionCommand>(Opt.Commands[I]);
if (!Cmd)
continue;
if (OutputSection *Sec = Cmd->Sec) {
@@ -692,6 +696,7 @@ void LinkerScript::adjustSectionsBeforeSorting() {
continue;
auto *OutSec = make<OutputSection>(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<PhdrEntry> &Phdrs,
+ ArrayRef<OutputSection *> 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<PhdrEntry> &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<OutputSectionCommand>(Base))
+ if (Cmd->Sec == Sec)
+ return true;
+ return false;
+ });
+ if (I == Opt.Commands.end())
return;
-
- auto *Cmd = dyn_cast<OutputSectionCommand>(Opt.Commands[I]);
+ auto *Cmd = cast<OutputSectionCommand>(*I);
for (BaseCommand *Base : Cmd->Commands)
if (auto *Data = dyn_cast<BytesDataCommand>(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<OutputSectionCommand>(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<OutputSection *> *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<PhdrEntry> &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<L>;
def alias_define_common_d: Flag<["-"], "d">, Alias<define_common>;
def alias_define_common_dc: F<"dc">, Alias<define_common>;
def alias_define_common_dp: F<"dp">, Alias<define_common>;
+def alias_defsym: S<"defsym">, Alias<defsym>;
def alias_discard_all_x: Flag<["-"], "x">, Alias<discard_all>;
def alias_discard_locals_X: Flag<["-"], "X">, Alias<discard_locals>;
def alias_dynamic_list: J<"dynamic-list=">, Alias<dynamic_list>;
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 <class ELFT> 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 <class ELFT> 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<R_ABS, R_TLSLD, R_TLSLD_PC>(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<R_GOT, R_GOT_FROM_END, R_GOT_PC, R_GOT_PAGE_PC>(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<ELFT>::addLazyObject(StringRef Name, LazyObjectFile &Obj) {
return;
// See comment for addLazyArchive above.
- if (S->isWeak()) {
+ if (S->isWeak())
replaceBody<LazyObject>(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 <class ELFT> void DynamicSection<ELFT>::addEntries() {
if (!Config->SoName.empty())
add({DT_SONAME, In<ELFT>::DynStrTab->addString(Config->SoName)});
+ if (!Config->Shared && !Config->Relocatable)
+ add({DT_DEBUG, (uint64_t)0});
+}
+
+// Add remaining entries to complete .dynamic contents.
+template <class ELFT> void DynamicSection<ELFT>::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 <class ELFT> void DynamicSection<ELFT>::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 <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
- if (this->Size)
- return; // Already finalized.
-
this->Link = In<ELFT>::DynStrTab->OutSec->SectionIndex;
if (In<ELFT>::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[] = {
@@ -772,17 +764,6 @@ bool X86_64TargetInfo<ELFT>::isPicRel(uint32_t Type) const {
}
template <class ELFT>
-bool X86_64TargetInfo<ELFT>::isTlsInitialExecRel(uint32_t Type) const {
- return Type == R_X86_64_GOTTPOFF;
-}
-
-template <class ELFT>
-bool X86_64TargetInfo<ELFT>::isTlsLocalDynamicRel(uint32_t Type) const {
- return Type == R_X86_64_DTPOFF32 || Type == R_X86_64_DTPOFF64 ||
- Type == R_X86_64_TLSLD;
-}
-
-template <class ELFT>
void X86_64TargetInfo<ELFT>::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type,
uint64_t Val) const {
// Convert
@@ -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 <class ELFT> void Writer<ELFT>::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 <class ELFT>
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<ELFT>(A, B);
@@ -1021,9 +1016,8 @@ template <class ELFT> void Writer<ELFT>::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 <class ELFT> void Writer<ELFT>::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 <class ELFT> std::vector<PhdrEntry> Writer<ELFT>::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 <class ELFT> void Writer<ELFT>::fixSectionAlignments() {
}
}
-bool elf::allocateHeaders(std::vector<PhdrEntry> &Phdrs,
- ArrayRef<OutputSection *> 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 <class ELFT> void Writer<ELFT>::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<section> 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<PhdrEntry> &, llvm::ArrayRef<OutputSection *>,
- uint64_t Min);
-
template <class ELFT> 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 <algorithm>
-#include <atomic>
-#include <condition_variable>
-#include <mutex>
-#include <stack>
#if defined(_MSC_VER) && LLVM_ENABLE_THREADS
#include <concrt.h>
@@ -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<std::mutex> lock(_condMut);
- ++_count;
- }
-
- void dec() {
- std::unique_lock<std::mutex> lock(_condMut);
- if (--_count == 0)
- _cond.notify_all();
- }
-
- void sync() const {
- std::unique_lock<std::mutex> 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<void()> func) = 0;
-};
-
-#if !defined(LLVM_ENABLE_THREADS) || LLVM_ENABLE_THREADS == 0
-class SyncExecutor : public Executor {
-public:
- virtual void add(std::function<void()> 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<void()> task) : _task(task) {}
-
- std::function<void()> _task;
-
- static void run(void *p) {
- Taskish *self = static_cast<Taskish *>(p);
- self->_task();
- concurrency::Free(self);
- }
- };
-
-public:
- virtual void add(std::function<void()> 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<std::mutex> lock(_mutex);
- _stop = true;
- lock.unlock();
- _cond.notify_all();
- // Wait for ~Latch.
- }
-
- void add(std::function<void()> f) override {
- std::unique_lock<std::mutex> lock(_mutex);
- _workStack.push(f);
- lock.unlock();
- _cond.notify_one();
- }
-
-private:
- void work() {
- while (true) {
- std::unique_lock<std::mutex> 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<bool> _stop;
- std::stack<std::function<void()>> _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<void()> 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 <class RandomAccessIterator, class Comp>
+#if !LLVM_ENABLE_THREADS
+template <class RandomAccessIterator, class Comparator>
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<RandomAccessIterator>::value_type>()) {
- std::sort(start, end, comp);
+ std::sort(Start, End, Comp);
}
#elif defined(_MSC_VER)
// Use ppl parallel_sort on Windows.
-template <class RandomAccessIterator, class Comp>
+template <class RandomAccessIterator, class Comparator>
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<RandomAccessIterator>::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 <class RandomAccessIterator, class Comp>
-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 <class RandomAccessIterator, class Comp>
-void parallel_quick_sort(RandomAccessIterator start, RandomAccessIterator end,
- const Comp &comp, TaskGroup &tg, size_t depth) {
+template <class RandomAccessIterator, class Comparator>
+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 <class RandomAccessIterator, class Comparator>
+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 <class RandomAccessIterator, class Comp>
+template <class RandomAccessIterator, class Comparator>
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<RandomAccessIterator>::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 <class T> void parallel_sort(T *start, T *end) {
- parallel_sort(start, end, std::less<T>());
+template <class T> void parallel_sort(T *Start, T *End) {
+ parallel_sort(Start, End, std::less<T>());
}
-#if !defined(LLVM_ENABLE_THREADS) || LLVM_ENABLE_THREADS == 0
+#if !LLVM_ENABLE_THREADS
template <class IterTy, class FuncTy>
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 <class IndexTy, class FuncTy>
@@ -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 <condition_variable>
+#include <functional>
+#include <mutex>
+
+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<std::mutex> lock(_condMut);
+ ++_count;
+ }
+
+ void dec() {
+ std::unique_lock<std::mutex> lock(_condMut);
+ if (--_count == 0)
+ _cond.notify_all();
+ }
+
+ void sync() const {
+ std::unique_lock<std::mutex> 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<void()> 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 <vector>
-
-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<SpecificAllocBase *> Instances;
-};
-
-template <class T> struct SpecificAlloc : public SpecificAllocBase {
- void reset() override { Alloc.DestroyAll(); }
- llvm::SpecificBumpPtrAllocator<T> Alloc;
-};
-
-// Use this arena if your object has a destructor.
-// Your destructor will be invoked from freeArena().
-template <typename T, typename... U> inline T *make(U &&... Args) {
- static SpecificAlloc<T> Alloc;
- return new (Alloc.Alloc.Allocate()) T(std::forward<U>(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 <atomic>
+#include <stack>
+#include <thread>
+
+#if defined(_MSC_VER) && LLVM_ENABLE_THREADS
+#include <concrt.h>
+#include <ppl.h>
+#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<void()> func) = 0;
+
+ static Executor *getDefaultExecutor();
+};
+
+#if !LLVM_ENABLE_THREADS
+class SyncExecutor : public Executor {
+public:
+ virtual void add(std::function<void()> 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<void()> Task) : Task(Task) {}
+
+ std::function<void()> Task;
+
+ static void run(void *P) {
+ Taskish *Self = static_cast<Taskish *>(P);
+ Self->Task();
+ concurrency::Free(Self);
+ }
+ };
+
+public:
+ virtual void add(std::function<void()> 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<std::mutex> Lock(Mutex);
+ Stop = true;
+ Lock.unlock();
+ Cond.notify_all();
+ // Wait for ~Latch.
+ }
+
+ void add(std::function<void()> F) override {
+ std::unique_lock<std::mutex> Lock(Mutex);
+ WorkStack.push(F);
+ Lock.unlock();
+ Cond.notify_one();
+ }
+
+private:
+ void work() {
+ while (true) {
+ std::unique_lock<std::mutex> 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<bool> Stop{false};
+ std::stack<std::function<void()>> WorkStack;
+ std::mutex Mutex;
+ std::condition_variable Cond;
+ Latch Done;
+};
+
+Executor *Executor::getDefaultExecutor() {
+ static ThreadPoolExecutor exec;
+ return &exec;
+}
+#endif
+}
+
+void TaskGroup::spawn(std::function<void()> 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}
)