Vendor import of lld trunk r302418:
https://llvm.org/svn/llvm-project/lld/trunk@302418
This commit is contained in:
parent
d803cda429
commit
fbe69f787a
@ -221,3 +221,4 @@ endif()
|
||||
add_subdirectory(docs)
|
||||
add_subdirectory(COFF)
|
||||
add_subdirectory(ELF)
|
||||
|
||||
|
@ -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;
|
||||
|
42
COFF/ICF.cpp
42
COFF/ICF.cpp
@ -49,10 +49,10 @@ private:
|
||||
|
||||
size_t findBoundary(size_t Begin, size_t End);
|
||||
|
||||
void forEachColorRange(size_t Begin, size_t End,
|
||||
void forEachClassRange(size_t Begin, size_t End,
|
||||
std::function<void(size_t, size_t)> Fn);
|
||||
|
||||
void forEachColor(std::function<void(size_t, size_t)> Fn);
|
||||
void forEachClass(std::function<void(size_t, size_t)> Fn);
|
||||
|
||||
std::vector<SectionChunk *> Chunks;
|
||||
int Cnt = 0;
|
||||
@ -85,7 +85,7 @@ bool ICF::isEligible(SectionChunk *C) {
|
||||
return C->isCOMDAT() && C->isLive() && Global && Executable && !Writable;
|
||||
}
|
||||
|
||||
// Split a range into smaller ranges by recoloring sections
|
||||
// Split an equivalence class into smaller classes.
|
||||
void ICF::segregate(size_t Begin, size_t End, bool Constant) {
|
||||
while (Begin < End) {
|
||||
// Divide [Begin, End) into two. Let Mid be the start index of the
|
||||
@ -101,7 +101,7 @@ void ICF::segregate(size_t Begin, size_t End, bool Constant) {
|
||||
// Split [Begin, End) into [Begin, Mid) and [Mid, End).
|
||||
uint32_t Id = NextId++;
|
||||
for (size_t I = Begin; I < Mid; ++I)
|
||||
Chunks[I]->Color[(Cnt + 1) % 2] = Id;
|
||||
Chunks[I]->Class[(Cnt + 1) % 2] = Id;
|
||||
|
||||
// If we created a group, we need to iterate the main loop again.
|
||||
if (Mid != End)
|
||||
@ -130,7 +130,7 @@ bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
|
||||
if (auto *D1 = dyn_cast<DefinedRegular>(B1))
|
||||
if (auto *D2 = dyn_cast<DefinedRegular>(B2))
|
||||
return D1->getValue() == D2->getValue() &&
|
||||
D1->getChunk()->Color[Cnt % 2] == D2->getChunk()->Color[Cnt % 2];
|
||||
D1->getChunk()->Class[Cnt % 2] == D2->getChunk()->Class[Cnt % 2];
|
||||
return false;
|
||||
};
|
||||
if (!std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq))
|
||||
@ -155,7 +155,7 @@ bool ICF::equalsVariable(const SectionChunk *A, const SectionChunk *B) {
|
||||
return true;
|
||||
if (auto *D1 = dyn_cast<DefinedRegular>(B1))
|
||||
if (auto *D2 = dyn_cast<DefinedRegular>(B2))
|
||||
return D1->getChunk()->Color[Cnt % 2] == D2->getChunk()->Color[Cnt % 2];
|
||||
return D1->getChunk()->Class[Cnt % 2] == D2->getChunk()->Class[Cnt % 2];
|
||||
return false;
|
||||
};
|
||||
return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq);
|
||||
@ -163,12 +163,12 @@ bool ICF::equalsVariable(const SectionChunk *A, const SectionChunk *B) {
|
||||
|
||||
size_t ICF::findBoundary(size_t Begin, size_t End) {
|
||||
for (size_t I = Begin + 1; I < End; ++I)
|
||||
if (Chunks[Begin]->Color[Cnt % 2] != Chunks[I]->Color[Cnt % 2])
|
||||
if (Chunks[Begin]->Class[Cnt % 2] != Chunks[I]->Class[Cnt % 2])
|
||||
return I;
|
||||
return End;
|
||||
}
|
||||
|
||||
void ICF::forEachColorRange(size_t Begin, size_t End,
|
||||
void ICF::forEachClassRange(size_t Begin, size_t End,
|
||||
std::function<void(size_t, size_t)> Fn) {
|
||||
if (Begin > 0)
|
||||
Begin = findBoundary(Begin - 1, End);
|
||||
@ -180,12 +180,12 @@ void ICF::forEachColorRange(size_t Begin, size_t End,
|
||||
}
|
||||
}
|
||||
|
||||
// Call Fn on each color group.
|
||||
void ICF::forEachColor(std::function<void(size_t, size_t)> Fn) {
|
||||
// Call Fn on each class group.
|
||||
void ICF::forEachClass(std::function<void(size_t, size_t)> Fn) {
|
||||
// If the number of sections are too small to use threading,
|
||||
// call Fn sequentially.
|
||||
if (Chunks.size() < 1024) {
|
||||
forEachColorRange(0, Chunks.size(), Fn);
|
||||
forEachClassRange(0, Chunks.size(), Fn);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -193,9 +193,9 @@ void ICF::forEachColor(std::function<void(size_t, size_t)> Fn) {
|
||||
size_t NumShards = 256;
|
||||
size_t Step = Chunks.size() / NumShards;
|
||||
parallel_for(size_t(0), NumShards, [&](size_t I) {
|
||||
forEachColorRange(I * Step, (I + 1) * Step, Fn);
|
||||
forEachClassRange(I * Step, (I + 1) * Step, Fn);
|
||||
});
|
||||
forEachColorRange(Step * NumShards, Chunks.size(), Fn);
|
||||
forEachClassRange(Step * NumShards, Chunks.size(), Fn);
|
||||
}
|
||||
|
||||
// Merge identical COMDAT sections.
|
||||
@ -209,11 +209,11 @@ void ICF::run(const std::vector<Chunk *> &Vec) {
|
||||
continue;
|
||||
|
||||
if (isEligible(SC)) {
|
||||
// Set MSB to 1 to avoid collisions with non-hash colors.
|
||||
SC->Color[0] = getHash(SC) | (1 << 31);
|
||||
// Set MSB to 1 to avoid collisions with non-hash classs.
|
||||
SC->Class[0] = getHash(SC) | (1 << 31);
|
||||
Chunks.push_back(SC);
|
||||
} else {
|
||||
SC->Color[0] = NextId++;
|
||||
SC->Class[0] = NextId++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,25 +224,25 @@ void ICF::run(const std::vector<Chunk *> &Vec) {
|
||||
// the same group are consecutive in the vector.
|
||||
std::stable_sort(Chunks.begin(), Chunks.end(),
|
||||
[](SectionChunk *A, SectionChunk *B) {
|
||||
return A->Color[0] < B->Color[0];
|
||||
return A->Class[0] < B->Class[0];
|
||||
});
|
||||
|
||||
// Compare static contents and assign unique IDs for each static content.
|
||||
forEachColor([&](size_t Begin, size_t End) { segregate(Begin, End, true); });
|
||||
forEachClass([&](size_t Begin, size_t End) { segregate(Begin, End, true); });
|
||||
++Cnt;
|
||||
|
||||
// Split groups by comparing relocations until convergence is obtained.
|
||||
do {
|
||||
Repeat = false;
|
||||
forEachColor(
|
||||
forEachClass(
|
||||
[&](size_t Begin, size_t End) { segregate(Begin, End, false); });
|
||||
++Cnt;
|
||||
} while (Repeat);
|
||||
|
||||
log("ICF needed " + Twine(Cnt) + " iterations");
|
||||
|
||||
// Merge sections in the same colors.
|
||||
forEachColor([&](size_t Begin, size_t End) {
|
||||
// Merge sections in the same classs.
|
||||
forEachClass([&](size_t Begin, size_t End) {
|
||||
if (End - Begin == 1)
|
||||
return;
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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};
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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>;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
65
include/lld/Core/TaskGroup.h
Normal file
65
include/lld/Core/TaskGroup.h
Normal 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
|
@ -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
|
@ -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
141
lib/Core/TaskGroup.cpp
Normal 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();
|
||||
});
|
||||
}
|
10
test/ELF/Inputs/i386-static-tls-model1.s
Normal file
10
test/ELF/Inputs/i386-static-tls-model1.s
Normal 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
|
9
test/ELF/Inputs/i386-static-tls-model2.s
Normal file
9
test/ELF/Inputs/i386-static-tls-model2.s
Normal 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
|
9
test/ELF/Inputs/i386-static-tls-model3.s
Normal file
9
test/ELF/Inputs/i386-static-tls-model3.s
Normal 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
|
9
test/ELF/Inputs/i386-static-tls-model4.s
Normal file
9
test/ELF/Inputs/i386-static-tls-model4.s
Normal 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
|
@ -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 {
|
||||
|
20
test/ELF/i386-static-tls-model.s
Normal file
20
test/ELF/i386-static-tls-model.s
Normal 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
|
@ -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
|
||||
|
3
test/ELF/linkerscript/Inputs/compress-debug-sections.s
Normal file
3
test/ELF/linkerscript/Inputs/compress-debug-sections.s
Normal file
@ -0,0 +1,3 @@
|
||||
.section .debug_str
|
||||
.asciz "CCC"
|
||||
.asciz "DDD"
|
36
test/ELF/linkerscript/compress-debug-sections.s
Normal file
36
test/ELF/linkerscript/compress-debug-sections.s
Normal 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"
|
6
test/ELF/lto/Inputs/duplicated-name.ll
Normal file
6
test/ELF/lto/Inputs/duplicated-name.ll
Normal 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
|
||||
}
|
@ -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"
|
||||
|
15
test/ELF/lto/duplicated-name.ll
Normal file
15
test/ELF/lto/duplicated-name.ll
Normal 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
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -3,5 +3,5 @@ add_lld_unittest(CoreTests
|
||||
)
|
||||
|
||||
target_link_libraries(CoreTests
|
||||
${LLVM_PTHREAD_LIB}
|
||||
lldCore ${LLVM_PTHREAD_LIB}
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user