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(COFF)
add_subdirectory(ELF)

View File

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

View File

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

View File

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

View File

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

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.
// 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));
}

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() {
File = check(Archive::create(MB),
MB.getBufferIdentifier() + ": failed to parse archive");
// Read the symbol table to construct Lazy objects.
for (const Archive::Symbol &Sym : File->symbols()) {
for (const Archive::Symbol &Sym : File->symbols())
Symtab<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);

View File

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

View File

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

View File

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

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_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>;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -124,8 +124,6 @@ public:
int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
void writeGotPltHeader(uint8_t *Buf) const override;
uint32_t getDynRel(uint32_t Type) const override;
bool isTlsLocalDynamicRel(uint32_t Type) const override;
bool isTlsInitialExecRel(uint32_t Type) const override;
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
void writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const override;
void writePltHeader(uint8_t *Buf) const override;
@ -147,8 +145,6 @@ public:
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
const uint8_t *Loc) const override;
bool isPicRel(uint32_t Type) const override;
bool isTlsLocalDynamicRel(uint32_t Type) const override;
bool isTlsInitialExecRel(uint32_t Type) const override;
void writeGotPltHeader(uint8_t *Buf) const override;
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
void writePltHeader(uint8_t *Buf) const override;
@ -193,7 +189,6 @@ public:
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
const uint8_t *Loc) const override;
bool isPicRel(uint32_t Type) const override;
bool isTlsInitialExecRel(uint32_t Type) const override;
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
void writePltHeader(uint8_t *Buf) const override;
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
@ -303,10 +298,6 @@ bool TargetInfo::needsThunk(RelExpr Expr, uint32_t RelocType,
return false;
}
bool TargetInfo::isTlsInitialExecRel(uint32_t Type) const { return false; }
bool TargetInfo::isTlsLocalDynamicRel(uint32_t Type) const { return false; }
void TargetInfo::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const {
writeGotPlt(Buf, S);
}
@ -360,6 +351,15 @@ X86TargetInfo::X86TargetInfo() {
RelExpr X86TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S,
const uint8_t *Loc) const {
// There are 4 different TLS variable models with varying degrees of
// flexibility and performance. LocalExec and InitialExec models are fast but
// less-flexible models. They cannot be used for dlopen(). If they are in use,
// we set DF_STATIC_TLS in the ELF header so that the runtime can reject such
// DSOs.
if (Type == R_386_TLS_LE || Type == R_386_TLS_LE_32 || Type == R_386_TLS_IE ||
Type == R_386_TLS_GOTIE)
Config->HasStaticTlsModel = true;
switch (Type) {
case R_386_8:
case R_386_16:
@ -451,14 +451,6 @@ uint32_t X86TargetInfo::getDynRel(uint32_t Type) const {
return Type;
}
bool X86TargetInfo::isTlsLocalDynamicRel(uint32_t Type) const {
return Type == R_386_TLS_LDO_32 || Type == R_386_TLS_LDM;
}
bool X86TargetInfo::isTlsInitialExecRel(uint32_t Type) const {
return Type == R_386_TLS_IE || Type == R_386_TLS_GOTIE;
}
void X86TargetInfo::writePltHeader(uint8_t *Buf) const {
if (Config->Pic) {
const uint8_t V[] = {
@ -771,17 +763,6 @@ bool X86_64TargetInfo<ELFT>::isPicRel(uint32_t Type) const {
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>
void X86_64TargetInfo<ELFT>::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type,
uint64_t Val) const {
@ -1383,11 +1364,6 @@ bool AArch64TargetInfo::usesOnlyLowPageBits(uint32_t Type) const {
}
}
bool AArch64TargetInfo::isTlsInitialExecRel(uint32_t Type) const {
return Type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 ||
Type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC;
}
bool AArch64TargetInfo::isPicRel(uint32_t Type) const {
return Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64;
}

View File

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

View File

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

View File

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

View File

@ -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 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 Comp>
void parallel_quick_sort(RandomAccessIterator start, RandomAccessIterator end,
const Comp &comp, TaskGroup &tg, size_t depth) {
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

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

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

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

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

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

View File

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

View File

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

View File

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

View File

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