Vendor import of lld trunk r302418:

https://llvm.org/svn/llvm-project/lld/trunk@302418
This commit is contained in:
Dimitry Andric 2017-05-08 17:13:44 +00:00
parent d803cda429
commit fbe69f787a
42 changed files with 702 additions and 636 deletions

View File

@ -221,3 +221,4 @@ endif()
add_subdirectory(docs) add_subdirectory(docs)
add_subdirectory(COFF) add_subdirectory(COFF)
add_subdirectory(ELF) add_subdirectory(ELF)

View File

@ -201,7 +201,7 @@ private:
// Used for ICF (Identical COMDAT Folding) // Used for ICF (Identical COMDAT Folding)
void replace(SectionChunk *Other); 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. // Sym points to a section symbol if this is a COMDAT chunk.
DefinedRegular *Sym = nullptr; DefinedRegular *Sym = nullptr;

View File

@ -49,10 +49,10 @@ private:
size_t findBoundary(size_t Begin, size_t End); 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); 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; std::vector<SectionChunk *> Chunks;
int Cnt = 0; int Cnt = 0;
@ -85,7 +85,7 @@ bool ICF::isEligible(SectionChunk *C) {
return C->isCOMDAT() && C->isLive() && Global && Executable && !Writable; 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) { void ICF::segregate(size_t Begin, size_t End, bool Constant) {
while (Begin < End) { while (Begin < End) {
// Divide [Begin, End) into two. Let Mid be the start index of the // 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). // Split [Begin, End) into [Begin, Mid) and [Mid, End).
uint32_t Id = NextId++; uint32_t Id = NextId++;
for (size_t I = Begin; I < Mid; ++I) 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 we created a group, we need to iterate the main loop again.
if (Mid != End) 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 *D1 = dyn_cast<DefinedRegular>(B1))
if (auto *D2 = dyn_cast<DefinedRegular>(B2)) if (auto *D2 = dyn_cast<DefinedRegular>(B2))
return D1->getValue() == D2->getValue() && 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; return false;
}; };
if (!std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq)) 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; return true;
if (auto *D1 = dyn_cast<DefinedRegular>(B1)) if (auto *D1 = dyn_cast<DefinedRegular>(B1))
if (auto *D2 = dyn_cast<DefinedRegular>(B2)) 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 false;
}; };
return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq); return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq);
@ -163,12 +163,12 @@ bool ICF::equalsVariable(const SectionChunk *A, const SectionChunk *B) {
size_t ICF::findBoundary(size_t Begin, size_t End) { size_t ICF::findBoundary(size_t Begin, size_t End) {
for (size_t I = Begin + 1; I < End; ++I) 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 I;
return End; 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) { std::function<void(size_t, size_t)> Fn) {
if (Begin > 0) if (Begin > 0)
Begin = findBoundary(Begin - 1, End); Begin = findBoundary(Begin - 1, End);
@ -180,12 +180,12 @@ void ICF::forEachColorRange(size_t Begin, size_t End,
} }
} }
// Call Fn on each color group. // Call Fn on each class group.
void ICF::forEachColor(std::function<void(size_t, size_t)> Fn) { void ICF::forEachClass(std::function<void(size_t, size_t)> Fn) {
// If the number of sections are too small to use threading, // If the number of sections are too small to use threading,
// call Fn sequentially. // call Fn sequentially.
if (Chunks.size() < 1024) { if (Chunks.size() < 1024) {
forEachColorRange(0, Chunks.size(), Fn); forEachClassRange(0, Chunks.size(), Fn);
return; return;
} }
@ -193,9 +193,9 @@ void ICF::forEachColor(std::function<void(size_t, size_t)> Fn) {
size_t NumShards = 256; size_t NumShards = 256;
size_t Step = Chunks.size() / NumShards; size_t Step = Chunks.size() / NumShards;
parallel_for(size_t(0), NumShards, [&](size_t I) { 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. // Merge identical COMDAT sections.
@ -209,11 +209,11 @@ void ICF::run(const std::vector<Chunk *> &Vec) {
continue; continue;
if (isEligible(SC)) { if (isEligible(SC)) {
// Set MSB to 1 to avoid collisions with non-hash colors. // Set MSB to 1 to avoid collisions with non-hash classs.
SC->Color[0] = getHash(SC) | (1 << 31); SC->Class[0] = getHash(SC) | (1 << 31);
Chunks.push_back(SC); Chunks.push_back(SC);
} else { } 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. // the same group are consecutive in the vector.
std::stable_sort(Chunks.begin(), Chunks.end(), std::stable_sort(Chunks.begin(), Chunks.end(),
[](SectionChunk *A, SectionChunk *B) { [](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. // 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; ++Cnt;
// Split groups by comparing relocations until convergence is obtained. // Split groups by comparing relocations until convergence is obtained.
do { do {
Repeat = false; Repeat = false;
forEachColor( forEachClass(
[&](size_t Begin, size_t End) { segregate(Begin, End, false); }); [&](size_t Begin, size_t End) { segregate(Begin, End, false); });
++Cnt; ++Cnt;
} while (Repeat); } while (Repeat);
log("ICF needed " + Twine(Cnt) + " iterations"); log("ICF needed " + Twine(Cnt) + " iterations");
// Merge sections in the same colors. // Merge sections in the same classs.
forEachColor([&](size_t Begin, size_t End) { forEachClass([&](size_t Begin, size_t End) {
if (End - Begin == 1) if (End - Begin == 1)
return; return;

View File

@ -133,7 +133,7 @@ static void dumpDebugT(ScopedPrinter &W, ObjectFile *File) {
if (Data.empty()) if (Data.empty())
return; return;
TypeDatabase TDB; TypeDatabase TDB(0);
TypeDumpVisitor TDV(TDB, &W, false); TypeDumpVisitor TDV(TDB, &W, false);
// Use a default implementation that does not follow type servers and instead // Use a default implementation that does not follow type servers and instead
// just dumps the contents of the TypeServer2 record. // 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())) if (auto EC = Reader.readArray(Symbols, Reader.getLength()))
fatal(EC, "StreamReader.readArray<CVSymbolArray> failed"); fatal(EC, "StreamReader.readArray<CVSymbolArray> failed");
TypeDatabase TDB; TypeDatabase TDB(0);
CVSymbolDumper SymbolDumper(W, TDB, nullptr, false); CVSymbolDumper SymbolDumper(W, TDB, nullptr, false);
if (auto EC = SymbolDumper.dump(Symbols)) if (auto EC = SymbolDumper.dump(Symbols))
fatal(EC, "CVSymbolDumper::dump failed"); fatal(EC, "CVSymbolDumper::dump failed");

View File

@ -73,6 +73,7 @@ struct VersionDefinition {
// Most fields are initialized by the driver. // Most fields are initialized by the driver.
struct Configuration { struct Configuration {
InputFile *FirstElf = nullptr; InputFile *FirstElf = nullptr;
bool HasStaticTlsModel = false;
uint8_t OSABI = 0; uint8_t OSABI = 0;
llvm::CachePruningPolicy ThinLTOCachePolicy; llvm::CachePruningPolicy ThinLTOCachePolicy;
llvm::StringMap<uint64_t> SectionStartMap; llvm::StringMap<uint64_t> SectionStartMap;
@ -99,7 +100,6 @@ struct Configuration {
std::vector<SymbolVersion> VersionScriptLocals; std::vector<SymbolVersion> VersionScriptLocals;
std::vector<uint8_t> BuildIdVector; std::vector<uint8_t> BuildIdVector;
bool AllowMultipleDefinition; bool AllowMultipleDefinition;
bool ArchiveWithoutSymbolsSeen = false;
bool AsNeeded = false; bool AsNeeded = false;
bool Bsymbolic; bool Bsymbolic;
bool BsymbolicFunctions; bool BsymbolicFunctions;

View File

@ -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. // Returns slices of MB by parsing MB as an archive file.
// Each slice consists of a member file in the archive. // Each slice consists of a member file in the archive.
std::vector<MemoryBufferRef> std::vector<std::pair<MemoryBufferRef, uint64_t>> static getArchiveMembers(
static getArchiveMembers(MemoryBufferRef MB) { MemoryBufferRef MB) {
std::unique_ptr<Archive> File = std::unique_ptr<Archive> File =
check(Archive::create(MB), check(Archive::create(MB),
MB.getBufferIdentifier() + ": failed to parse archive"); MB.getBufferIdentifier() + ": failed to parse archive");
std::vector<MemoryBufferRef> V; std::vector<std::pair<MemoryBufferRef, uint64_t>> V;
Error Err = Error::success(); Error Err = Error::success();
for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) { for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) {
Archive::Child C = Archive::Child C =
@ -139,7 +139,7 @@ static getArchiveMembers(MemoryBufferRef MB) {
check(C.getMemoryBufferRef(), check(C.getMemoryBufferRef(),
MB.getBufferIdentifier() + MB.getBufferIdentifier() +
": could not get the buffer for a child of the archive"); ": 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) if (Err)
fatal(MB.getBufferIdentifier() + ": Archive::children failed: " + fatal(MB.getBufferIdentifier() + ": Archive::children failed: " +
@ -152,8 +152,7 @@ static getArchiveMembers(MemoryBufferRef MB) {
return V; return V;
} }
// Opens and parses a file. Path has to be resolved already. // Opens a file and create a file object. Path has to be resolved already.
// Newly created memory buffers are owned by this driver.
void LinkerDriver::addFile(StringRef Path, bool WithLOption) { void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
using namespace sys::fs; using namespace sys::fs;
@ -171,14 +170,31 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
case file_magic::unknown: case file_magic::unknown:
readLinkerScript(MBRef); readLinkerScript(MBRef);
return; return;
case file_magic::archive: case file_magic::archive: {
// Handle -whole-archive.
if (InWholeArchive) { if (InWholeArchive) {
for (MemoryBufferRef MB : getArchiveMembers(MBRef)) for (const auto &P : getArchiveMembers(MBRef))
Files.push_back(createObjectFile(MB, Path)); Files.push_back(createObjectFile(P.first, Path, P.second));
return; 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; return;
}
case file_magic::elf_shared_object: case file_magic::elf_shared_object:
if (Config->Relocatable) { if (Config->Relocatable) {
error("attempted static link of dynamic object " + Path); error("attempted static link of dynamic object " + Path);
@ -199,7 +215,7 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
return; return;
default: default:
if (InLib) if (InLib)
Files.push_back(make<LazyObjectFile>(MBRef)); Files.push_back(make<LazyObjectFile>(MBRef, "", 0));
else else
Files.push_back(createObjectFile(MBRef)); Files.push_back(createObjectFile(MBRef));
} }

View File

@ -596,17 +596,13 @@ SymbolBody *elf::ObjectFile<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
} }
} }
ArchiveFile::ArchiveFile(std::unique_ptr<Archive> &&File)
: InputFile(ArchiveKind, File->getMemoryBufferRef()),
File(std::move(File)) {}
template <class ELFT> void ArchiveFile::parse() { template <class ELFT> void ArchiveFile::parse() {
File = check(Archive::create(MB), for (const Archive::Symbol &Sym : File->symbols())
MB.getBufferIdentifier() + ": failed to parse archive");
// Read the symbol table to construct Lazy objects.
for (const Archive::Symbol &Sym : File->symbols()) {
Symtab<ELFT>::X->addLazyArchive(this, Sym); 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. // Returns a buffer pointing to a member file containing a given symbol.
@ -981,6 +977,13 @@ MemoryBufferRef LazyObjectFile::getBuffer() {
return MB; 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() { template <class ELFT> void LazyObjectFile::parse() {
for (StringRef Sym : getSymbols()) for (StringRef Sym : getSymbols())
Symtab<ELFT>::X->addLazyObject(Sym, *this); Symtab<ELFT>::X->addLazyObject(Sym, *this);

View File

@ -219,7 +219,11 @@ private:
// archive file semantics. // archive file semantics.
class LazyObjectFile : public InputFile { class LazyObjectFile : public InputFile {
public: 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) { static bool classof(const InputFile *F) {
return F->kind() == LazyObjectKind; return F->kind() == LazyObjectKind;
@ -227,6 +231,7 @@ public:
template <class ELFT> void parse(); template <class ELFT> void parse();
MemoryBufferRef getBuffer(); MemoryBufferRef getBuffer();
InputFile *fetch();
private: private:
std::vector<StringRef> getSymbols(); std::vector<StringRef> getSymbols();
@ -234,12 +239,13 @@ private:
std::vector<StringRef> getBitcodeSymbols(); std::vector<StringRef> getBitcodeSymbols();
bool Seen = false; bool Seen = false;
uint64_t OffsetInArchive;
}; };
// An ArchiveFile object represents a .a file. // An ArchiveFile object represents a .a file.
class ArchiveFile : public InputFile { class ArchiveFile : public InputFile {
public: 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; } static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
template <class ELFT> void parse(); template <class ELFT> void parse();

View File

@ -406,27 +406,22 @@ void LinkerScript::processCommands(OutputSectionFactory &Factory) {
} }
// Add input sections to an output section. // Add input sections to an output section.
unsigned Pos = 0; for (InputSectionBase *S : V)
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++;
Factory.addInputSec(S, Cmd->Name, Cmd->Sec); Factory.addInputSec(S, Cmd->Name, Cmd->Sec);
if (OutputSection *Sec = Cmd->Sec) {
assert(Sec->SectionIndex == INT_MAX);
Sec->SectionIndex = I;
} }
} }
} }
CurOutSec = nullptr; CurOutSec = nullptr;
} }
void LinkerScript::fabricateDefaultCommands(bool AllocateHeader) { void LinkerScript::fabricateDefaultCommands() {
std::vector<BaseCommand *> Commands; std::vector<BaseCommand *> Commands;
// Define start address // Define start address
uint64_t StartAddr = Config->ImageBase; uint64_t StartAddr = Config->ImageBase + elf::getHeaderSize();
if (AllocateHeader)
StartAddr += elf::getHeaderSize();
// The Sections with -T<section> have been sorted in order of ascending // The Sections with -T<section> have been sorted in order of ascending
// address. We must lower StartAddr if the lowest -T<section address> as // address. We must lower StartAddr if the lowest -T<section address> as
@ -488,6 +483,11 @@ void LinkerScript::addOrphanSections(OutputSectionFactory &Factory) {
} else { } else {
auto *Cmd = cast<OutputSectionCommand>(*I); auto *Cmd = cast<OutputSectionCommand>(*I);
Factory.addInputSec(S, Name, Cmd->Sec); 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>(""); auto *ISD = make<InputSectionDescription>("");
ISD->Sections.push_back(S); ISD->Sections.push_back(S);
Cmd->Commands.push_back(ISD); Cmd->Commands.push_back(ISD);
@ -495,17 +495,22 @@ void LinkerScript::addOrphanSections(OutputSectionFactory &Factory) {
} }
} }
static bool isTbss(OutputSection *Sec) { uint64_t LinkerScript::advance(uint64_t Size, unsigned Align) {
return (Sec->Flags & SHF_TLS) && Sec->Type == SHT_NOBITS; 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) { void LinkerScript::output(InputSection *S) {
bool IsTbss = isTbss(CurOutSec); uint64_t Pos = advance(S->getSize(), S->Alignment);
S->OutSecOff = Pos - S->getSize() - CurOutSec->Addr;
uint64_t Pos = IsTbss ? Dot + ThreadBssOffset : Dot;
Pos = alignTo(Pos, S->Alignment);
S->OutSecOff = Pos - CurOutSec->Addr;
Pos += S->getSize();
// Update output section size after adding each section. This is so that // Update output section size after adding each section. This is so that
// SIZEOF works correctly in the case below: // SIZEOF works correctly in the case below:
@ -524,11 +529,6 @@ void LinkerScript::output(InputSection *S) {
" bytes"); " bytes");
} }
} }
if (IsTbss)
ThreadBssOffset = Pos - Dot;
else
Dot = Pos;
} }
void LinkerScript::switchTo(OutputSection *Sec) { void LinkerScript::switchTo(OutputSection *Sec) {
@ -536,9 +536,7 @@ void LinkerScript::switchTo(OutputSection *Sec) {
return; return;
CurOutSec = Sec; CurOutSec = Sec;
CurOutSec->Addr = advance(0, CurOutSec->Alignment);
Dot = alignTo(Dot, CurOutSec->Alignment);
CurOutSec->Addr = isTbss(CurOutSec) ? Dot + ThreadBssOffset : Dot;
// If neither AT nor AT> is specified for an allocatable section, the linker // 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 // 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; Dot = CurMemRegion->Offset;
switchTo(Sec); 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) for (BaseCommand *C : Cmd->Commands)
process(*C); process(*C);
} }
@ -678,8 +681,9 @@ void LinkerScript::adjustSectionsBeforeSorting() {
// consequeces and gives us a section to put the symbol in. // consequeces and gives us a section to put the symbol in.
uint64_t Flags = SHF_ALLOC; uint64_t Flags = SHF_ALLOC;
uint32_t Type = SHT_PROGBITS; 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) if (!Cmd)
continue; continue;
if (OutputSection *Sec = Cmd->Sec) { if (OutputSection *Sec = Cmd->Sec) {
@ -692,6 +696,7 @@ void LinkerScript::adjustSectionsBeforeSorting() {
continue; continue;
auto *OutSec = make<OutputSection>(Cmd->Name, Type, Flags); auto *OutSec = make<OutputSection>(Cmd->Name, Type, Flags);
OutSec->SectionIndex = I;
OutputSections->push_back(OutSec); OutputSections->push_back(OutSec);
Cmd->Sec = 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) { void LinkerScript::assignAddresses(std::vector<PhdrEntry> &Phdrs) {
// Assign addresses as instructed by linker script SECTIONS sub-commands. // Assign addresses as instructed by linker script SECTIONS sub-commands.
Dot = 0; Dot = 0;
@ -994,12 +1041,17 @@ static void writeInt(uint8_t *Buf, uint64_t Data, uint64_t Size) {
llvm_unreachable("unsupported Size argument"); llvm_unreachable("unsupported Size argument");
} }
void LinkerScript::writeDataBytes(StringRef Name, uint8_t *Buf) { void LinkerScript::writeDataBytes(OutputSection *Sec, uint8_t *Buf) {
int I = getSectionIndex(Name); auto I = std::find_if(Opt.Commands.begin(), Opt.Commands.end(),
if (I == INT_MAX) [=](BaseCommand *Base) {
if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base))
if (Cmd->Sec == Sec)
return true;
return false;
});
if (I == Opt.Commands.end())
return; return;
auto *Cmd = cast<OutputSectionCommand>(*I);
auto *Cmd = dyn_cast<OutputSectionCommand>(Opt.Commands[I]);
for (BaseCommand *Base : Cmd->Commands) for (BaseCommand *Base : Cmd->Commands)
if (auto *Data = dyn_cast<BytesDataCommand>(Base)) if (auto *Data = dyn_cast<BytesDataCommand>(Base))
writeInt(Buf + Data->Offset, Data->Expression().getValue(), Data->Size); writeInt(Buf + Data->Offset, Data->Expression().getValue(), Data->Size);
@ -1013,18 +1065,6 @@ bool LinkerScript::hasLMA(StringRef Name) {
return false; 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) { ExprValue LinkerScript::getSymbolValue(const Twine &Loc, StringRef S) {
if (S == ".") if (S == ".")
return {CurOutSec, Dot - CurOutSec->Addr}; return {CurOutSec, Dot - CurOutSec->Addr};

View File

@ -228,6 +228,7 @@ protected:
MemoryRegion *findMemoryRegion(OutputSectionCommand *Cmd); MemoryRegion *findMemoryRegion(OutputSectionCommand *Cmd);
void switchTo(OutputSection *Sec); void switchTo(OutputSection *Sec);
uint64_t advance(uint64_t Size, unsigned Align);
void output(InputSection *Sec); void output(InputSection *Sec);
void process(BaseCommand &Base); void process(BaseCommand &Base);
@ -252,7 +253,7 @@ public:
bool isDefined(StringRef S); bool isDefined(StringRef S);
std::vector<OutputSection *> *OutputSections; std::vector<OutputSection *> *OutputSections;
void fabricateDefaultCommands(bool AllocateHeader); void fabricateDefaultCommands();
void addOrphanSections(OutputSectionFactory &Factory); void addOrphanSections(OutputSectionFactory &Factory);
void removeEmptyCommands(); void removeEmptyCommands();
void adjustSectionsBeforeSorting(); void adjustSectionsBeforeSorting();
@ -269,9 +270,8 @@ public:
void processNonSectionCommands(); void processNonSectionCommands();
void synchronize(); void synchronize();
void assignAddresses(std::vector<PhdrEntry> &Phdrs); 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 addSymbol(SymbolAssignment *Cmd);
void processCommands(OutputSectionFactory &Factory); void processCommands(OutputSectionFactory &Factory);

View File

@ -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_d: Flag<["-"], "d">, Alias<define_common>;
def alias_define_common_dc: F<"dc">, 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_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_all_x: Flag<["-"], "x">, Alias<discard_all>;
def alias_discard_locals_X: Flag<["-"], "X">, Alias<discard_locals>; def alias_discard_locals_X: Flag<["-"], "X">, Alias<discard_locals>;
def alias_dynamic_list: J<"dynamic-list=">, Alias<dynamic_list>; def alias_dynamic_list: J<"dynamic-list=">, Alias<dynamic_list>;

View File

@ -68,7 +68,8 @@ void OutputSection::writeHeaderTo(typename ELFT::Shdr *Shdr) {
OutputSection::OutputSection(StringRef Name, uint32_t Type, uint64_t Flags) OutputSection::OutputSection(StringRef Name, uint32_t Type, uint64_t Flags)
: SectionBase(Output, Name, Flags, /*Entsize*/ 0, /*Alignment*/ 1, Type, : SectionBase(Output, Name, Flags, /*Entsize*/ 0, /*Alignment*/ 1, Type,
/*Info*/ 0, /*Info*/ 0,
/*Link*/ 0) {} /*Link*/ 0),
SectionIndex(INT_MAX) {}
static bool compareByFilePosition(InputSection *A, InputSection *B) { static bool compareByFilePosition(InputSection *A, InputSection *B) {
// Synthetic doesn't have link order dependecy, stable_sort will keep it last // 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; 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) { void OutputSection::addSection(InputSection *S) {
assert(S->Live); assert(S->Live);
Sections.push_back(S); Sections.push_back(S);
S->OutSec = this; S->OutSec = this;
this->updateAlignment(S->Alignment); 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 // If this section contains a table of fixed-size entries, sh_entsize
// holds the element size. Consequently, if this contains two or more // holds the element size. Consequently, if this contains two or more
// input sections, all of them must have the same sh_entsize. However, // 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. // and scan relocations to setup sections' offsets.
void OutputSection::assignOffsets() { void OutputSection::assignOffsets() {
uint64_t Off = 0; uint64_t Off = 0;
for (InputSection *S : Sections) { for (InputSection *S : Sections)
Off = alignTo(Off, S->Alignment); Off = updateOffset(Off, S);
S->OutSecOff = Off;
Off += S->getSize();
}
this->Size = Off; 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 // Linker scripts may have BYTE()-family commands with which you
// can write arbitrary bytes to the output. Process them if any. // 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) { static uint64_t getOutFlags(InputSectionBase *S) {

View File

@ -233,7 +233,7 @@ handleTlsRelocation(uint32_t Type, SymbolBody &Body, InputSectionBase &C,
} }
// Local-Dynamic relocs can be relaxed to Local-Exec. // 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( C.Relocations.push_back(
{R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Body}); {R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Body});
return 1; 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 // Initial-Exec relocs can be relaxed to Local-Exec if the symbol is locally
// defined. // 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( C.Relocations.push_back(
{R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Body}); {R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Body});
return 1; return 1;
@ -694,17 +695,6 @@ static void reportUndefined(SymbolBody &Sym, InputSectionBase &S,
warn(Msg); warn(Msg);
} else { } else {
error(Msg); 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;
}
} }
} }

View File

@ -540,13 +540,10 @@ void SymbolTable<ELFT>::addLazyObject(StringRef Name, LazyObjectFile &Obj) {
return; return;
// See comment for addLazyArchive above. // See comment for addLazyArchive above.
if (S->isWeak()) { if (S->isWeak())
replaceBody<LazyObject>(S, Name, Obj, S->body()->Type); replaceBody<LazyObject>(S, Name, Obj, S->body()->Type);
} else { else if (InputFile *F = Obj.fetch())
MemoryBufferRef MBRef = Obj.getBuffer(); addFile(F);
if (!MBRef.getBuffer().empty())
addFile(createObjectFile(MBRef));
}
} }
// Process undefined (-u) flags by loading lazy symbols named by those flags. // Process undefined (-u) flags by loading lazy symbols named by those flags.

View File

@ -327,12 +327,7 @@ InputFile *LazyArchive::fetch() {
return createObjectFile(MBInfo.first, file()->getName(), MBInfo.second); return createObjectFile(MBInfo.first, file()->getName(), MBInfo.second);
} }
InputFile *LazyObject::fetch() { InputFile *LazyObject::fetch() { return file()->fetch(); }
MemoryBufferRef MBRef = file()->getBuffer();
if (MBRef.getBuffer().empty())
return nullptr;
return createObjectFile(MBRef);
}
uint8_t Symbol::computeBinding() const { uint8_t Symbol::computeBinding() const {
if (Config->Relocatable) if (Config->Relocatable)

View File

@ -1038,6 +1038,15 @@ template <class ELFT> void DynamicSection<ELFT>::addEntries() {
if (!Config->SoName.empty()) if (!Config->SoName.empty())
add({DT_SONAME, In<ELFT>::DynStrTab->addString(Config->SoName)}); 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. // Set DT_FLAGS and DT_FLAGS_1.
uint32_t DtFlags = 0; uint32_t DtFlags = 0;
uint32_t DtFlags1 = 0; uint32_t DtFlags1 = 0;
@ -1055,21 +1064,14 @@ template <class ELFT> void DynamicSection<ELFT>::addEntries() {
DtFlags |= DF_ORIGIN; DtFlags |= DF_ORIGIN;
DtFlags1 |= DF_1_ORIGIN; DtFlags1 |= DF_1_ORIGIN;
} }
if (Config->HasStaticTlsModel)
DtFlags |= DF_STATIC_TLS;
if (DtFlags) if (DtFlags)
add({DT_FLAGS, DtFlags}); add({DT_FLAGS, DtFlags});
if (DtFlags1) if (DtFlags1)
add({DT_FLAGS_1, 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; this->Link = In<ELFT>::DynStrTab->OutSec->SectionIndex;
if (In<ELFT>::RelaDyn->OutSec->Size > 0) { if (In<ELFT>::RelaDyn->OutSec->Size > 0) {
bool IsRela = Config->IsRela; bool IsRela = Config->IsRela;

View File

@ -124,8 +124,6 @@ public:
int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override; int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
void writeGotPltHeader(uint8_t *Buf) const override; void writeGotPltHeader(uint8_t *Buf) const override;
uint32_t getDynRel(uint32_t Type) 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 writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
void writeIgotPlt(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; void writePltHeader(uint8_t *Buf) const override;
@ -147,8 +145,6 @@ public:
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S, RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
const uint8_t *Loc) const override; const uint8_t *Loc) const override;
bool isPicRel(uint32_t Type) 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 writeGotPltHeader(uint8_t *Buf) const override;
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override; void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
void writePltHeader(uint8_t *Buf) const override; void writePltHeader(uint8_t *Buf) const override;
@ -193,7 +189,6 @@ public:
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S, RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
const uint8_t *Loc) const override; const uint8_t *Loc) const override;
bool isPicRel(uint32_t Type) 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 writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
void writePltHeader(uint8_t *Buf) const override; void writePltHeader(uint8_t *Buf) const override;
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, 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; 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 { void TargetInfo::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const {
writeGotPlt(Buf, S); writeGotPlt(Buf, S);
} }
@ -360,6 +351,15 @@ X86TargetInfo::X86TargetInfo() {
RelExpr X86TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S, RelExpr X86TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S,
const uint8_t *Loc) const { 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) { switch (Type) {
case R_386_8: case R_386_8:
case R_386_16: case R_386_16:
@ -451,14 +451,6 @@ uint32_t X86TargetInfo::getDynRel(uint32_t Type) const {
return Type; 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 { void X86TargetInfo::writePltHeader(uint8_t *Buf) const {
if (Config->Pic) { if (Config->Pic) {
const uint8_t V[] = { const uint8_t V[] = {
@ -771,17 +763,6 @@ bool X86_64TargetInfo<ELFT>::isPicRel(uint32_t Type) const {
return Type != R_X86_64_PC32 && Type != R_X86_64_32; return Type != R_X86_64_PC32 && Type != R_X86_64_32;
} }
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> template <class ELFT>
void X86_64TargetInfo<ELFT>::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, void X86_64TargetInfo<ELFT>::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type,
uint64_t Val) const { 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 { bool AArch64TargetInfo::isPicRel(uint32_t Type) const {
return Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64; return Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64;
} }

View File

@ -23,8 +23,6 @@ class SymbolBody;
class TargetInfo { class TargetInfo {
public: 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 bool isPicRel(uint32_t Type) const { return true; }
virtual uint32_t getDynRel(uint32_t Type) const { return Type; } virtual uint32_t getDynRel(uint32_t Type) const { return Type; }
virtual void writeGotPltHeader(uint8_t *Buf) const {} virtual void writeGotPltHeader(uint8_t *Buf) const {}

View File

@ -62,7 +62,6 @@ private:
void assignFileOffsets(); void assignFileOffsets();
void assignFileOffsetsBinary(); void assignFileOffsetsBinary();
void setPhdrs(); void setPhdrs();
void fixHeaders();
void fixSectionAlignments(); void fixSectionAlignments();
void fixPredefinedSymbols(); void fixPredefinedSymbols();
void openFile(); void openFile();
@ -86,7 +85,6 @@ private:
uint64_t FileSize; uint64_t FileSize;
uint64_t SectionHeaderOff; uint64_t SectionHeaderOff;
bool AllocateHeader = true;
}; };
} // anonymous namespace } // anonymous namespace
@ -252,7 +250,7 @@ template <class ELFT> void Writer<ELFT>::run() {
} else { } else {
if (!Script->Opt.HasSections) { if (!Script->Opt.HasSections) {
fixSectionAlignments(); fixSectionAlignments();
Script->fabricateDefaultCommands(AllocateHeader); Script->fabricateDefaultCommands();
} }
Script->synchronize(); Script->synchronize();
Script->assignAddresses(Phdrs); Script->assignAddresses(Phdrs);
@ -747,15 +745,12 @@ static bool compareSectionsNonScript(const OutputSection *A,
// Output section ordering is determined by this function. // Output section ordering is determined by this function.
template <class ELFT> template <class ELFT>
static bool compareSections(const OutputSection *A, const OutputSection *B) { static bool compareSections(const OutputSection *A, const OutputSection *B) {
// For now, put sections mentioned in a linker script first. // For now, put sections mentioned in a linker script
int AIndex = Script->getSectionIndex(A->Name); // first. Sections not on linker script will have a SectionIndex of
int BIndex = Script->getSectionIndex(B->Name); // INT_MAX.
bool AInScript = AIndex != INT_MAX; int AIndex = A->SectionIndex;
bool BInScript = BIndex != INT_MAX; int BIndex = B->SectionIndex;
if (AInScript != BInScript) if (AIndex != BIndex)
return AInScript;
// If both are in the script, use that order.
if (AInScript)
return AIndex < BIndex; return AIndex < BIndex;
return compareSectionsNonScript<ELFT>(A, B); return compareSectionsNonScript<ELFT>(A, B);
@ -1021,9 +1016,8 @@ template <class ELFT> void Writer<ELFT>::sortSections() {
auto I = OutputSections.begin(); auto I = OutputSections.begin();
auto E = OutputSections.end(); auto E = OutputSections.end();
auto NonScriptI = auto NonScriptI =
std::find_if(OutputSections.begin(), E, [](OutputSection *S) { std::find_if(OutputSections.begin(), E,
return Script->getSectionIndex(S->Name) == INT_MAX; [](OutputSection *S) { return S->SectionIndex == INT_MAX; });
});
while (NonScriptI != E) { while (NonScriptI != E) {
auto BestPos = std::max_element( auto BestPos = std::max_element(
I, NonScriptI, [&](OutputSection *&A, OutputSection *&B) { I, NonScriptI, [&](OutputSection *&A, OutputSection *&B) {
@ -1176,7 +1170,7 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
if (!Config->Relocatable && !Config->OFormatBinary) { if (!Config->Relocatable && !Config->OFormatBinary) {
Phdrs = Script->hasPhdrsCommands() ? Script->createPhdrs() : createPhdrs(); Phdrs = Script->hasPhdrsCommands() ? Script->createPhdrs() : createPhdrs();
addPtArmExid(Phdrs); addPtArmExid(Phdrs);
fixHeaders(); Out::ProgramHeaders->Size = sizeof(Elf_Phdr) * Phdrs.size();
} }
// Dynamic section must be the last one in this list and dynamic // 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. // Add the first PT_LOAD segment for regular output sections.
uint64_t Flags = computeFlags(PF_R); uint64_t Flags = computeFlags(PF_R);
PhdrEntry *Load = AddHdr(PT_LOAD, Flags); 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) { for (OutputSection *Sec : OutputSections) {
if (!(Sec->Flags & SHF_ALLOC)) if (!(Sec->Flags & SHF_ALLOC))
break; 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 // Adjusts the file alignment for a given output section and returns
// its new file offset. The file offset must be the same with its // 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 // virtual address (modulo the page size) so that the loader can load

View File

@ -49,9 +49,6 @@ struct PhdrEntry {
llvm::StringRef getOutputSectionName(llvm::StringRef Name); llvm::StringRef getOutputSectionName(llvm::StringRef Name);
bool allocateHeaders(std::vector<PhdrEntry> &, llvm::ArrayRef<OutputSection *>,
uint64_t Min);
template <class ELFT> uint32_t getMipsEFlags(); template <class ELFT> uint32_t getMipsEFlags();
uint8_t getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag, uint8_t getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag,

View File

@ -10,16 +10,12 @@
#ifndef LLD_CORE_PARALLEL_H #ifndef LLD_CORE_PARALLEL_H
#define LLD_CORE_PARALLEL_H #define LLD_CORE_PARALLEL_H
#include "lld/Core/Instrumentation.h"
#include "lld/Core/LLVM.h" #include "lld/Core/LLVM.h"
#include "lld/Core/TaskGroup.h"
#include "llvm/Support/MathExtras.h" #include "llvm/Support/MathExtras.h"
#include "llvm/Support/thread.h" #include "llvm/Config/llvm-config.h"
#include <algorithm> #include <algorithm>
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <stack>
#if defined(_MSC_VER) && LLVM_ENABLE_THREADS #if defined(_MSC_VER) && LLVM_ENABLE_THREADS
#include <concrt.h> #include <concrt.h>
@ -27,249 +23,84 @@
#endif #endif
namespace lld { 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: #if !LLVM_ENABLE_THREADS
explicit Latch(uint32_t count = 0) : _count(count) {} template <class RandomAccessIterator, class Comparator>
~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>
void parallel_sort( void parallel_sort(
RandomAccessIterator start, RandomAccessIterator end, RandomAccessIterator Start, RandomAccessIterator End,
const Comp &comp = std::less< const Comparator &Comp = std::less<
typename std::iterator_traits<RandomAccessIterator>::value_type>()) { typename std::iterator_traits<RandomAccessIterator>::value_type>()) {
std::sort(start, end, comp); std::sort(Start, End, Comp);
} }
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
// Use ppl parallel_sort on Windows. // Use ppl parallel_sort on Windows.
template <class RandomAccessIterator, class Comp> template <class RandomAccessIterator, class Comparator>
void parallel_sort( void parallel_sort(
RandomAccessIterator start, RandomAccessIterator end, RandomAccessIterator Start, RandomAccessIterator End,
const Comp &comp = std::less< const Comparator &Comp = std::less<
typename std::iterator_traits<RandomAccessIterator>::value_type>()) { typename std::iterator_traits<RandomAccessIterator>::value_type>()) {
concurrency::parallel_sort(start, end, comp); concurrency::parallel_sort(Start, End, Comp);
} }
#else #else
namespace detail { namespace detail {
const ptrdiff_t minParallelSize = 1024; const ptrdiff_t MinParallelSize = 1024;
/// \brief Inclusive median. /// \brief Inclusive median.
template <class RandomAccessIterator, class Comp> template <class RandomAccessIterator, class Comparator>
RandomAccessIterator medianOf3(RandomAccessIterator start, RandomAccessIterator medianOf3(RandomAccessIterator Start,
RandomAccessIterator end, const Comp &comp) { RandomAccessIterator End,
RandomAccessIterator mid = start + (std::distance(start, end) / 2); const Comparator &Comp) {
return comp(*start, *(end - 1)) RandomAccessIterator Mid = Start + (std::distance(Start, End) / 2);
? (comp(*mid, *(end - 1)) ? (comp(*start, *mid) ? mid : start) return Comp(*Start, *(End - 1))
: end - 1) ? (Comp(*Mid, *(End - 1)) ? (Comp(*Start, *Mid) ? Mid : Start)
: (comp(*mid, *start) ? (comp(*(end - 1), *mid) ? mid : end - 1) : End - 1)
: start); : (Comp(*Mid, *Start) ? (Comp(*(End - 1), *Mid) ? Mid : End - 1)
: Start);
} }
template <class RandomAccessIterator, class Comp> template <class RandomAccessIterator, class Comparator>
void parallel_quick_sort(RandomAccessIterator start, RandomAccessIterator end, void parallel_quick_sort(RandomAccessIterator Start, RandomAccessIterator End,
const Comp &comp, TaskGroup &tg, size_t depth) { const Comparator &Comp, TaskGroup &TG, size_t Depth) {
// Do a sequential sort for small inputs. // Do a sequential sort for small inputs.
if (std::distance(start, end) < detail::minParallelSize || depth == 0) { if (std::distance(Start, End) < detail::MinParallelSize || Depth == 0) {
std::sort(start, end, comp); std::sort(Start, End, Comp);
return; return;
} }
// Partition. // Partition.
auto pivot = medianOf3(start, end, comp); auto Pivot = medianOf3(Start, End, Comp);
// Move pivot to end. // Move Pivot to End.
std::swap(*(end - 1), *pivot); std::swap(*(End - 1), *Pivot);
pivot = std::partition(start, end - 1, [&comp, end](decltype(*start) v) { Pivot = std::partition(Start, End - 1, [&Comp, End](decltype(*Start) V) {
return comp(v, *(end - 1)); return Comp(V, *(End - 1));
}); });
// Move pivot to middle of partition. // Move Pivot to middle of partition.
std::swap(*pivot, *(end - 1)); std::swap(*Pivot, *(End - 1));
// Recurse. // Recurse.
tg.spawn([=, &comp, &tg] { TG.spawn([=, &Comp, &TG] {
parallel_quick_sort(start, pivot, comp, tg, depth - 1); 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( void parallel_sort(
RandomAccessIterator start, RandomAccessIterator end, RandomAccessIterator Start, RandomAccessIterator End,
const Comp &comp = std::less< const Comparator &Comp = std::less<
typename std::iterator_traits<RandomAccessIterator>::value_type>()) { typename std::iterator_traits<RandomAccessIterator>::value_type>()) {
TaskGroup tg; TaskGroup TG;
detail::parallel_quick_sort(start, end, comp, tg, detail::parallel_quick_sort(Start, End, Comp, TG,
llvm::Log2_64(std::distance(start, end)) + 1); llvm::Log2_64(std::distance(Start, End)) + 1);
} }
#endif #endif
template <class T> void parallel_sort(T *start, T *end) { template <class T> void parallel_sort(T *Start, T *End) {
parallel_sort(start, end, std::less<T>()); 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> template <class IterTy, class FuncTy>
void parallel_for_each(IterTy Begin, IterTy End, FuncTy Fn) { void parallel_for_each(IterTy Begin, IterTy End, FuncTy Fn) {
std::for_each(Begin, End, Fn); std::for_each(Begin, End, Fn);
@ -302,12 +133,12 @@ void parallel_for_each(IterTy Begin, IterTy End, FuncTy Fn) {
if (TaskSize == 0) if (TaskSize == 0)
TaskSize = 1; TaskSize = 1;
TaskGroup Tg; TaskGroup TG;
while (TaskSize <= std::distance(Begin, End)) { 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; 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> template <class IndexTy, class FuncTy>
@ -316,20 +147,20 @@ void parallel_for(IndexTy Begin, IndexTy End, FuncTy Fn) {
if (TaskSize == 0) if (TaskSize == 0)
TaskSize = 1; TaskSize = 1;
TaskGroup Tg; TaskGroup TG;
IndexTy I = Begin; IndexTy I = Begin;
for (; I + TaskSize < End; I += TaskSize) { for (; I + TaskSize < End; I += TaskSize) {
Tg.spawn([=, &Fn] { TG.spawn([=, &Fn] {
for (IndexTy J = I, E = I + TaskSize; J != E; ++J) for (IndexTy J = I, E = I + TaskSize; J != E; ++J)
Fn(J); Fn(J);
}); });
} }
Tg.spawn([=, &Fn] { TG.spawn([=, &Fn] {
for (IndexTy J = I; J < End; ++J) for (IndexTy J = I; J < End; ++J)
Fn(J); Fn(J);
}); });
} }
#endif #endif
} // end namespace lld } // End namespace lld
#endif // LLD_CORE_PARALLEL_H #endif // LLD_CORE_PARALLEL_H

View File

@ -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

View File

@ -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

View File

@ -12,6 +12,7 @@ add_lld_library(lldCore
Resolver.cpp Resolver.cpp
SymbolTable.cpp SymbolTable.cpp
TargetOptionsCommandFlags.cpp TargetOptionsCommandFlags.cpp
TaskGroup.cpp
Writer.cpp Writer.cpp
ADDITIONAL_HEADER_DIRS ADDITIONAL_HEADER_DIRS
@ -20,6 +21,9 @@ add_lld_library(lldCore
LINK_COMPONENTS LINK_COMPONENTS
MC MC
Support Support
LINK_LIBS
${LLVM_PTHREAD_LIB}
DEPENDS DEPENDS
${tablegen_deps} ${tablegen_deps}

141
lib/Core/TaskGroup.cpp Normal file
View File

@ -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();
});
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -4,6 +4,11 @@
# RUN: llvm-readobj -t -s %t | FileCheck %s # RUN: llvm-readobj -t -s %t | FileCheck %s
# RUN: llvm-objdump -d -print-imm-hex %t | FileCheck %s --check-prefix=USE # 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 ## In compare with GNU linkers, symbol defined with --defsym does
## not get aliased name in symbol table: ## not get aliased name in symbol table:
# CHECK: Symbol { # CHECK: Symbol {

View File

@ -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

View File

@ -13,8 +13,8 @@
// GOTRELSHARED-NEXT: SHF_ALLOC // GOTRELSHARED-NEXT: SHF_ALLOC
// GOTRELSHARED-NEXT: SHF_WRITE // GOTRELSHARED-NEXT: SHF_WRITE
// GOTRELSHARED-NEXT: ] // GOTRELSHARED-NEXT: ]
// GOTRELSHARED-NEXT: Address: 0x1058 // GOTRELSHARED-NEXT: Address: 0x1060
// GOTRELSHARED-NEXT: Offset: 0x1058 // GOTRELSHARED-NEXT: Offset: 0x1060
// GOTRELSHARED-NEXT: Size: 16 // GOTRELSHARED-NEXT: Size: 16
// GOTRELSHARED-NEXT: Link: 0 // GOTRELSHARED-NEXT: Link: 0
// GOTRELSHARED-NEXT: Info: 0 // GOTRELSHARED-NEXT: Info: 0
@ -31,36 +31,36 @@
// GOTRELSHARED-NEXT: 0x202D R_386_RELATIVE - 0x0 // GOTRELSHARED-NEXT: 0x202D R_386_RELATIVE - 0x0
// GOTRELSHARED-NEXT: 0x2036 R_386_RELATIVE - 0x0 // GOTRELSHARED-NEXT: 0x2036 R_386_RELATIVE - 0x0
// GOTRELSHARED-NEXT: 0x203F R_386_RELATIVE - 0x0 // GOTRELSHARED-NEXT: 0x203F R_386_RELATIVE - 0x0
// GOTRELSHARED-NEXT: 0x1058 R_386_TLS_TPOFF tlslocal0 0x0 // GOTRELSHARED-NEXT: 0x1060 R_386_TLS_TPOFF tlslocal0 0x0
// GOTRELSHARED-NEXT: 0x105C R_386_TLS_TPOFF tlslocal1 0x0 // GOTRELSHARED-NEXT: 0x1064 R_386_TLS_TPOFF tlslocal1 0x0
// GOTRELSHARED-NEXT: 0x1060 R_386_TLS_TPOFF tlsshared0 0x0 // GOTRELSHARED-NEXT: 0x1068 R_386_TLS_TPOFF tlsshared0 0x0
// GOTRELSHARED-NEXT: 0x1064 R_386_TLS_TPOFF tlsshared1 0x0 // GOTRELSHARED-NEXT: 0x106C R_386_TLS_TPOFF tlsshared1 0x0
// GOTRELSHARED-NEXT: } // GOTRELSHARED-NEXT: }
// GOTRELSHARED-NEXT: ] // GOTRELSHARED-NEXT: ]
// GOTRELSHARED: 0x6FFFFFFA RELCOUNT 8 // GOTRELSHARED: 0x6FFFFFFA RELCOUNT 8
// DISASMSHARED: Disassembly of section test: // DISASMSHARED: Disassembly of section test:
// DISASMSHARED-NEXT: _start: // DISASMSHARED-NEXT: _start:
// (.got)[0] = 0x2050 = 8272 // (.got)[0] = 0x1060 = 4192
// (.got)[1] = 0x2054 = 8276 // (.got)[1] = 0x1064 = 4196
// (.got)[2] = 0x2058 = 8280 // (.got)[2] = 0x1068 = 4200
// (.got)[3] = 0x205C = 8284 // (.got)[3] = 0x106C = 4204
// DISASMSHARED-NEXT: 2000: 8b 0d 58 10 00 00 movl 4184, %ecx // DISASMSHARED-NEXT: 2000: {{.*}} movl 4192, %ecx
// DISASMSHARED-NEXT: 2006: 65 8b 01 movl %gs:(%ecx), %eax // DISASMSHARED-NEXT: 2006: {{.*}} movl %gs:(%ecx), %eax
// DISASMSHARED-NEXT: 2009: a1 58 10 00 00 movl 4184, %eax // DISASMSHARED-NEXT: 2009: {{.*}} movl 4192, %eax
// DISASMSHARED-NEXT: 200e: 65 8b 00 movl %gs:(%eax), %eax // DISASMSHARED-NEXT: 200e: {{.*}} movl %gs:(%eax), %eax
// DISASMSHARED-NEXT: 2011: 03 0d 58 10 00 00 addl 4184, %ecx // DISASMSHARED-NEXT: 2011: {{.*}} addl 4192, %ecx
// DISASMSHARED-NEXT: 2017: 65 8b 01 movl %gs:(%ecx), %eax // DISASMSHARED-NEXT: 2017: {{.*}} movl %gs:(%ecx), %eax
// DISASMSHARED-NEXT: 201a: 8b 0d 5c 10 00 00 movl 4188, %ecx // DISASMSHARED-NEXT: 201a: {{.*}} movl 4196, %ecx
// DISASMSHARED-NEXT: 2020: 65 8b 01 movl %gs:(%ecx), %eax // DISASMSHARED-NEXT: 2020: {{.*}} movl %gs:(%ecx), %eax
// DISASMSHARED-NEXT: 2023: a1 5c 10 00 00 movl 4188, %eax // DISASMSHARED-NEXT: 2023: {{.*}} movl 4196, %eax
// DISASMSHARED-NEXT: 2028: 65 8b 00 movl %gs:(%eax), %eax // DISASMSHARED-NEXT: 2028: {{.*}} movl %gs:(%eax), %eax
// DISASMSHARED-NEXT: 202b: 03 0d 5c 10 00 00 addl 4188, %ecx // DISASMSHARED-NEXT: 202b: {{.*}} addl 4196, %ecx
// DISASMSHARED-NEXT: 2031: 65 8b 01 movl %gs:(%ecx), %eax // DISASMSHARED-NEXT: 2031: {{.*}} movl %gs:(%ecx), %eax
// DISASMSHARED-NEXT: 2034: 8b 0d 60 10 00 00 movl 4192, %ecx // DISASMSHARED-NEXT: 2034: {{.*}} movl 4200, %ecx
// DISASMSHARED-NEXT: 203a: 65 8b 01 movl %gs:(%ecx), %eax // DISASMSHARED-NEXT: 203a: {{.*}} movl %gs:(%ecx), %eax
// DISASMSHARED-NEXT: 203d: 03 0d 64 10 00 00 addl 4196, %ecx // DISASMSHARED-NEXT: 203d: {{.*}} addl 4204, %ecx
// DISASMSHARED-NEXT: 2043: 65 8b 01 movl %gs:(%ecx), %eax // DISASMSHARED-NEXT: 2043: {{.*}} movl %gs:(%ecx), %eax
.type tlslocal0,@object .type tlslocal0,@object
.section .tbss,"awT",@nobits .section .tbss,"awT",@nobits

View File

@ -0,0 +1,3 @@
.section .debug_str
.asciz "CCC"
.asciz "DDD"

View File

@ -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"

View File

@ -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
}

View File

@ -4,29 +4,15 @@
; encountered an empty archive index and undefined references (to prevent ; encountered an empty archive index and undefined references (to prevent
; noisy false alarms). ; noisy false alarms).
; RUN: rm -fr %T/archive-no-index ; RUN: llvm-as -o %t1.o %s
; RUN: mkdir %T/archive-no-index ; RUN: llvm-as -o %t2.o %S/Inputs/archive.ll
; 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-ar crs %T/archive-no-index/libfs.a %T/archive-no-index/f.o ; RUN: rm -f %t1.a %t2.a
; RUN: ld.lld -emain -m elf_x86_64 %t.o -o %t %T/archive-no-index/libf.a \ ; RUN: llvm-ar crS %t1.a %t2.o
; RUN: %T/archive-no-index/libfs.a ; RUN: llvm-ar crs %t2.a %t2.o
; RUN: llvm-as %S/Inputs/archive-3.ll -o %T/archive-no-index/foo.o ; RUN: ld.lld -o %t -emain -m elf_x86_64 %t1.o %t1.a
; RUN: llvm-ar crs %T/archive-no-index/libfoo.a %T/archive-no-index/foo.o ; RUN: ld.lld -o %t -emain -m elf_x86_64 %t1.o %t2.a
; 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
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu" target triple = "x86_64-unknown-linux-gnu"

View File

@ -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
}

View File

@ -1,14 +1,21 @@
; RUN: opt -module-summary %s -o %t.o ; RUN: opt -module-summary %s -o %t.o
; RUN: opt -module-summary %p/Inputs/thin1.ll -o %t.coll.o ; RUN: mkdir -p %t1 %t2
; RUN: llvm-ar rcs %t1.a %t.coll.o ; RUN: opt -module-summary %p/Inputs/thin1.ll -o %t1/t.coll.o
; RUN: opt -module-summary %p/Inputs/thin2.ll -o %t.coll.o ; RUN: opt -module-summary %p/Inputs/thin2.ll -o %t2/t.coll.o
; RUN: llvm-ar rcsc %t2.a %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 ; RUN: llvm-nm %t | FileCheck %s
; Check we handle this case correctly even in presence of --whole-archive. ; 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 ; RUN: llvm-nm %t | FileCheck %s
; CHECK: T _start ; CHECK: T _start

View File

@ -56,8 +56,8 @@ addl tls1@gotntpoff(%ebx),%eax
// CHECK-NEXT: SHF_ALLOC // CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_WRITE // CHECK-NEXT: SHF_WRITE
// CHECK-NEXT: ] // CHECK-NEXT: ]
// CHECK-NEXT: Address: 0x3068 // CHECK-NEXT: Address: 0x3070
// CHECK-NEXT: Offset: 0x3068 // CHECK-NEXT: Offset: 0x3070
// CHECK-NEXT: Size: 32 // CHECK-NEXT: Size: 32
// CHECK-NEXT: Link: 0 // CHECK-NEXT: Link: 0
// CHECK-NEXT: Info: 0 // CHECK-NEXT: Info: 0
@ -66,13 +66,13 @@ addl tls1@gotntpoff(%ebx),%eax
// CHECK: Relocations [ // CHECK: Relocations [
// CHECK: Section ({{.+}}) .rel.dyn { // CHECK: Section ({{.+}}) .rel.dyn {
// CHECK-NEXT: 0x3078 R_386_TLS_DTPMOD32 - 0x0 // CHECK-NEXT: 0x3080 R_386_TLS_DTPMOD32 - 0x0
// CHECK-NEXT: 0x3068 R_386_TLS_DTPMOD32 tls0 0x0 // CHECK-NEXT: 0x3070 R_386_TLS_DTPMOD32 tls0 0x0
// CHECK-NEXT: 0x306C R_386_TLS_DTPOFF32 tls0 0x0 // CHECK-NEXT: 0x3074 R_386_TLS_DTPOFF32 tls0 0x0
// CHECK-NEXT: 0x3080 R_386_TLS_TPOFF tls0 0x0 // CHECK-NEXT: 0x3088 R_386_TLS_TPOFF tls0 0x0
// CHECK-NEXT: 0x3070 R_386_TLS_DTPMOD32 tls1 0x0 // CHECK-NEXT: 0x3078 R_386_TLS_DTPMOD32 tls1 0x0
// CHECK-NEXT: 0x3074 R_386_TLS_DTPOFF32 tls1 0x0 // CHECK-NEXT: 0x307C R_386_TLS_DTPOFF32 tls1 0x0
// CHECK-NEXT: 0x3084 R_386_TLS_TPOFF tls1 0x0 // CHECK-NEXT: 0x308C R_386_TLS_TPOFF tls1 0x0
// CHECK-NEXT: } // CHECK-NEXT: }
// DIS: Disassembly of section .text: // DIS: Disassembly of section .text:
@ -80,20 +80,20 @@ addl tls1@gotntpoff(%ebx),%eax
// General dynamic model: // General dynamic model:
// -32 and -24 are first and second GOT entries offsets. // -32 and -24 are first and second GOT entries offsets.
// Each one is a pair of records. // Each one is a pair of records.
// DIS-NEXT: 1000: 8d 04 1d e0 ff ff ff leal -32(,%ebx), %eax // DIS-NEXT: 1000: {{.*}} leal -32(,%ebx), %eax
// DIS-NEXT: 1007: e8 64 00 00 00 calll 100 // DIS-NEXT: 1007: {{.*}} calll 100
// DIS-NEXT: 100c: 8d 04 1d e8 ff ff ff leal -24(,%ebx), %eax // DIS-NEXT: 100c: {{.*}} leal -24(,%ebx), %eax
// DIS-NEXT: 1013: e8 58 00 00 00 calll 88 // DIS-NEXT: 1013: {{.*}} calll 88
// Local dynamic model: // Local dynamic model:
// -16 is a local module tls index offset. // -16 is a local module tls index offset.
// DIS-NEXT: 1018: 8d 83 f0 ff ff ff leal -16(%ebx), %eax // DIS-NEXT: 1018: {{.*}} leal -16(%ebx), %eax
// DIS-NEXT: 101e: e8 4d 00 00 00 calll 77 // DIS-NEXT: 101e: {{.*}} calll 77
// DIS-NEXT: 1023: 8d 90 08 00 00 00 leal 8(%eax), %edx // DIS-NEXT: 1023: {{.*}} leal 8(%eax), %edx
// DIS-NEXT: 1029: 8d 83 f0 ff ff ff leal -16(%ebx), %eax // DIS-NEXT: 1029: {{.*}} leal -16(%ebx), %eax
// DIS-NEXT: 102f: e8 3c 00 00 00 calll 60 // DIS-NEXT: 102f: {{.*}} calll 60
// DIS-NEXT: 1034: 8d 90 0c 00 00 00 leal 12(%eax), %edx // DIS-NEXT: 1034: {{.*}} leal 12(%eax), %edx
// Initial exec model: // Initial exec model:
// DIS-NEXT: 103a: 65 a1 00 00 00 00 movl %gs:0, %eax // DIS-NEXT: 103a: {{.*}} movl %gs:0, %eax
// DIS-NEXT: 1040: 03 83 f8 ff ff ff addl -8(%ebx), %eax // DIS-NEXT: 1040: {{.*}} addl -8(%ebx), %eax
// DIS-NEXT: 1046: 65 a1 00 00 00 00 movl %gs:0, %eax // DIS-NEXT: 1046: {{.*}} movl %gs:0, %eax
// DIS-NEXT: 104c: 03 83 fc ff ff ff addl -4(%ebx), %eax // DIS-NEXT: 104c: {{.*}} addl -4(%ebx), %eax

View File

@ -10,7 +10,7 @@
// RUN: .tbss : { *(.tbss) } \ // RUN: .tbss : { *(.tbss) } \
// RUN: .data.rel.ro : { *(.data.rel.ro) } \ // RUN: .data.rel.ro : { *(.data.rel.ro) } \
// RUN: }" > %t.script // 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: echo SCRIPT
// RUN: llvm-readobj -s %tout2 | FileCheck %s // RUN: llvm-readobj -s %tout2 | FileCheck %s
.global _start .global _start
@ -61,6 +61,6 @@ _start:
// CHECK-NEXT: SHF_ALLOC // CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_WRITE // CHECK-NEXT: SHF_WRITE
// CHECK-NEXT: ] // CHECK-NEXT: ]
// CHECK-NEXT: Address: 0x202010 // CHECK-NEXT: Address: 0x202004
// CHECK-NEXT: Offset: 0x2010 // CHECK-NEXT: Offset: 0x2004
// CHECK-NEXT: Size: 4 // CHECK-NEXT: Size: 4

View File

@ -13,8 +13,8 @@
// GOTREL-NEXT: SHF_ALLOC // GOTREL-NEXT: SHF_ALLOC
// GOTREL-NEXT: SHF_WRITE // GOTREL-NEXT: SHF_WRITE
// GOTREL-NEXT: ] // GOTREL-NEXT: ]
// GOTREL-NEXT: Address: 0x12058 // GOTREL-NEXT: Address: 0x12060
// GOTREL-NEXT: Offset: 0x2058 // GOTREL-NEXT: Offset: 0x2060
// GOTREL-NEXT: Size: 8 // GOTREL-NEXT: Size: 8
// GOTREL-NEXT: Link: 0 // GOTREL-NEXT: Link: 0
// GOTREL-NEXT: Info: 0 // GOTREL-NEXT: Info: 0
@ -23,8 +23,8 @@
// GOTREL-NEXT: } // GOTREL-NEXT: }
// GOTREL: Relocations [ // GOTREL: Relocations [
// GOTREL-NEXT: Section ({{.*}}) .rel.dyn { // GOTREL-NEXT: Section ({{.*}}) .rel.dyn {
// GOTREL-NEXT: 0x12058 R_386_TLS_TPOFF tlsshared0 0x0 // GOTREL-NEXT: 0x12060 R_386_TLS_TPOFF tlsshared0 0x0
// GOTREL-NEXT: 0x1205C R_386_TLS_TPOFF tlsshared1 0x0 // GOTREL-NEXT: 0x12064 R_386_TLS_TPOFF tlsshared1 0x0
// GOTREL-NEXT: } // GOTREL-NEXT: }
// GOTREL-NEXT: ] // GOTREL-NEXT: ]
@ -32,24 +32,24 @@
// DISASM-NEXT: _start: // DISASM-NEXT: _start:
// 4294967288 = 0xFFFFFFF8 // 4294967288 = 0xFFFFFFF8
// 4294967292 = 0xFFFFFFFC // 4294967292 = 0xFFFFFFFC
// 73808 = (.got)[0] = 0x12058 // 73824 = (.got)[0] = 0x12060
// 73812 = (.got)[1] = 0x1205C // 73828 = (.got)[1] = 0x12064
// DISASM-NEXT: 11000: c7 c1 f8 ff ff ff movl $4294967288, %ecx // DISASM-NEXT: 11000: {{.*}} movl $4294967288, %ecx
// DISASM-NEXT: 11006: 65 8b 01 movl %gs:(%ecx), %eax // DISASM-NEXT: 11006: {{.*}} movl %gs:(%ecx), %eax
// DISASM-NEXT: 11009: b8 f8 ff ff ff movl $4294967288, %eax // DISASM-NEXT: 11009: {{.*}} movl $4294967288, %eax
// DISASM-NEXT: 1100e: 65 8b 00 movl %gs:(%eax), %eax // DISASM-NEXT: 1100e: {{.*}} movl %gs:(%eax), %eax
// DISASM-NEXT: 11011: 81 c1 f8 ff ff ff addl $4294967288, %ecx // DISASM-NEXT: 11011: {{.*}} addl $4294967288, %ecx
// DISASM-NEXT: 11017: 65 8b 01 movl %gs:(%ecx), %eax // DISASM-NEXT: 11017: {{.*}} movl %gs:(%ecx), %eax
// DISASM-NEXT: 1101a: c7 c1 fc ff ff ff movl $4294967292, %ecx // DISASM-NEXT: 1101a: {{.*}} movl $4294967292, %ecx
// DISASM-NEXT: 11020: 65 8b 01 movl %gs:(%ecx), %eax // DISASM-NEXT: 11020: {{.*}} movl %gs:(%ecx), %eax
// DISASM-NEXT: 11023: b8 fc ff ff ff movl $4294967292, %eax // DISASM-NEXT: 11023: {{.*}} movl $4294967292, %eax
// DISASM-NEXT: 11028: 65 8b 00 movl %gs:(%eax), %eax // DISASM-NEXT: 11028: {{.*}} movl %gs:(%eax), %eax
// DISASM-NEXT: 1102b: 81 c1 fc ff ff ff addl $4294967292, %ecx // DISASM-NEXT: 1102b: {{.*}} addl $4294967292, %ecx
// DISASM-NEXT: 11031: 65 8b 01 movl %gs:(%ecx), %eax // DISASM-NEXT: 11031: {{.*}} movl %gs:(%ecx), %eax
// DISASM-NEXT: 11034: 8b 0d 58 20 01 00 movl 73816, %ecx // DISASM-NEXT: 11034: {{.*}} movl 73824, %ecx
// DISASM-NEXT: 1103a: 65 8b 01 movl %gs:(%ecx), %eax // DISASM-NEXT: 1103a: {{.*}} movl %gs:(%ecx), %eax
// DISASM-NEXT: 1103d: 03 0d 5c 20 01 00 addl 73820, %ecx // DISASM-NEXT: 1103d: {{.*}} addl 73828, %ecx
// DISASM-NEXT: 11043: 65 8b 01 movl %gs:(%ecx), %eax // DISASM-NEXT: 11043: {{.*}} movl %gs:(%ecx), %eax
.type tlslocal0,@object .type tlslocal0,@object
.section .tbss,"awT",@nobits .section .tbss,"awT",@nobits

View File

@ -3,5 +3,5 @@ add_lld_unittest(CoreTests
) )
target_link_libraries(CoreTests target_link_libraries(CoreTests
${LLVM_PTHREAD_LIB} lldCore ${LLVM_PTHREAD_LIB}
) )