Vendor import of lld trunk r301939:
https://llvm.org/svn/llvm-project/lld/trunk@301939
This commit is contained in:
parent
e06a19b85d
commit
274c9ff540
@ -326,41 +326,38 @@ void SEHTableChunk::writeTo(uint8_t *Buf) const {
|
||||
// usually loaded to that address. However, if there's already another
|
||||
// DLL that overlaps, the loader has to relocate it. To do that, DLLs
|
||||
// contain .reloc sections which contain offsets that need to be fixed
|
||||
// up at runtime. If the loader find that a DLL cannot be loaded to its
|
||||
// up at runtime. If the loader finds that a DLL cannot be loaded to its
|
||||
// desired base address, it loads it to somewhere else, and add <actual
|
||||
// base address> - <desired base address> to each offset that is
|
||||
// specified by .reloc section.
|
||||
// specified by the .reloc section. In ELF terms, .reloc sections
|
||||
// contain relative relocations in REL format (as opposed to RELA.)
|
||||
//
|
||||
// In ELF terms, .reloc sections contain arrays of relocation offsets.
|
||||
// All these offsets in the section are implicitly R_*_RELATIVE, and
|
||||
// addends are read from section contents (so it is REL as opposed to
|
||||
// RELA).
|
||||
// This already significantly reduces the size of relocations compared
|
||||
// to ELF .rel.dyn, but Windows does more to reduce it (probably because
|
||||
// it was invented for PCs in the late '80s or early '90s.) Offsets in
|
||||
// .reloc are grouped by page where the page size is 12 bits, and
|
||||
// offsets sharing the same page address are stored consecutively to
|
||||
// represent them with less space. This is very similar to the page
|
||||
// table which is grouped by (multiple stages of) pages.
|
||||
//
|
||||
// This already reduce the size of relocations to 1/3 compared to ELF
|
||||
// .dynrel, but Windows does more to reduce it (probably because it was
|
||||
// invented for PCs in the late '80s or early '90s.) Offsets in .reloc
|
||||
// are grouped by page where page size is 16 bits, and offsets sharing
|
||||
// the same page address are stored consecutively to represent them with
|
||||
// less space. This is a very similar to the page table which is grouped
|
||||
// by (multiple stages of) pages.
|
||||
//
|
||||
// For example, let's say we have 0x00030, 0x00500, 0x01000, 0x01100,
|
||||
// 0x20004, and 0x20008 in a .reloc section. In the section, they are
|
||||
// represented like this:
|
||||
// For example, let's say we have 0x00030, 0x00500, 0x00700, 0x00A00,
|
||||
// 0x20004, and 0x20008 in a .reloc section for x64. The uppermost 4
|
||||
// bits have a type IMAGE_REL_BASED_DIR64 or 0xA. In the section, they
|
||||
// are represented like this:
|
||||
//
|
||||
// 0x00000 -- page address (4 bytes)
|
||||
// 16 -- size of this block (4 bytes)
|
||||
// 0x0030 -- entries (2 bytes each)
|
||||
// 0x0500
|
||||
// 0x1000
|
||||
// 0x1100
|
||||
// 0xA030 -- entries (2 bytes each)
|
||||
// 0xA500
|
||||
// 0xA700
|
||||
// 0xAA00
|
||||
// 0x20000 -- page address (4 bytes)
|
||||
// 12 -- size of this block (4 bytes)
|
||||
// 0x0004 -- entries (2 bytes each)
|
||||
// 0x0008
|
||||
// 0xA004 -- entries (2 bytes each)
|
||||
// 0xA008
|
||||
//
|
||||
// Usually we have a lot of relocatinos for each page, so the number of
|
||||
// bytes for one .reloc entry is close to 2 bytes.
|
||||
// Usually we have a lot of relocations for each page, so the number of
|
||||
// bytes for one .reloc entry is close to 2 bytes on average.
|
||||
BaserelChunk::BaserelChunk(uint32_t Page, Baserel *Begin, Baserel *End) {
|
||||
// Block header consists of 4 byte page RVA and 4 byte block size.
|
||||
// Each entry is 2 byte. Last entry may be padding.
|
||||
|
@ -59,6 +59,7 @@ void log(const Twine &Msg) {
|
||||
if (Config->Verbose) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
outs() << Argv0 << ": " << Msg << "\n";
|
||||
outs().flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
10
COFF/ICF.cpp
10
COFF/ICF.cpp
@ -71,10 +71,18 @@ uint32_t ICF::getHash(SectionChunk *C) {
|
||||
}
|
||||
|
||||
// Returns true if section S is subject of ICF.
|
||||
//
|
||||
// Microsoft's documentation
|
||||
// (https://msdn.microsoft.com/en-us/library/bxwfs976.aspx; visited April
|
||||
// 2017) says that /opt:icf folds both functions and read-only data.
|
||||
// Despite that, the MSVC linker folds only functions. We found
|
||||
// a few instances of programs that are not safe for data merging.
|
||||
// Therefore, we merge only functions just like the MSVC tool.
|
||||
bool ICF::isEligible(SectionChunk *C) {
|
||||
bool Global = C->Sym && C->Sym->isExternal();
|
||||
bool Executable = C->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE;
|
||||
bool Writable = C->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
|
||||
return C->isCOMDAT() && C->isLive() && Global && !Writable;
|
||||
return C->isCOMDAT() && C->isLive() && Global && Executable && !Writable;
|
||||
}
|
||||
|
||||
// Split a range into smaller ranges by recoloring sections
|
||||
|
@ -327,6 +327,9 @@ void ImportFile::parse() {
|
||||
|
||||
ImpSym = cast<DefinedImportData>(
|
||||
Symtab->addImportData(ImpName, this)->body());
|
||||
if (Hdr->getType() == llvm::COFF::IMPORT_CONST)
|
||||
ConstSym =
|
||||
cast<DefinedImportData>(Symtab->addImportData(Name, this)->body());
|
||||
|
||||
// If type is function, we need to create a thunk which jump to an
|
||||
// address pointed by the __imp_ symbol. (This allows you to call
|
||||
|
@ -167,6 +167,7 @@ public:
|
||||
static bool classof(const InputFile *F) { return F->kind() == ImportKind; }
|
||||
|
||||
DefinedImportData *ImpSym = nullptr;
|
||||
DefinedImportData *ConstSym = nullptr;
|
||||
DefinedImportThunk *ThunkSym = nullptr;
|
||||
std::string DLLName;
|
||||
|
||||
|
141
COFF/MapFile.cpp
141
COFF/MapFile.cpp
@ -11,21 +11,21 @@
|
||||
// hierarchically the output sections, input sections, input files and
|
||||
// symbol:
|
||||
//
|
||||
// Address Size Align Out In File Symbol
|
||||
// =================================================================
|
||||
// 00201000 00000015 4 .text
|
||||
// 00201000 0000000e 4 .text
|
||||
// 00201000 0000000e 4 test.o
|
||||
// 0020100e 00000000 0 local
|
||||
// 00201005 00000000 0 f(int)
|
||||
// Address Size Align Out File Symbol
|
||||
// 00201000 00000015 4 .text
|
||||
// 00201000 0000000e 4 test.o:(.text)
|
||||
// 0020100e 00000000 0 local
|
||||
// 00201005 00000000 0 f(int)
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MapFile.h"
|
||||
#include "Error.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "Writer.h"
|
||||
|
||||
#include "lld/Core/Parallel.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
@ -34,72 +34,58 @@ using namespace llvm::object;
|
||||
using namespace lld;
|
||||
using namespace lld::coff;
|
||||
|
||||
static void writeOutSecLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size,
|
||||
uint64_t Align, StringRef Name) {
|
||||
OS << format("%08llx %08llx %5lld ", Address, Size, Align)
|
||||
<< left_justify(Name, 7);
|
||||
typedef DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>
|
||||
SymbolMapTy;
|
||||
|
||||
// Print out the first three columns of a line.
|
||||
static void writeHeader(raw_ostream &OS, uint64_t Addr, uint64_t Size,
|
||||
uint64_t Align) {
|
||||
OS << format("%08llx %08llx %5lld ", Addr, Size, Align);
|
||||
}
|
||||
|
||||
static void writeInSecLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size,
|
||||
uint64_t Align, StringRef Name) {
|
||||
// Pass an empty name to align the text to the correct column.
|
||||
writeOutSecLine(OS, Address, Size, Align, "");
|
||||
OS << ' ' << left_justify(Name, 7);
|
||||
static std::string indent(int Depth) { return std::string(Depth * 8, ' '); }
|
||||
|
||||
// Returns a list of all symbols that we want to print out.
|
||||
static std::vector<DefinedRegular *> getSymbols() {
|
||||
std::vector<DefinedRegular *> V;
|
||||
for (coff::ObjectFile *File : Symtab->ObjectFiles)
|
||||
for (SymbolBody *B : File->getSymbols())
|
||||
if (auto *Sym = dyn_cast<DefinedRegular>(B))
|
||||
if (Sym && !Sym->getCOFFSymbol().isSectionDefinition())
|
||||
V.push_back(Sym);
|
||||
return V;
|
||||
}
|
||||
|
||||
static void writeFileLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size,
|
||||
uint64_t Align, StringRef Name) {
|
||||
// Pass an empty name to align the text to the correct column.
|
||||
writeInSecLine(OS, Address, Size, Align, "");
|
||||
OS << ' ' << left_justify(Name, 7);
|
||||
}
|
||||
// Returns a map from sections to their symbols.
|
||||
static SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> Syms) {
|
||||
SymbolMapTy Ret;
|
||||
for (DefinedRegular *S : Syms)
|
||||
Ret[S->getChunk()].push_back(S);
|
||||
|
||||
static void writeSymbolLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size,
|
||||
StringRef Name) {
|
||||
// Pass an empty name to align the text to the correct column.
|
||||
writeFileLine(OS, Address, Size, 0, "");
|
||||
OS << ' ' << left_justify(Name, 7);
|
||||
}
|
||||
|
||||
static void writeSectionChunk(raw_fd_ostream &OS, const SectionChunk *SC,
|
||||
StringRef &PrevName) {
|
||||
StringRef Name = SC->getSectionName();
|
||||
if (Name != PrevName) {
|
||||
writeInSecLine(OS, SC->getRVA(), SC->getSize(), SC->getAlign(), Name);
|
||||
OS << '\n';
|
||||
PrevName = Name;
|
||||
}
|
||||
coff::ObjectFile *File = SC->File;
|
||||
if (!File)
|
||||
return;
|
||||
writeFileLine(OS, SC->getRVA(), SC->getSize(), SC->getAlign(),
|
||||
toString(File));
|
||||
OS << '\n';
|
||||
ArrayRef<SymbolBody *> Syms = File->getSymbols();
|
||||
for (SymbolBody *Sym : Syms) {
|
||||
auto *DR = dyn_cast<DefinedRegular>(Sym);
|
||||
if (!DR || DR->getChunk() != SC ||
|
||||
DR->getCOFFSymbol().isSectionDefinition())
|
||||
continue;
|
||||
writeSymbolLine(OS, DR->getRVA(), SC->getSize(), toString(*Sym));
|
||||
OS << '\n';
|
||||
// Sort symbols by address.
|
||||
for (auto &It : Ret) {
|
||||
SmallVectorImpl<DefinedRegular *> &V = It.second;
|
||||
std::sort(V.begin(), V.end(), [](DefinedRegular *A, DefinedRegular *B) {
|
||||
return A->getRVA() < B->getRVA();
|
||||
});
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
|
||||
static void writeMapFile2(raw_fd_ostream &OS,
|
||||
ArrayRef<OutputSection *> OutputSections) {
|
||||
OS << "Address Size Align Out In File Symbol\n";
|
||||
// Construct a map from symbols to their stringified representations.
|
||||
static DenseMap<DefinedRegular *, std::string>
|
||||
getSymbolStrings(ArrayRef<DefinedRegular *> Syms) {
|
||||
std::vector<std::string> Str(Syms.size());
|
||||
parallel_for((size_t)0, Syms.size(), [&](size_t I) {
|
||||
raw_string_ostream OS(Str[I]);
|
||||
writeHeader(OS, Syms[I]->getRVA(), 0, 0);
|
||||
OS << indent(2) << toString(*Syms[I]);
|
||||
});
|
||||
|
||||
for (OutputSection *Sec : OutputSections) {
|
||||
uint32_t VA = Sec->getRVA();
|
||||
writeOutSecLine(OS, VA, Sec->getVirtualSize(), /*Align=*/PageSize,
|
||||
Sec->getName());
|
||||
OS << '\n';
|
||||
StringRef PrevName = "";
|
||||
for (Chunk *C : Sec->getChunks())
|
||||
if (const auto *SC = dyn_cast<SectionChunk>(C))
|
||||
writeSectionChunk(OS, SC, PrevName);
|
||||
}
|
||||
DenseMap<DefinedRegular *, std::string> Ret;
|
||||
for (size_t I = 0, E = Syms.size(); I < E; ++I)
|
||||
Ret[Syms[I]] = std::move(Str[I]);
|
||||
return Ret;
|
||||
}
|
||||
|
||||
void coff::writeMapFile(ArrayRef<OutputSection *> OutputSections) {
|
||||
@ -110,5 +96,30 @@ void coff::writeMapFile(ArrayRef<OutputSection *> OutputSections) {
|
||||
raw_fd_ostream OS(Config->MapFile, EC, sys::fs::F_None);
|
||||
if (EC)
|
||||
fatal("cannot open " + Config->MapFile + ": " + EC.message());
|
||||
writeMapFile2(OS, OutputSections);
|
||||
|
||||
// Collect symbol info that we want to print out.
|
||||
std::vector<DefinedRegular *> Syms = getSymbols();
|
||||
SymbolMapTy SectionSyms = getSectionSyms(Syms);
|
||||
DenseMap<DefinedRegular *, std::string> SymStr = getSymbolStrings(Syms);
|
||||
|
||||
// Print out the header line.
|
||||
OS << "Address Size Align Out In Symbol\n";
|
||||
|
||||
// Print out file contents.
|
||||
for (OutputSection *Sec : OutputSections) {
|
||||
writeHeader(OS, Sec->getRVA(), Sec->getVirtualSize(), /*Align=*/PageSize);
|
||||
OS << Sec->getName() << '\n';
|
||||
|
||||
for (Chunk *C : Sec->getChunks()) {
|
||||
auto *SC = dyn_cast<SectionChunk>(C);
|
||||
if (!SC)
|
||||
continue;
|
||||
|
||||
writeHeader(OS, SC->getRVA(), SC->getSize(), SC->getAlign());
|
||||
OS << indent(1) << SC->File->getName() << ":(" << SC->getSectionName()
|
||||
<< ")\n";
|
||||
for (DefinedRegular *Sym : SectionSyms[SC])
|
||||
OS << SymStr[Sym] << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ struct Configuration {
|
||||
llvm::StringRef SoName;
|
||||
llvm::StringRef Sysroot;
|
||||
llvm::StringRef ThinLTOCacheDir;
|
||||
std::string RPath;
|
||||
std::string Rpath;
|
||||
std::vector<VersionDefinition> VersionDefinitions;
|
||||
std::vector<llvm::StringRef> AuxiliaryList;
|
||||
std::vector<llvm::StringRef> SearchPaths;
|
||||
|
@ -124,7 +124,7 @@ 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>
|
||||
LinkerDriver::getArchiveMembers(MemoryBufferRef MB) {
|
||||
static getArchiveMembers(MemoryBufferRef MB) {
|
||||
std::unique_ptr<Archive> File =
|
||||
check(Archive::create(MB),
|
||||
MB.getBufferIdentifier() + ": failed to parse archive");
|
||||
@ -242,6 +242,9 @@ static void checkOptions(opt::InputArgList &Args) {
|
||||
if (Config->Pie && Config->Shared)
|
||||
error("-shared and -pie may not be used together");
|
||||
|
||||
if (!Config->Shared && !Config->AuxiliaryList.empty())
|
||||
error("-f may not be used without -shared");
|
||||
|
||||
if (Config->Relocatable) {
|
||||
if (Config->Shared)
|
||||
error("-r and -shared may not be used together");
|
||||
@ -396,7 +399,7 @@ static std::vector<StringRef> getArgs(opt::InputArgList &Args, int Id) {
|
||||
return V;
|
||||
}
|
||||
|
||||
static std::string getRPath(opt::InputArgList &Args) {
|
||||
static std::string getRpath(opt::InputArgList &Args) {
|
||||
std::vector<StringRef> V = getArgs(Args, OPT_rpath);
|
||||
return llvm::join(V.begin(), V.end(), ":");
|
||||
}
|
||||
@ -444,16 +447,14 @@ static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &Args) {
|
||||
}
|
||||
|
||||
static Target2Policy getTarget2(opt::InputArgList &Args) {
|
||||
if (auto *Arg = Args.getLastArg(OPT_target2)) {
|
||||
StringRef S = Arg->getValue();
|
||||
if (S == "rel")
|
||||
return Target2Policy::Rel;
|
||||
if (S == "abs")
|
||||
return Target2Policy::Abs;
|
||||
if (S == "got-rel")
|
||||
return Target2Policy::GotRel;
|
||||
error("unknown --target2 option: " + S);
|
||||
}
|
||||
StringRef S = getString(Args, OPT_target2, "got-rel");
|
||||
if (S == "rel")
|
||||
return Target2Policy::Rel;
|
||||
if (S == "abs")
|
||||
return Target2Policy::Abs;
|
||||
if (S == "got-rel")
|
||||
return Target2Policy::GotRel;
|
||||
error("unknown --target2 option: " + S);
|
||||
return Target2Policy::GotRel;
|
||||
}
|
||||
|
||||
@ -550,6 +551,29 @@ static std::pair<bool, bool> getHashStyle(opt::InputArgList &Args) {
|
||||
return {true, true};
|
||||
}
|
||||
|
||||
// Parse --build-id or --build-id=<style>. We handle "tree" as a
|
||||
// synonym for "sha1" because all our hash functions including
|
||||
// -build-id=sha1 are actually tree hashes for performance reasons.
|
||||
static std::pair<BuildIdKind, std::vector<uint8_t>>
|
||||
getBuildId(opt::InputArgList &Args) {
|
||||
if (Args.hasArg(OPT_build_id))
|
||||
return {BuildIdKind::Fast, {}};
|
||||
|
||||
StringRef S = getString(Args, OPT_build_id_eq, "none");
|
||||
if (S == "md5")
|
||||
return {BuildIdKind::Md5, {}};
|
||||
if (S == "sha1" || S == "tree")
|
||||
return {BuildIdKind::Sha1, {}};
|
||||
if (S == "uuid")
|
||||
return {BuildIdKind::Uuid, {}};
|
||||
if (S.startswith("0x"))
|
||||
return {BuildIdKind::Hexstring, parseHex(S.substr(2))};
|
||||
|
||||
if (S != "none")
|
||||
error("unknown --build-id style: " + S);
|
||||
return {BuildIdKind::None, {}};
|
||||
}
|
||||
|
||||
static std::vector<StringRef> getLines(MemoryBufferRef MB) {
|
||||
SmallVector<StringRef, 0> Arr;
|
||||
MB.getBuffer().split(Arr, '\n');
|
||||
@ -564,14 +588,14 @@ static std::vector<StringRef> getLines(MemoryBufferRef MB) {
|
||||
}
|
||||
|
||||
static bool getCompressDebugSections(opt::InputArgList &Args) {
|
||||
if (auto *Arg = Args.getLastArg(OPT_compress_debug_sections)) {
|
||||
StringRef S = Arg->getValue();
|
||||
if (S == "zlib")
|
||||
return zlib::isAvailable();
|
||||
if (S != "none")
|
||||
error("unknown --compress-debug-sections value: " + S);
|
||||
}
|
||||
return false;
|
||||
StringRef S = getString(Args, OPT_compress_debug_sections, "none");
|
||||
if (S == "none")
|
||||
return false;
|
||||
if (S != "zlib")
|
||||
error("unknown --compress-debug-sections value: " + S);
|
||||
if (!zlib::isAvailable())
|
||||
error("--compress-debug-sections: zlib is not available");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Initializes Config members by the command line options.
|
||||
@ -616,7 +640,7 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
|
||||
Config->OutputFile = getString(Args, OPT_o);
|
||||
Config->Pie = getArg(Args, OPT_pie, OPT_nopie, false);
|
||||
Config->PrintGcSections = Args.hasArg(OPT_print_gc_sections);
|
||||
Config->RPath = getRPath(Args);
|
||||
Config->Rpath = getRpath(Args);
|
||||
Config->Relocatable = Args.hasArg(OPT_relocatable);
|
||||
Config->SaveTemps = Args.hasArg(OPT_save_temps);
|
||||
Config->SearchPaths = getArgs(Args, OPT_L);
|
||||
@ -679,32 +703,7 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
|
||||
Config->ZRelro = false;
|
||||
|
||||
std::tie(Config->SysvHash, Config->GnuHash) = getHashStyle(Args);
|
||||
|
||||
// Parse --build-id or --build-id=<style>. We handle "tree" as a
|
||||
// synonym for "sha1" because all of our hash functions including
|
||||
// -build-id=sha1 are tree hashes for performance reasons.
|
||||
if (Args.hasArg(OPT_build_id))
|
||||
Config->BuildId = BuildIdKind::Fast;
|
||||
if (auto *Arg = Args.getLastArg(OPT_build_id_eq)) {
|
||||
StringRef S = Arg->getValue();
|
||||
if (S == "md5") {
|
||||
Config->BuildId = BuildIdKind::Md5;
|
||||
} else if (S == "sha1" || S == "tree") {
|
||||
Config->BuildId = BuildIdKind::Sha1;
|
||||
} else if (S == "uuid") {
|
||||
Config->BuildId = BuildIdKind::Uuid;
|
||||
} else if (S == "none") {
|
||||
Config->BuildId = BuildIdKind::None;
|
||||
} else if (S.startswith("0x")) {
|
||||
Config->BuildId = BuildIdKind::Hexstring;
|
||||
Config->BuildIdVector = parseHex(S.substr(2));
|
||||
} else {
|
||||
error("unknown --build-id style: " + S);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Config->Shared && !Config->AuxiliaryList.empty())
|
||||
error("-f may not be used without -shared");
|
||||
std::tie(Config->BuildId, Config->BuildIdVector) = getBuildId(Args);
|
||||
|
||||
if (auto *Arg = Args.getLastArg(OPT_symbol_ordering_file))
|
||||
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
|
||||
|
@ -31,7 +31,6 @@ public:
|
||||
void addLibrary(StringRef Name);
|
||||
|
||||
private:
|
||||
std::vector<MemoryBufferRef> getArchiveMembers(MemoryBufferRef MB);
|
||||
void readConfigs(llvm::opt::InputArgList &Args);
|
||||
void createFiles(llvm::opt::InputArgList &Args);
|
||||
void inferMachineType();
|
||||
|
@ -60,6 +60,7 @@ void elf::log(const Twine &Msg) {
|
||||
if (Config->Verbose) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
outs() << Argv0 << ": " << Msg << "\n";
|
||||
outs().flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,10 +123,10 @@ std::string elf::ObjectFile<ELFT>::getLineInfo(InputSectionBase *S,
|
||||
return "";
|
||||
}
|
||||
|
||||
// Returns "(internal)", "foo.a(bar.o)" or "baz.o".
|
||||
// Returns "<internal>", "foo.a(bar.o)" or "baz.o".
|
||||
std::string lld::toString(const InputFile *F) {
|
||||
if (!F)
|
||||
return "(internal)";
|
||||
return "<internal>";
|
||||
|
||||
if (F->ToStringCache.empty()) {
|
||||
if (F->ArchiveName.empty())
|
||||
@ -137,15 +137,13 @@ std::string lld::toString(const InputFile *F) {
|
||||
return F->ToStringCache;
|
||||
}
|
||||
|
||||
template <class ELFT> static ELFKind getELFKind() {
|
||||
if (ELFT::TargetEndianness == support::little)
|
||||
return ELFT::Is64Bits ? ELF64LEKind : ELF32LEKind;
|
||||
return ELFT::Is64Bits ? ELF64BEKind : ELF32BEKind;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
ELFFileBase<ELFT>::ELFFileBase(Kind K, MemoryBufferRef MB) : InputFile(K, MB) {
|
||||
EKind = getELFKind<ELFT>();
|
||||
if (ELFT::TargetEndianness == support::little)
|
||||
EKind = ELFT::Is64Bits ? ELF64LEKind : ELF32LEKind;
|
||||
else
|
||||
EKind = ELFT::Is64Bits ? ELF64BEKind : ELF32BEKind;
|
||||
|
||||
EMachine = getObj().getHeader()->e_machine;
|
||||
OSABI = getObj().getHeader()->e_ident[llvm::ELF::EI_OSABI];
|
||||
}
|
||||
@ -174,8 +172,10 @@ void ELFFileBase<ELFT>::initSymtab(ArrayRef<Elf_Shdr> Sections,
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
elf::ObjectFile<ELFT>::ObjectFile(MemoryBufferRef M)
|
||||
: ELFFileBase<ELFT>(Base::ObjectKind, M) {}
|
||||
elf::ObjectFile<ELFT>::ObjectFile(MemoryBufferRef M, StringRef ArchiveName)
|
||||
: ELFFileBase<ELFT>(Base::ObjectKind, M) {
|
||||
this->ArchiveName = ArchiveName;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
ArrayRef<SymbolBody *> elf::ObjectFile<ELFT>::getLocalSymbols() {
|
||||
@ -361,6 +361,15 @@ InputSectionBase *elf::ObjectFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) {
|
||||
return Target;
|
||||
}
|
||||
|
||||
// Create a regular InputSection class that has the same contents
|
||||
// as a given section.
|
||||
InputSectionBase *toRegularSection(MergeInputSection *Sec) {
|
||||
auto *Ret = make<InputSection>(Sec->Flags, Sec->Type, Sec->Alignment,
|
||||
Sec->Data, Sec->Name);
|
||||
Ret->File = Sec->File;
|
||||
return Ret;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
InputSectionBase *
|
||||
elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec,
|
||||
@ -398,9 +407,18 @@ elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec,
|
||||
if (Target->FirstRelocation)
|
||||
fatal(toString(this) +
|
||||
": multiple relocation sections to one section are not supported");
|
||||
if (isa<MergeInputSection>(Target))
|
||||
fatal(toString(this) +
|
||||
": relocations pointing to SHF_MERGE are not supported");
|
||||
|
||||
// Mergeable sections with relocations are tricky because relocations
|
||||
// need to be taken into account when comparing section contents for
|
||||
// merging. It doesn't worth supporting such mergeable sections because
|
||||
// they are rare and it'd complicates the internal design (we usually
|
||||
// have to determine if two sections are mergeable early in the link
|
||||
// process much before applying relocations). We simply handle mergeable
|
||||
// sections with relocations as non-mergeable.
|
||||
if (auto *MS = dyn_cast<MergeInputSection>(Target)) {
|
||||
Target = toRegularSection(MS);
|
||||
this->Sections[Sec.sh_info] = Target;
|
||||
}
|
||||
|
||||
size_t NumRelocations;
|
||||
if (Sec.sh_type == SHT_RELA) {
|
||||
@ -461,6 +479,15 @@ elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec,
|
||||
if (Config->Strip != StripPolicy::None && Name.startswith(".debug"))
|
||||
return &InputSection::Discarded;
|
||||
|
||||
// If -gdb-index is given, LLD creates .gdb_index section, and that
|
||||
// section serves the same purpose as .debug_gnu_pub{names,types} sections.
|
||||
// If that's the case, we want to eliminate .debug_gnu_pub{names,types}
|
||||
// because they are redundant and can waste large amount of disk space
|
||||
// (for example, they are about 400 MiB in total for a clang debug build.)
|
||||
if (Config->GdbIndex &&
|
||||
(Name == ".debug_gnu_pubnames" || Name == ".debug_gnu_pubtypes"))
|
||||
return &InputSection::Discarded;
|
||||
|
||||
// The linkonce feature is a sort of proto-comdat. Some glibc i386 object
|
||||
// files contain definitions of symbol "__x86.get_pc_thunk.bx" in linkonce
|
||||
// sections. Drop those sections to avoid duplicate symbol errors.
|
||||
@ -665,7 +692,7 @@ template <class ELFT> void SharedFile<ELFT>::parseSoName() {
|
||||
uint64_t Val = Dyn.getVal();
|
||||
if (Val >= this->StringTable.size())
|
||||
fatal(toString(this) + ": invalid DT_SONAME entry");
|
||||
SoName = StringRef(this->StringTable.data() + Val);
|
||||
SoName = this->StringTable.data() + Val;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -748,7 +775,7 @@ template <class ELFT> void SharedFile<ELFT>::parseRest() {
|
||||
// with explicit versions.
|
||||
if (V) {
|
||||
StringRef VerName = this->StringTable.data() + V->getAux()->vda_name;
|
||||
Name = Saver.save(Twine(Name) + "@" + VerName);
|
||||
Name = Saver.save(Name + "@" + VerName);
|
||||
elf::Symtab<ELFT>::X->addShared(this, Name, Sym, V);
|
||||
}
|
||||
}
|
||||
@ -862,76 +889,50 @@ void BitcodeFile::parse(DenseSet<CachedHashStringRef> &ComdatGroups) {
|
||||
Symbols.push_back(createBitcodeSymbol<ELFT>(KeptComdats, ObjSym, this));
|
||||
}
|
||||
|
||||
// Small bit of template meta programming to handle the SharedFile constructor
|
||||
// being the only one with a DefaultSoName parameter.
|
||||
template <template <class> class T, class E>
|
||||
typename std::enable_if<std::is_same<T<E>, SharedFile<E>>::value,
|
||||
InputFile *>::type
|
||||
createELFAux(MemoryBufferRef MB, StringRef DefaultSoName) {
|
||||
return make<T<E>>(MB, DefaultSoName);
|
||||
}
|
||||
template <template <class> class T, class E>
|
||||
typename std::enable_if<!std::is_same<T<E>, SharedFile<E>>::value,
|
||||
InputFile *>::type
|
||||
createELFAux(MemoryBufferRef MB, StringRef DefaultSoName) {
|
||||
return make<T<E>>(MB);
|
||||
}
|
||||
|
||||
template <template <class> class T>
|
||||
static InputFile *createELFFile(MemoryBufferRef MB, StringRef DefaultSoName) {
|
||||
static ELFKind getELFKind(MemoryBufferRef MB) {
|
||||
unsigned char Size;
|
||||
unsigned char Endian;
|
||||
std::tie(Size, Endian) = getElfArchType(MB.getBuffer());
|
||||
|
||||
if (Endian != ELFDATA2LSB && Endian != ELFDATA2MSB)
|
||||
fatal(MB.getBufferIdentifier() + ": invalid data encoding");
|
||||
if (Size != ELFCLASS32 && Size != ELFCLASS64)
|
||||
fatal(MB.getBufferIdentifier() + ": invalid file class");
|
||||
|
||||
size_t BufSize = MB.getBuffer().size();
|
||||
if ((Size == ELFCLASS32 && BufSize < sizeof(Elf32_Ehdr)) ||
|
||||
(Size == ELFCLASS64 && BufSize < sizeof(Elf64_Ehdr)))
|
||||
fatal(MB.getBufferIdentifier() + ": file is too short");
|
||||
|
||||
InputFile *Obj;
|
||||
if (Size == ELFCLASS32 && Endian == ELFDATA2LSB)
|
||||
Obj = createELFAux<T, ELF32LE>(MB, DefaultSoName);
|
||||
else if (Size == ELFCLASS32 && Endian == ELFDATA2MSB)
|
||||
Obj = createELFAux<T, ELF32BE>(MB, DefaultSoName);
|
||||
else if (Size == ELFCLASS64 && Endian == ELFDATA2LSB)
|
||||
Obj = createELFAux<T, ELF64LE>(MB, DefaultSoName);
|
||||
else if (Size == ELFCLASS64 && Endian == ELFDATA2MSB)
|
||||
Obj = createELFAux<T, ELF64BE>(MB, DefaultSoName);
|
||||
else
|
||||
fatal(MB.getBufferIdentifier() + ": invalid file class");
|
||||
|
||||
if (!Config->FirstElf)
|
||||
Config->FirstElf = Obj;
|
||||
return Obj;
|
||||
if (Size == ELFCLASS32)
|
||||
return (Endian == ELFDATA2LSB) ? ELF32LEKind : ELF32BEKind;
|
||||
return (Endian == ELFDATA2LSB) ? ELF64LEKind : ELF64BEKind;
|
||||
}
|
||||
|
||||
template <class ELFT> void BinaryFile::parse() {
|
||||
StringRef Buf = MB.getBuffer();
|
||||
ArrayRef<uint8_t> Data =
|
||||
makeArrayRef<uint8_t>((const uint8_t *)Buf.data(), Buf.size());
|
||||
|
||||
std::string Filename = MB.getBufferIdentifier();
|
||||
std::transform(Filename.begin(), Filename.end(), Filename.begin(),
|
||||
[](char C) { return isalnum(C) ? C : '_'; });
|
||||
Filename = "_binary_" + Filename;
|
||||
StringRef StartName = Saver.save(Twine(Filename) + "_start");
|
||||
StringRef EndName = Saver.save(Twine(Filename) + "_end");
|
||||
StringRef SizeName = Saver.save(Twine(Filename) + "_size");
|
||||
|
||||
ArrayRef<uint8_t> Data = toArrayRef(MB.getBuffer());
|
||||
auto *Section =
|
||||
make<InputSection>(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, 8, Data, ".data");
|
||||
Sections.push_back(Section);
|
||||
|
||||
elf::Symtab<ELFT>::X->addRegular(StartName, STV_DEFAULT, STT_OBJECT, 0, 0,
|
||||
STB_GLOBAL, Section, nullptr);
|
||||
elf::Symtab<ELFT>::X->addRegular(EndName, STV_DEFAULT, STT_OBJECT,
|
||||
Data.size(), 0, STB_GLOBAL, Section,
|
||||
nullptr);
|
||||
elf::Symtab<ELFT>::X->addRegular(SizeName, STV_DEFAULT, STT_OBJECT,
|
||||
Data.size(), 0, STB_GLOBAL, nullptr,
|
||||
// For each input file foo that is embedded to a result as a binary
|
||||
// blob, we define _binary_foo_{start,end,size} symbols, so that
|
||||
// user programs can access blobs by name. Non-alphanumeric
|
||||
// characters in a filename are replaced with underscore.
|
||||
std::string S = "_binary_" + MB.getBufferIdentifier().str();
|
||||
for (size_t I = 0; I < S.size(); ++I)
|
||||
if (!isalnum(S[I]))
|
||||
S[I] = '_';
|
||||
|
||||
elf::Symtab<ELFT>::X->addRegular(Saver.save(S + "_start"), STV_DEFAULT,
|
||||
STT_OBJECT, 0, 0, STB_GLOBAL, Section,
|
||||
nullptr);
|
||||
elf::Symtab<ELFT>::X->addRegular(Saver.save(S + "_end"), STV_DEFAULT,
|
||||
STT_OBJECT, Data.size(), 0, STB_GLOBAL,
|
||||
Section, nullptr);
|
||||
elf::Symtab<ELFT>::X->addRegular(Saver.save(S + "_size"), STV_DEFAULT,
|
||||
STT_OBJECT, Data.size(), 0, STB_GLOBAL,
|
||||
nullptr, nullptr);
|
||||
}
|
||||
|
||||
static bool isBitcode(MemoryBufferRef MB) {
|
||||
@ -941,15 +942,36 @@ static bool isBitcode(MemoryBufferRef MB) {
|
||||
|
||||
InputFile *elf::createObjectFile(MemoryBufferRef MB, StringRef ArchiveName,
|
||||
uint64_t OffsetInArchive) {
|
||||
InputFile *F = isBitcode(MB)
|
||||
? make<BitcodeFile>(MB, ArchiveName, OffsetInArchive)
|
||||
: createELFFile<ObjectFile>(MB, "");
|
||||
F->ArchiveName = ArchiveName;
|
||||
return F;
|
||||
if (isBitcode(MB))
|
||||
return make<BitcodeFile>(MB, ArchiveName, OffsetInArchive);
|
||||
|
||||
switch (getELFKind(MB)) {
|
||||
case ELF32LEKind:
|
||||
return make<ObjectFile<ELF32LE>>(MB, ArchiveName);
|
||||
case ELF32BEKind:
|
||||
return make<ObjectFile<ELF32BE>>(MB, ArchiveName);
|
||||
case ELF64LEKind:
|
||||
return make<ObjectFile<ELF64LE>>(MB, ArchiveName);
|
||||
case ELF64BEKind:
|
||||
return make<ObjectFile<ELF64BE>>(MB, ArchiveName);
|
||||
default:
|
||||
llvm_unreachable("getELFKind");
|
||||
}
|
||||
}
|
||||
|
||||
InputFile *elf::createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName) {
|
||||
return createELFFile<SharedFile>(MB, DefaultSoName);
|
||||
switch (getELFKind(MB)) {
|
||||
case ELF32LEKind:
|
||||
return make<SharedFile<ELF32LE>>(MB, DefaultSoName);
|
||||
case ELF32BEKind:
|
||||
return make<SharedFile<ELF32BE>>(MB, DefaultSoName);
|
||||
case ELF64LEKind:
|
||||
return make<SharedFile<ELF64LE>>(MB, DefaultSoName);
|
||||
case ELF64BEKind:
|
||||
return make<SharedFile<ELF64BE>>(MB, DefaultSoName);
|
||||
default:
|
||||
llvm_unreachable("getELFKind");
|
||||
}
|
||||
}
|
||||
|
||||
MemoryBufferRef LazyObjectFile::getBuffer() {
|
||||
@ -1004,17 +1026,18 @@ std::vector<StringRef> LazyObjectFile::getSymbols() {
|
||||
if (isBitcode(this->MB))
|
||||
return getBitcodeSymbols();
|
||||
|
||||
unsigned char Size;
|
||||
unsigned char Endian;
|
||||
std::tie(Size, Endian) = getElfArchType(this->MB.getBuffer());
|
||||
if (Size == ELFCLASS32) {
|
||||
if (Endian == ELFDATA2LSB)
|
||||
return getElfSymbols<ELF32LE>();
|
||||
switch (getELFKind(this->MB)) {
|
||||
case ELF32LEKind:
|
||||
return getElfSymbols<ELF32LE>();
|
||||
case ELF32BEKind:
|
||||
return getElfSymbols<ELF32BE>();
|
||||
}
|
||||
if (Endian == ELFDATA2LSB)
|
||||
case ELF64LEKind:
|
||||
return getElfSymbols<ELF64LE>();
|
||||
return getElfSymbols<ELF64BE>();
|
||||
case ELF64BEKind:
|
||||
return getElfSymbols<ELF64BE>();
|
||||
default:
|
||||
llvm_unreachable("getELFKind");
|
||||
}
|
||||
}
|
||||
|
||||
template void ArchiveFile::parse<ELF32LE>();
|
||||
|
@ -156,7 +156,7 @@ public:
|
||||
ArrayRef<SymbolBody *> getSymbols();
|
||||
ArrayRef<SymbolBody *> getLocalSymbols();
|
||||
|
||||
explicit ObjectFile(MemoryBufferRef M);
|
||||
ObjectFile(MemoryBufferRef M, StringRef ArchiveName);
|
||||
void parse(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
|
||||
|
||||
InputSectionBase *getSection(const Elf_Sym &Sym) const;
|
||||
|
@ -39,9 +39,7 @@ std::vector<InputSectionBase *> elf::InputSections;
|
||||
|
||||
// Returns a string to construct an error message.
|
||||
std::string lld::toString(const InputSectionBase *Sec) {
|
||||
// File can be absent if section is synthetic.
|
||||
std::string FileName = Sec->File ? Sec->File->getName() : "<internal>";
|
||||
return (FileName + ":(" + Sec->Name + ")").str();
|
||||
return (toString(Sec->File) + ":(" + Sec->Name + ")").str();
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
|
@ -105,6 +105,11 @@ BitcodeCompiler::~BitcodeCompiler() = default;
|
||||
static void undefine(Symbol *S) {
|
||||
replaceBody<Undefined>(S, S->body()->getName(), /*IsLocal=*/false,
|
||||
STV_DEFAULT, S->body()->Type, nullptr);
|
||||
// It shouldn't normally matter what the binding is, but if a bug in the LTO
|
||||
// implementation causes it to fail to provide a definition for a symbol,
|
||||
// setting the binding to STB_GLOBAL will cause the linker to report an
|
||||
// undefined symbol error, even if the definition was weak.
|
||||
S->Binding = STB_GLOBAL;
|
||||
}
|
||||
|
||||
void BitcodeCompiler::add(BitcodeFile &F) {
|
||||
|
@ -406,8 +406,15 @@ void LinkerScript::processCommands(OutputSectionFactory &Factory) {
|
||||
}
|
||||
|
||||
// Add input sections to an output section.
|
||||
for (InputSectionBase *S : V)
|
||||
Factory.addInputSec(S, Cmd->Name);
|
||||
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++;
|
||||
Factory.addInputSec(S, Cmd->Name, Cmd->Sec);
|
||||
}
|
||||
}
|
||||
}
|
||||
CurOutSec = nullptr;
|
||||
@ -465,9 +472,26 @@ void LinkerScript::fabricateDefaultCommands(bool AllocateHeader) {
|
||||
|
||||
// Add sections that didn't match any sections command.
|
||||
void LinkerScript::addOrphanSections(OutputSectionFactory &Factory) {
|
||||
for (InputSectionBase *S : InputSections)
|
||||
if (S->Live && !S->OutSec)
|
||||
Factory.addInputSec(S, getOutputSectionName(S->Name));
|
||||
for (InputSectionBase *S : InputSections) {
|
||||
if (!S->Live || S->OutSec)
|
||||
continue;
|
||||
StringRef Name = getOutputSectionName(S->Name);
|
||||
auto I = std::find_if(
|
||||
Opt.Commands.begin(), Opt.Commands.end(), [&](BaseCommand *Base) {
|
||||
if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base))
|
||||
return Cmd->Name == Name;
|
||||
return false;
|
||||
});
|
||||
if (I == Opt.Commands.end()) {
|
||||
Factory.addInputSec(S, Name);
|
||||
} else {
|
||||
auto *Cmd = cast<OutputSectionCommand>(*I);
|
||||
Factory.addInputSec(S, Name, Cmd->Sec);
|
||||
auto *ISD = make<InputSectionDescription>("");
|
||||
ISD->Sections.push_back(S);
|
||||
Cmd->Commands.push_back(ISD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool isTbss(OutputSection *Sec) {
|
||||
@ -475,8 +499,6 @@ static bool isTbss(OutputSection *Sec) {
|
||||
}
|
||||
|
||||
void LinkerScript::output(InputSection *S) {
|
||||
if (!AlreadyOutputIS.insert(S).second)
|
||||
return;
|
||||
bool IsTbss = isTbss(CurOutSec);
|
||||
|
||||
uint64_t Pos = IsTbss ? Dot + ThreadBssOffset : Dot;
|
||||
@ -508,19 +530,9 @@ void LinkerScript::output(InputSection *S) {
|
||||
Dot = Pos;
|
||||
}
|
||||
|
||||
void LinkerScript::flush() {
|
||||
assert(CurOutSec);
|
||||
if (!AlreadyOutputOS.insert(CurOutSec).second)
|
||||
return;
|
||||
for (InputSection *I : CurOutSec->Sections)
|
||||
output(I);
|
||||
}
|
||||
|
||||
void LinkerScript::switchTo(OutputSection *Sec) {
|
||||
if (CurOutSec == Sec)
|
||||
return;
|
||||
if (AlreadyOutputOS.count(Sec))
|
||||
return;
|
||||
|
||||
CurOutSec = Sec;
|
||||
|
||||
@ -571,19 +583,11 @@ void LinkerScript::process(BaseCommand &Base) {
|
||||
|
||||
if (!Sec->Live)
|
||||
continue;
|
||||
assert(CurOutSec == Sec->OutSec || AlreadyOutputOS.count(Sec->OutSec));
|
||||
assert(CurOutSec == Sec->OutSec);
|
||||
output(cast<InputSection>(Sec));
|
||||
}
|
||||
}
|
||||
|
||||
static OutputSection *
|
||||
findSection(StringRef Name, const std::vector<OutputSection *> &Sections) {
|
||||
for (OutputSection *Sec : Sections)
|
||||
if (Sec->Name == Name)
|
||||
return Sec;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// This function searches for a memory region to place the given output
|
||||
// section in. If found, a pointer to the appropriate memory region is
|
||||
// returned. Otherwise, a nullptr is returned.
|
||||
@ -638,19 +642,8 @@ void LinkerScript::assignOffsets(OutputSectionCommand *Cmd) {
|
||||
Dot = CurMemRegion->Offset;
|
||||
switchTo(Sec);
|
||||
|
||||
// flush() may add orphan sections, so the order of flush() and
|
||||
// symbol assignments is important. We want to call flush() first so
|
||||
// that symbols pointing the end of the current section points to
|
||||
// the location after orphan sections.
|
||||
auto Mid =
|
||||
std::find_if(Cmd->Commands.rbegin(), Cmd->Commands.rend(),
|
||||
[](BaseCommand *Cmd) { return !isa<SymbolAssignment>(Cmd); })
|
||||
.base();
|
||||
for (auto I = Cmd->Commands.begin(); I != Mid; ++I)
|
||||
process(**I);
|
||||
flush();
|
||||
for (auto I = Mid, E = Cmd->Commands.end(); I != E; ++I)
|
||||
process(**I);
|
||||
for (BaseCommand *C : Cmd->Commands)
|
||||
process(*C);
|
||||
}
|
||||
|
||||
void LinkerScript::removeEmptyCommands() {
|
||||
@ -663,7 +656,8 @@ void LinkerScript::removeEmptyCommands() {
|
||||
auto Pos = std::remove_if(
|
||||
Opt.Commands.begin(), Opt.Commands.end(), [&](BaseCommand *Base) {
|
||||
if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base))
|
||||
return !Cmd->Sec;
|
||||
return std::find(OutputSections->begin(), OutputSections->end(),
|
||||
Cmd->Sec) == OutputSections->end();
|
||||
return false;
|
||||
});
|
||||
Opt.Commands.erase(Pos, Opt.Commands.end());
|
||||
@ -687,8 +681,7 @@ void LinkerScript::adjustSectionsBeforeSorting() {
|
||||
auto *Cmd = dyn_cast<OutputSectionCommand>(Base);
|
||||
if (!Cmd)
|
||||
continue;
|
||||
if (OutputSection *Sec = findSection(Cmd->Name, *OutputSections)) {
|
||||
Cmd->Sec = Sec;
|
||||
if (OutputSection *Sec = Cmd->Sec) {
|
||||
Flags = Sec->Flags;
|
||||
Type = Sec->Type;
|
||||
continue;
|
||||
@ -820,15 +813,24 @@ void LinkerScript::placeOrphanSections() {
|
||||
++CmdIndex;
|
||||
}
|
||||
|
||||
// If there is no command corresponding to this output section,
|
||||
// create one and put a InputSectionDescription in it so that both
|
||||
// representations agree on which input sections to use.
|
||||
auto Pos = std::find_if(CmdIter, E, [&](BaseCommand *Base) {
|
||||
auto *Cmd = dyn_cast<OutputSectionCommand>(Base);
|
||||
return Cmd && Cmd->Name == Name;
|
||||
});
|
||||
if (Pos == E) {
|
||||
auto *Cmd = make<OutputSectionCommand>(Name);
|
||||
Cmd->Sec = Sec;
|
||||
Opt.Commands.insert(CmdIter, Cmd);
|
||||
++CmdIndex;
|
||||
|
||||
Cmd->Sec = Sec;
|
||||
auto *ISD = make<InputSectionDescription>("");
|
||||
for (InputSection *IS : Sec->Sections)
|
||||
ISD->Sections.push_back(IS);
|
||||
Cmd->Commands.push_back(ISD);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -846,6 +848,51 @@ void LinkerScript::processNonSectionCommands() {
|
||||
}
|
||||
}
|
||||
|
||||
// Do a last effort at synchronizing the linker script "AST" and the section
|
||||
// list. This is needed to account for last minute changes, like adding a
|
||||
// .ARM.exidx terminator and sorting SHF_LINK_ORDER sections.
|
||||
//
|
||||
// FIXME: We should instead create the "AST" earlier and the above changes would
|
||||
// be done directly in the "AST".
|
||||
//
|
||||
// This can only handle new sections being added and sections being reordered.
|
||||
void LinkerScript::synchronize() {
|
||||
for (BaseCommand *Base : Opt.Commands) {
|
||||
auto *Cmd = dyn_cast<OutputSectionCommand>(Base);
|
||||
if (!Cmd)
|
||||
continue;
|
||||
ArrayRef<InputSection *> Sections = Cmd->Sec->Sections;
|
||||
std::vector<InputSectionBase **> ScriptSections;
|
||||
DenseSet<InputSectionBase *> ScriptSectionsSet;
|
||||
for (BaseCommand *Base : Cmd->Commands) {
|
||||
auto *ISD = dyn_cast<InputSectionDescription>(Base);
|
||||
if (!ISD)
|
||||
continue;
|
||||
for (InputSectionBase *&IS : ISD->Sections) {
|
||||
if (IS->Live) {
|
||||
ScriptSections.push_back(&IS);
|
||||
ScriptSectionsSet.insert(IS);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<InputSectionBase *> Missing;
|
||||
for (InputSection *IS : Sections)
|
||||
if (!ScriptSectionsSet.count(IS))
|
||||
Missing.push_back(IS);
|
||||
if (!Missing.empty()) {
|
||||
auto ISD = make<InputSectionDescription>("");
|
||||
ISD->Sections = Missing;
|
||||
Cmd->Commands.push_back(ISD);
|
||||
for (InputSectionBase *&IS : ISD->Sections)
|
||||
if (IS->Live)
|
||||
ScriptSections.push_back(&IS);
|
||||
}
|
||||
assert(ScriptSections.size() == Sections.size());
|
||||
for (int I = 0, N = Sections.size(); I < N; ++I)
|
||||
*ScriptSections[I] = Sections[I];
|
||||
}
|
||||
}
|
||||
|
||||
void LinkerScript::assignAddresses(std::vector<PhdrEntry> &Phdrs) {
|
||||
// Assign addresses as instructed by linker script SECTIONS sub-commands.
|
||||
Dot = 0;
|
||||
|
@ -228,7 +228,6 @@ protected:
|
||||
MemoryRegion *findMemoryRegion(OutputSectionCommand *Cmd);
|
||||
|
||||
void switchTo(OutputSection *Sec);
|
||||
void flush();
|
||||
void output(InputSection *Sec);
|
||||
void process(BaseCommand &Base);
|
||||
|
||||
@ -242,9 +241,6 @@ protected:
|
||||
OutputSection *CurOutSec = nullptr;
|
||||
MemoryRegion *CurMemRegion = nullptr;
|
||||
|
||||
llvm::DenseSet<OutputSection *> AlreadyOutputOS;
|
||||
llvm::DenseSet<InputSectionBase *> AlreadyOutputIS;
|
||||
|
||||
public:
|
||||
bool hasPhdrsCommands() { return !Opt.PhdrsCommands.empty(); }
|
||||
uint64_t getDot() { return Dot; }
|
||||
@ -271,6 +267,7 @@ public:
|
||||
void assignOffsets(OutputSectionCommand *Cmd);
|
||||
void placeOrphanSections();
|
||||
void processNonSectionCommands();
|
||||
void synchronize();
|
||||
void assignAddresses(std::vector<PhdrEntry> &Phdrs);
|
||||
int getSectionIndex(StringRef Name);
|
||||
|
||||
|
171
ELF/MapFile.cpp
171
ELF/MapFile.cpp
@ -11,19 +11,19 @@
|
||||
// hierarchically the output sections, input sections, input files and
|
||||
// symbol:
|
||||
//
|
||||
// Address Size Align Out In File Symbol
|
||||
// =================================================================
|
||||
// 00201000 00000015 4 .text
|
||||
// 00201000 0000000e 4 .text
|
||||
// 00201000 0000000e 4 test.o
|
||||
// 0020100e 00000000 0 local
|
||||
// 00201005 00000000 0 f(int)
|
||||
// Address Size Align Out In Symbol
|
||||
// 00201000 00000015 4 .text
|
||||
// 00201000 0000000e 4 test.o:(.text)
|
||||
// 0020100e 00000000 0 local
|
||||
// 00201005 00000000 0 f(int)
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MapFile.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Strings.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Threads.h"
|
||||
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
@ -33,83 +33,68 @@ using namespace llvm::object;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
static void writeOutSecLine(raw_fd_ostream &OS, int Width, uint64_t Address,
|
||||
uint64_t Size, uint64_t Align, StringRef Name) {
|
||||
OS << format("%0*llx %0*llx %5lld ", Width, Address, Width, Size, Align)
|
||||
<< left_justify(Name, 7);
|
||||
}
|
||||
|
||||
static void writeInSecLine(raw_fd_ostream &OS, int Width, uint64_t Address,
|
||||
uint64_t Size, uint64_t Align, StringRef Name) {
|
||||
// Pass an empty name to align the text to the correct column.
|
||||
writeOutSecLine(OS, Width, Address, Size, Align, "");
|
||||
OS << ' ' << left_justify(Name, 7);
|
||||
}
|
||||
|
||||
static void writeFileLine(raw_fd_ostream &OS, int Width, uint64_t Address,
|
||||
uint64_t Size, uint64_t Align, StringRef Name) {
|
||||
// Pass an empty name to align the text to the correct column.
|
||||
writeInSecLine(OS, Width, Address, Size, Align, "");
|
||||
OS << ' ' << left_justify(Name, 7);
|
||||
}
|
||||
|
||||
static void writeSymbolLine(raw_fd_ostream &OS, int Width, uint64_t Address,
|
||||
uint64_t Size, StringRef Name) {
|
||||
// Pass an empty name to align the text to the correct column.
|
||||
writeFileLine(OS, Width, Address, Size, 0, "");
|
||||
OS << ' ' << left_justify(Name, 7);
|
||||
}
|
||||
typedef DenseMap<const SectionBase *, SmallVector<DefinedRegular *, 4>>
|
||||
SymbolMapTy;
|
||||
|
||||
// Print out the first three columns of a line.
|
||||
template <class ELFT>
|
||||
static void writeInputSection(raw_fd_ostream &OS, const InputSection *IS,
|
||||
StringRef &PrevName) {
|
||||
int Width = ELFT::Is64Bits ? 16 : 8;
|
||||
StringRef Name = IS->Name;
|
||||
if (Name != PrevName) {
|
||||
writeInSecLine(OS, Width, IS->OutSec->Addr + IS->OutSecOff, IS->getSize(),
|
||||
IS->Alignment, Name);
|
||||
OS << '\n';
|
||||
PrevName = Name;
|
||||
}
|
||||
|
||||
elf::ObjectFile<ELFT> *File = IS->template getFile<ELFT>();
|
||||
if (!File)
|
||||
return;
|
||||
writeFileLine(OS, Width, IS->OutSec->Addr + IS->OutSecOff, IS->getSize(),
|
||||
IS->Alignment, toString(File));
|
||||
OS << '\n';
|
||||
|
||||
for (SymbolBody *Sym : File->getSymbols()) {
|
||||
auto *DR = dyn_cast<DefinedRegular>(Sym);
|
||||
if (!DR)
|
||||
continue;
|
||||
if (DR->Section != IS)
|
||||
continue;
|
||||
if (DR->isSection())
|
||||
continue;
|
||||
writeSymbolLine(OS, Width, Sym->getVA(), Sym->getSize<ELFT>(),
|
||||
toString(*Sym));
|
||||
OS << '\n';
|
||||
}
|
||||
static void writeHeader(raw_ostream &OS, uint64_t Addr, uint64_t Size,
|
||||
uint64_t Align) {
|
||||
int W = ELFT::Is64Bits ? 16 : 8;
|
||||
OS << format("%0*llx %0*llx %5lld ", W, Addr, W, Size, Align);
|
||||
}
|
||||
|
||||
static std::string indent(int Depth) { return std::string(Depth * 8, ' '); }
|
||||
|
||||
// Returns a list of all symbols that we want to print out.
|
||||
template <class ELFT> std::vector<DefinedRegular *> getSymbols() {
|
||||
std::vector<DefinedRegular *> V;
|
||||
for (elf::ObjectFile<ELFT> *File : Symtab<ELFT>::X->getObjectFiles())
|
||||
for (SymbolBody *B : File->getSymbols())
|
||||
if (B->File == File && !B->isSection())
|
||||
if (auto *Sym = dyn_cast<DefinedRegular>(B))
|
||||
if (Sym->Section)
|
||||
V.push_back(Sym);
|
||||
return V;
|
||||
}
|
||||
|
||||
// Returns a map from sections to their symbols.
|
||||
template <class ELFT>
|
||||
static void writeMapFile2(raw_fd_ostream &OS,
|
||||
ArrayRef<OutputSection *> OutputSections) {
|
||||
int Width = ELFT::Is64Bits ? 16 : 8;
|
||||
SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> Syms) {
|
||||
SymbolMapTy Ret;
|
||||
for (DefinedRegular *S : Syms)
|
||||
Ret[S->Section].push_back(S);
|
||||
|
||||
OS << left_justify("Address", Width) << ' ' << left_justify("Size", Width)
|
||||
<< " Align Out In File Symbol\n";
|
||||
|
||||
for (OutputSection *Sec : OutputSections) {
|
||||
writeOutSecLine(OS, Width, Sec->Addr, Sec->Size, Sec->Alignment, Sec->Name);
|
||||
OS << '\n';
|
||||
|
||||
StringRef PrevName = "";
|
||||
for (InputSection *IS : Sec->Sections) {
|
||||
writeInputSection<ELFT>(OS, IS, PrevName);
|
||||
}
|
||||
// Sort symbols by address. We want to print out symbols in the
|
||||
// order in the output file rather than the order they appeared
|
||||
// in the input files.
|
||||
for (auto &It : Ret) {
|
||||
SmallVectorImpl<DefinedRegular *> &V = It.second;
|
||||
std::sort(V.begin(), V.end(), [](DefinedRegular *A, DefinedRegular *B) {
|
||||
return A->getVA() < B->getVA();
|
||||
});
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
|
||||
// Construct a map from symbols to their stringified representations.
|
||||
// Demangling symbols (which is what toString() does) is slow, so
|
||||
// we do that in batch using parallel-for.
|
||||
template <class ELFT>
|
||||
DenseMap<DefinedRegular *, std::string>
|
||||
getSymbolStrings(ArrayRef<DefinedRegular *> Syms) {
|
||||
std::vector<std::string> Str(Syms.size());
|
||||
parallelFor(0, Syms.size(), [&](size_t I) {
|
||||
raw_string_ostream OS(Str[I]);
|
||||
writeHeader<ELFT>(OS, Syms[I]->getVA(), Syms[I]->template getSize<ELFT>(),
|
||||
0);
|
||||
OS << indent(2) << toString(*Syms[I]);
|
||||
});
|
||||
|
||||
DenseMap<DefinedRegular *, std::string> Ret;
|
||||
for (size_t I = 0, E = Syms.size(); I < E; ++I)
|
||||
Ret[Syms[I]] = std::move(Str[I]);
|
||||
return Ret;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
@ -117,12 +102,38 @@ void elf::writeMapFile(ArrayRef<OutputSection *> OutputSections) {
|
||||
if (Config->MapFile.empty())
|
||||
return;
|
||||
|
||||
// Open a map file for writing.
|
||||
std::error_code EC;
|
||||
raw_fd_ostream OS(Config->MapFile, EC, sys::fs::F_None);
|
||||
if (EC)
|
||||
if (EC) {
|
||||
error("cannot open " + Config->MapFile + ": " + EC.message());
|
||||
else
|
||||
writeMapFile2<ELFT>(OS, OutputSections);
|
||||
return;
|
||||
}
|
||||
|
||||
// Collect symbol info that we want to print out.
|
||||
std::vector<DefinedRegular *> Syms = getSymbols<ELFT>();
|
||||
SymbolMapTy SectionSyms = getSectionSyms<ELFT>(Syms);
|
||||
DenseMap<DefinedRegular *, std::string> SymStr = getSymbolStrings<ELFT>(Syms);
|
||||
|
||||
// Print out the header line.
|
||||
int W = ELFT::Is64Bits ? 16 : 8;
|
||||
OS << left_justify("Address", W) << ' ' << left_justify("Size", W)
|
||||
<< " Align Out In Symbol\n";
|
||||
|
||||
// Print out file contents.
|
||||
for (OutputSection *OSec : OutputSections) {
|
||||
writeHeader<ELFT>(OS, OSec->Addr, OSec->Size, OSec->Alignment);
|
||||
OS << OSec->Name << '\n';
|
||||
|
||||
// Dump symbols for each input section.
|
||||
for (InputSection *IS : OSec->Sections) {
|
||||
writeHeader<ELFT>(OS, OSec->Addr + IS->OutSecOff, IS->getSize(),
|
||||
IS->Alignment);
|
||||
OS << indent(1) << toString(IS) << '\n';
|
||||
for (DefinedRegular *Sym : SectionSyms[IS])
|
||||
OS << SymStr[Sym] << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template void elf::writeMapFile<ELF32LE>(ArrayRef<OutputSection *>);
|
||||
|
@ -344,6 +344,26 @@ def end_group_paren: Flag<["-"], ")">;
|
||||
def start_group: F<"start-group">;
|
||||
def start_group_paren: Flag<["-"], "(">;
|
||||
|
||||
// LTO-related options.
|
||||
def lto_aa_pipeline: J<"lto-aa-pipeline=">,
|
||||
HelpText<"AA pipeline to run during LTO. Used in conjunction with -lto-newpm-passes">;
|
||||
def lto_newpm_passes: J<"lto-newpm-passes=">,
|
||||
HelpText<"Passes to run during LTO">;
|
||||
def lto_partitions: J<"lto-partitions=">,
|
||||
HelpText<"Number of LTO codegen partitions">;
|
||||
def disable_verify: F<"disable-verify">;
|
||||
def mllvm: S<"mllvm">;
|
||||
def opt_remarks_filename: Separate<["--"], "opt-remarks-filename">,
|
||||
HelpText<"YAML output file for optimization remarks">;
|
||||
def opt_remarks_with_hotness: Flag<["--"], "opt-remarks-with-hotness">,
|
||||
HelpText<"Include hotness informations in the optimization remarks file">;
|
||||
def save_temps: F<"save-temps">;
|
||||
def thinlto_cache_dir: J<"thinlto-cache-dir=">,
|
||||
HelpText<"Path to ThinLTO cached object file directory">;
|
||||
def thinlto_cache_policy: S<"thinlto-cache-policy">,
|
||||
HelpText<"Pruning policy for the ThinLTO cache">;
|
||||
def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">;
|
||||
|
||||
// Ignore LTO plugin-related options.
|
||||
// clang -flto passes -plugin and -plugin-opt to the linker. This is required
|
||||
// for ld.gold and ld.bfd to get LTO working. But it's not for lld which doesn't
|
||||
@ -365,6 +385,7 @@ def no_add_needed: F<"no-add-needed">;
|
||||
def no_allow_shlib_undefined: F<"no-allow-shlib-undefined">;
|
||||
def no_copy_dt_needed_entries: F<"no-copy-dt-needed-entries">,
|
||||
Alias<no_add_needed>;
|
||||
def no_keep_memory: F<"no-keep-memory">;
|
||||
def no_mmap_output_file: F<"no-mmap-output-file">;
|
||||
def no_warn_common: F<"no-warn-common">;
|
||||
def no_warn_mismatch: F<"no-warn-mismatch">;
|
||||
@ -382,23 +403,3 @@ def Qy : F<"Qy">;
|
||||
// Aliases for ignored options
|
||||
def alias_version_script_version_script: J<"version-script=">,
|
||||
Alias<version_script>;
|
||||
|
||||
// LTO-related options.
|
||||
def lto_aa_pipeline: J<"lto-aa-pipeline=">,
|
||||
HelpText<"AA pipeline to run during LTO. Used in conjunction with -lto-newpm-passes">;
|
||||
def lto_newpm_passes: J<"lto-newpm-passes=">,
|
||||
HelpText<"Passes to run during LTO">;
|
||||
def lto_partitions: J<"lto-partitions=">,
|
||||
HelpText<"Number of LTO codegen partitions">;
|
||||
def disable_verify: F<"disable-verify">;
|
||||
def mllvm: S<"mllvm">;
|
||||
def opt_remarks_filename: Separate<["--"], "opt-remarks-filename">,
|
||||
HelpText<"YAML output file for optimization remarks">;
|
||||
def opt_remarks_with_hotness: Flag<["--"], "opt-remarks-with-hotness">,
|
||||
HelpText<"Include hotness informations in the optimization remarks file">;
|
||||
def save_temps: F<"save-temps">;
|
||||
def thinlto_cache_dir: J<"thinlto-cache-dir=">,
|
||||
HelpText<"Path to ThinLTO cached object file directory">;
|
||||
def thinlto_cache_policy: S<"thinlto-cache-policy">,
|
||||
HelpText<"Pruning policy for the ThinLTO cache">;
|
||||
def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">;
|
||||
|
@ -395,14 +395,20 @@ static void reportDiscarded(InputSectionBase *IS) {
|
||||
|
||||
void OutputSectionFactory::addInputSec(InputSectionBase *IS,
|
||||
StringRef OutsecName) {
|
||||
SectionKey Key = createKey(IS, OutsecName);
|
||||
OutputSection *&Sec = Map[Key];
|
||||
return addInputSec(IS, OutsecName, Sec);
|
||||
}
|
||||
|
||||
void OutputSectionFactory::addInputSec(InputSectionBase *IS,
|
||||
StringRef OutsecName,
|
||||
OutputSection *&Sec) {
|
||||
if (!IS->Live) {
|
||||
reportDiscarded(IS);
|
||||
return;
|
||||
}
|
||||
|
||||
SectionKey Key = createKey(IS, OutsecName);
|
||||
uint64_t Flags = getOutFlags(IS);
|
||||
OutputSection *&Sec = Map[Key];
|
||||
if (Sec) {
|
||||
if (getIncompatibleFlags(Sec->Flags) != getIncompatibleFlags(IS->Flags))
|
||||
error("incompatible section flags for " + Sec->Name +
|
||||
@ -418,7 +424,7 @@ void OutputSectionFactory::addInputSec(InputSectionBase *IS,
|
||||
}
|
||||
Sec->Flags |= Flags;
|
||||
} else {
|
||||
Sec = make<OutputSection>(Key.Name, IS->Type, Flags);
|
||||
Sec = make<OutputSection>(OutsecName, IS->Type, Flags);
|
||||
OutputSections.push_back(Sec);
|
||||
}
|
||||
|
||||
|
@ -141,6 +141,8 @@ public:
|
||||
~OutputSectionFactory();
|
||||
|
||||
void addInputSec(InputSectionBase *IS, StringRef OutsecName);
|
||||
void addInputSec(InputSectionBase *IS, StringRef OutsecName,
|
||||
OutputSection *&Sec);
|
||||
|
||||
private:
|
||||
llvm::SmallDenseMap<SectionKey, OutputSection *> Map;
|
||||
|
@ -76,6 +76,10 @@ llvm::Optional<std::string> demangle(StringRef Name);
|
||||
inline StringRef toStringRef(ArrayRef<uint8_t> Arr) {
|
||||
return {(const char *)Arr.data(), Arr.size()};
|
||||
}
|
||||
|
||||
inline ArrayRef<uint8_t> toArrayRef(StringRef S) {
|
||||
return {(const uint8_t *)S.data(), S.size()};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,9 @@ template <class ELFT> static bool isCompatible(InputFile *F) {
|
||||
|
||||
// Add symbols in File to the symbol table.
|
||||
template <class ELFT> void SymbolTable<ELFT>::addFile(InputFile *File) {
|
||||
if (!Config->FirstElf && isa<ELFFileBase<ELFT>>(File))
|
||||
Config->FirstElf = File;
|
||||
|
||||
if (!isCompatible<ELFT>(File))
|
||||
return;
|
||||
|
||||
|
@ -97,7 +97,7 @@ static ArrayRef<uint8_t> getVersion() {
|
||||
|
||||
// Creates a .comment section containing LLD version info.
|
||||
// With this feature, you can identify LLD-generated binaries easily
|
||||
// by "objdump -s -j .comment <file>".
|
||||
// by "readelf --string-dump .comment <file>".
|
||||
// The returned object is a mergeable string section.
|
||||
template <class ELFT> MergeInputSection *elf::createCommentSection() {
|
||||
typename ELFT::Shdr Hdr = {};
|
||||
@ -541,6 +541,13 @@ template <class ELFT> void EhFrameSection<ELFT>::finalizeContents() {
|
||||
Off += alignTo(Fde->size(), Config->Wordsize);
|
||||
}
|
||||
}
|
||||
|
||||
// The LSB standard does not allow a .eh_frame section with zero
|
||||
// Call Frame Information records. Therefore add a CIE record length
|
||||
// 0 as a terminator if this .eh_frame section is empty.
|
||||
if (Off == 0)
|
||||
Off = 4;
|
||||
|
||||
this->Size = Off;
|
||||
}
|
||||
|
||||
@ -1022,9 +1029,9 @@ template <class ELFT> void DynamicSection<ELFT>::addEntries() {
|
||||
// fixed early.
|
||||
for (StringRef S : Config->AuxiliaryList)
|
||||
add({DT_AUXILIARY, In<ELFT>::DynStrTab->addString(S)});
|
||||
if (!Config->RPath.empty())
|
||||
if (!Config->Rpath.empty())
|
||||
add({Config->EnableNewDtags ? DT_RUNPATH : DT_RPATH,
|
||||
In<ELFT>::DynStrTab->addString(Config->RPath)});
|
||||
In<ELFT>::DynStrTab->addString(Config->Rpath)});
|
||||
for (SharedFile<ELFT> *F : Symtab<ELFT>::X->getSharedFiles())
|
||||
if (F->isNeeded())
|
||||
add({DT_NEEDED, In<ELFT>::DynStrTab->addString(F->SoName)});
|
||||
|
@ -101,7 +101,7 @@ StringRef elf::getOutputSectionName(StringRef Name) {
|
||||
for (StringRef V : {".rel.", ".rela."}) {
|
||||
if (Name.startswith(V)) {
|
||||
StringRef Inner = getOutputSectionName(Name.substr(V.size() - 1));
|
||||
return Saver.save(Twine(V.drop_back()) + Inner);
|
||||
return Saver.save(V.drop_back() + Inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -123,7 +123,7 @@ StringRef elf::getOutputSectionName(StringRef Name) {
|
||||
// ".zdebug_" is a prefix for ZLIB-compressed sections.
|
||||
// Because we decompressed input sections, we want to remove 'z'.
|
||||
if (Name.startswith(".zdebug_"))
|
||||
return Saver.save(Twine(".") + Name.substr(2));
|
||||
return Saver.save("." + Name.substr(2));
|
||||
return Name;
|
||||
}
|
||||
|
||||
@ -254,6 +254,7 @@ template <class ELFT> void Writer<ELFT>::run() {
|
||||
fixSectionAlignments();
|
||||
Script->fabricateDefaultCommands(Config->MaxPageSize);
|
||||
}
|
||||
Script->synchronize();
|
||||
Script->assignAddresses(Phdrs);
|
||||
|
||||
// Remove empty PT_LOAD to avoid causing the dynamic linker to try to mmap a
|
||||
@ -1080,6 +1081,7 @@ static void removeUnusedSyntheticSections(std::vector<OutputSection *> &V) {
|
||||
|
||||
SS->OutSec->Sections.erase(std::find(SS->OutSec->Sections.begin(),
|
||||
SS->OutSec->Sections.end(), SS));
|
||||
SS->Live = false;
|
||||
// If there are no other sections in the output section, remove it from the
|
||||
// output.
|
||||
if (SS->OutSec->Sections.empty())
|
||||
|
@ -135,7 +135,7 @@ LDFLAGS when building your programs.
|
||||
|
||||
LLD leaves its name and version number to a ``.comment`` section in an
|
||||
output. If you are in doubt whether you are successfully using LLD or
|
||||
not, run ``objdump -s -j .comment <output-file>`` and examine the
|
||||
not, run ``readelf --string-dump .comment <output-file>`` and examine the
|
||||
output. If the string "Linker: LLD" is included in the output, you are
|
||||
using LLD.
|
||||
|
||||
|
21
test/COFF/Inputs/constant-import.s
Normal file
21
test/COFF/Inputs/constant-import.s
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
.def __DllMainCRTStartup@12
|
||||
.type 32
|
||||
.scl 2
|
||||
.endef
|
||||
.global __DllMainCRTStartup@12
|
||||
__DllMainCRTStartup@12:
|
||||
ret
|
||||
|
||||
.data
|
||||
.def _Data
|
||||
.type 0
|
||||
.scl 2
|
||||
.endef
|
||||
.global _Data
|
||||
_Data:
|
||||
.long ___CFConstantStringClassReference
|
||||
|
||||
.section .drectve
|
||||
.ascii " -export:_Data"
|
||||
|
5
test/COFF/constant.test
Normal file
5
test/COFF/constant.test
Normal file
@ -0,0 +1,5 @@
|
||||
RUN: mkdir -p %t
|
||||
RUN: llvm-mc -triple i686-unknown-windows-msvc -filetype obj -o %t/import.o %S/Inputs/constant-import.s
|
||||
RUN: llc -mtriple i686-unknown-windows-msvc -filetype obj -o %t/export.o %S/Inputs/constant-export.ll
|
||||
RUN: lld-link -machine:x86 -dll -out:%t/export.dll %t/export.o -entry:__CFConstantStringClassReference
|
||||
RUN: lld-link -machine:x86 -dll -out:%t/import.dll %t/import.o %t/export.lib
|
61
test/COFF/icf-data.test
Normal file
61
test/COFF/icf-data.test
Normal file
@ -0,0 +1,61 @@
|
||||
# RUN: yaml2obj < %s > %t.obj
|
||||
# RUN: lld-link /entry:foo /out:%t.exe /subsystem:console /include:bar \
|
||||
# RUN: /verbose %t.obj > %t.log 2>&1
|
||||
# RUN: FileCheck %s < %t.log
|
||||
|
||||
# CHECK-NOT: Removed foo
|
||||
# CHECK-NOT: Removed bar
|
||||
|
||||
--- !COFF
|
||||
header:
|
||||
Machine: IMAGE_FILE_MACHINE_AMD64
|
||||
Characteristics: []
|
||||
sections:
|
||||
- Name: '.text$mn'
|
||||
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ ]
|
||||
Alignment: 16
|
||||
SectionData: 4883EC28E8000000004883C428C3
|
||||
- Name: '.text$mn'
|
||||
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ ]
|
||||
Alignment: 16
|
||||
SectionData: 4883EC28E8000000004883C428C3
|
||||
symbols:
|
||||
- Name: '.text$mn'
|
||||
Value: 0
|
||||
SectionNumber: 1
|
||||
SimpleType: IMAGE_SYM_TYPE_NULL
|
||||
ComplexType: IMAGE_SYM_DTYPE_NULL
|
||||
StorageClass: IMAGE_SYM_CLASS_STATIC
|
||||
SectionDefinition:
|
||||
Length: 14
|
||||
NumberOfRelocations: 0
|
||||
NumberOfLinenumbers: 0
|
||||
CheckSum: 1682752513
|
||||
Number: 0
|
||||
Selection: IMAGE_COMDAT_SELECT_NODUPLICATES
|
||||
- Name: '.text$mn'
|
||||
Value: 0
|
||||
SectionNumber: 2
|
||||
SimpleType: IMAGE_SYM_TYPE_NULL
|
||||
ComplexType: IMAGE_SYM_DTYPE_NULL
|
||||
StorageClass: IMAGE_SYM_CLASS_STATIC
|
||||
SectionDefinition:
|
||||
Length: 14
|
||||
NumberOfRelocations: 0
|
||||
NumberOfLinenumbers: 0
|
||||
CheckSum: 1682752513
|
||||
Number: 0
|
||||
Selection: IMAGE_COMDAT_SELECT_NODUPLICATES
|
||||
- Name: foo
|
||||
Value: 0
|
||||
SectionNumber: 1
|
||||
SimpleType: IMAGE_SYM_TYPE_NULL
|
||||
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
|
||||
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
|
||||
- Name: bar
|
||||
Value: 0
|
||||
SectionNumber: 2
|
||||
SimpleType: IMAGE_SYM_TYPE_NULL
|
||||
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
|
||||
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
|
||||
...
|
@ -1,11 +1,10 @@
|
||||
# RUN: yaml2obj < %p/Inputs/ret42.yaml > %t.obj
|
||||
# RUN: lld-link /out:%t.exe /entry:main /lldmap:%T/foo.map %t.obj
|
||||
# RUN: FileCheck %s < %T/foo.map
|
||||
# RUN: FileCheck -strict-whitespace %s < %T/foo.map
|
||||
# RUN: lld-link /out:%T/bar.exe /entry:main /lldmap %t.obj
|
||||
# RUN: FileCheck %s < %T/bar.map
|
||||
# RUN: FileCheck -strict-whitespace %s < %T/bar.map
|
||||
|
||||
# CHECK: Address Size Align Out In File Symbol
|
||||
# CHECK: Address Size Align Out In Symbol
|
||||
# CHECK-NEXT: 00001000 00000006 4096 .text
|
||||
# CHECK-NEXT: 00001000 00000006 16 .text$mn
|
||||
# CHECK-NEXT: 00001000 00000006 16 {{.*}}lldmap.test.tmp.obj
|
||||
# CHECK-NEXT: 00001000 00000006 0 main
|
||||
# CHECK-NEXT: 00001000 00000006 16 {{.*}}lldmap.test.tmp.obj:(.text$mn)
|
||||
# CHECK-NEXT: 00001000 00000000 0 main
|
||||
|
2
test/ELF/Inputs/eh-frame-end.s
Normal file
2
test/ELF/Inputs/eh-frame-end.s
Normal file
@ -0,0 +1,2 @@
|
||||
.section ".eh_frame", "a", @progbits
|
||||
.long 0
|
@ -15,4 +15,4 @@ foo = 0x123
|
||||
|
||||
// DUP: duplicate symbol: foo
|
||||
// DUP-NEXT: >>> defined in {{.*}}.o
|
||||
// DUP-NEXT: >>> defined in (internal)
|
||||
// DUP-NEXT: >>> defined in <internal>
|
||||
|
10
test/ELF/debug-gnu-pubnames.s
Normal file
10
test/ELF/debug-gnu-pubnames.s
Normal file
@ -0,0 +1,10 @@
|
||||
# REQUIRES: x86
|
||||
# RUN: ld.lld -e main %p/Inputs/gdb-index-a.elf %p/Inputs/gdb-index-b.elf -o %t1.exe
|
||||
# RUN: llvm-readobj -sections %t1.exe | FileCheck -check-prefix=CHECK1 %s
|
||||
# CHECK1: Name: .debug_gnu_pubnames
|
||||
# CHECK1: Name: .debug_gnu_pubtypes
|
||||
|
||||
# RUN: ld.lld -gdb-index -e main %p/Inputs/gdb-index-a.elf %p/Inputs/gdb-index-b.elf -o %t2.exe
|
||||
# RUN: llvm-readobj -sections %t2.exe | FileCheck -check-prefix=CHECK2 %s
|
||||
# CHECK2-NOT: Name: .debug_gnu_pubnames
|
||||
# CHECK2-NOT: Name: .debug_gnu_pubtypes
|
@ -8,6 +8,7 @@
|
||||
# RUN: chmod 100 %t.dir
|
||||
# RUN: cd %t.dir
|
||||
# RUN: ld.lld %t.o -o %t.exe
|
||||
# RUN: chmod 755 %t.dir
|
||||
|
||||
.globl _start
|
||||
_start:
|
||||
|
17
test/ELF/eh-frame-begin-end.s
Normal file
17
test/ELF/eh-frame-begin-end.s
Normal file
@ -0,0 +1,17 @@
|
||||
// REQUIRES: x86
|
||||
// RUN: llvm-mc -filetype=obj -triple=amd64-unknown-openbsd %s -o %t.o
|
||||
// RUN: llvm-mc -filetype=obj -triple=amd64-unknown-openbsd %p/Inputs/eh-frame-end.s -o %t2.o
|
||||
// RUN: ld.lld %t.o %t2.o -o %t
|
||||
// RUN: llvm-readobj -sections %t | FileCheck %s
|
||||
|
||||
// CHECK: Name: .eh_frame
|
||||
// CHECK-NEXT: Type: SHT_PROGBITS
|
||||
// CHECK-NEXT: Flags [
|
||||
// CHECK-NEXT: SHF_ALLOC
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: Address: 0x200120
|
||||
// CHECK-NEXT: Offset: 0x120
|
||||
// CHECK-NEXT: Size: 4
|
||||
|
||||
.section ".eh_frame", "a", @progbits
|
||||
__EH_FRAME_BEGIN__:
|
@ -34,8 +34,8 @@
|
||||
# CHECK-NEXT: 0: Offset = 0x0, Length = 0x34
|
||||
# CHECK-NEXT: 1: Offset = 0x34, Length = 0x34
|
||||
# CHECK: Address area offset = 0x38, has 2 entries:
|
||||
# CHECK-NEXT: Low address = 0x201000, High address = 0x20100b, CU index = 0
|
||||
# CHECK-NEXT: Low address = 0x20100b, High address = 0x201016, CU index = 1
|
||||
# CHECK-NEXT: Low/High address = [0x201000, 0x20100b) (Size: 0xb), CU id = 0
|
||||
# CHECK-NEXT: Low/High address = [0x20100b, 0x201016) (Size: 0xb), CU id = 1
|
||||
# CHECK: Symbol table offset = 0x60, size = 1024, filled slots:
|
||||
# CHECK-NEXT: 489: Name offset = 0x1d, CU vector offset = 0x0
|
||||
# CHECK-NEXT: String name: main, CU vector index: 0
|
||||
|
33
test/ELF/linkerscript/section-metadata.s
Normal file
33
test/ELF/linkerscript/section-metadata.s
Normal file
@ -0,0 +1,33 @@
|
||||
# REQUIRES: x86
|
||||
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
|
||||
|
||||
# RUN: echo "SECTIONS { .text : { *(.text.bar) *(.text.foo) } }" > %t.script
|
||||
# RUN: ld.lld -o %t --script %t.script %t.o
|
||||
# RUN: llvm-objdump -s %t | FileCheck %s
|
||||
|
||||
# RUN: echo "SECTIONS { .text : { *(.text.foo) *(.text.bar) } }" > %t.script
|
||||
# RUN: ld.lld -o %t --script %t.script %t.o
|
||||
# RUN: llvm-objdump -s %t | FileCheck --check-prefix=INV %s
|
||||
|
||||
|
||||
# CHECK: Contents of section .text:
|
||||
# CHECK-NEXT: 02000000 00000000 01000000 00000000
|
||||
# CHECK: Contents of section .rodata:
|
||||
# CHECK-NEXT: 02000000 00000000 01000000 00000000
|
||||
|
||||
# INV: Contents of section .text:
|
||||
# INV-NEXT: 01000000 00000000 02000000 00000000
|
||||
# INV: Contents of section .rodata:
|
||||
# INV-NEXT: 01000000 00000000 02000000 00000000
|
||||
|
||||
.global _start
|
||||
_start:
|
||||
|
||||
.section .text.bar,"a",@progbits
|
||||
.quad 2
|
||||
.section .text.foo,"a",@progbits
|
||||
.quad 1
|
||||
.section .rodata.foo,"ao",@progbits,.text.foo
|
||||
.quad 1
|
||||
.section .rodata.bar,"ao",@progbits,.text.bar
|
||||
.quad 2
|
@ -86,7 +86,8 @@
|
||||
|
||||
# Idx Name Size
|
||||
# SEC-MULTI: 1 .text 0000000e {{[0-9a-f]*}} TEXT DATA
|
||||
# SEC-MULTI-NEXT: .data 00000023 {{[0-9a-f]*}} DATA
|
||||
# SEC-MULTI-NEXT: .data 00000020 {{[0-9a-f]*}} DATA
|
||||
# SEC-MULTI-NEXT: .data 00000003 {{[0-9a-f]*}} DATA
|
||||
# SEC-MULTI-NEXT: .bss 00000002 {{[0-9a-f]*}} BSS
|
||||
# SEC-MULTI-NEXT: .comment 00000008 {{[0-9a-f]*}}
|
||||
# SEC-MULTI-NEXT: .symtab 00000030 {{[0-9a-f]*}}
|
||||
|
@ -6,10 +6,10 @@
|
||||
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/map-file4.s -o %t4.o
|
||||
// RUN: rm -f %t4.a
|
||||
// RUN: llvm-ar rc %t4.a %t4.o
|
||||
// RUN: ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -M | FileCheck %s
|
||||
// RUN: ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -print-map | FileCheck %s
|
||||
// RUN: ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -M | FileCheck -strict-whitespace %s
|
||||
// RUN: ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -print-map | FileCheck -strict-whitespace %s
|
||||
// RUN: ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -Map=%t.map
|
||||
// RUN: FileCheck %s < %t.map
|
||||
// RUN: FileCheck -strict-whitespace %s < %t.map
|
||||
|
||||
.global _start
|
||||
_start:
|
||||
@ -26,36 +26,33 @@ bar:
|
||||
local:
|
||||
.comm common,4,16
|
||||
|
||||
// CHECK: Address Size Align Out In File Symbol
|
||||
// CHECK: Address Size Align Out In Symbol
|
||||
// CHECK-NEXT: 0000000000200158 0000000000000030 8 .eh_frame
|
||||
// CHECK-NEXT: 0000000000200158 0000000000000030 8 .eh_frame
|
||||
// CHECK-NEXT: 0000000000200158 0000000000000030 8 <internal>:(.eh_frame)
|
||||
// CHECK-NEXT: 0000000000201000 0000000000000015 4 .text
|
||||
// CHECK-NEXT: 0000000000201000 000000000000000e 4 .text
|
||||
// CHECK-NEXT: 0000000000201000 000000000000000e 4 {{.*}}{{/|\\}}map-file.s.tmp1.o
|
||||
// CHECK-NEXT: 000000000020100e 0000000000000000 0 local
|
||||
// CHECK-NEXT: 0000000000201005 0000000000000000 0 f(int)
|
||||
// CHECK-NEXT: 0000000000201000 0000000000000000 0 _start
|
||||
// CHECK-NEXT: 0000000000201010 0000000000000002 4 {{.*}}{{/|\\}}map-file.s.tmp2.o
|
||||
// CHECK-NEXT: 0000000000201010 0000000000000000 0 foo
|
||||
// CHECK-NEXT: 0000000000201011 0000000000000000 0 bar
|
||||
// CHECK-NEXT: 0000000000201012 0000000000000000 1 .text.zed
|
||||
// CHECK-NEXT: 0000000000201012 0000000000000000 1 {{.*}}{{/|\\}}map-file.s.tmp2.o
|
||||
// CHECK-NEXT: 0000000000201012 0000000000000000 0 zed
|
||||
// CHECK-NEXT: 0000000000201014 0000000000000000 4 .text
|
||||
// CHECK-NEXT: 0000000000201014 0000000000000000 4 {{.*}}{{/|\\}}map-file.s.tmp3.o
|
||||
// CHECK-NEXT: 0000000000201014 0000000000000000 0 bah
|
||||
// CHECK-NEXT: 0000000000201014 0000000000000001 4 {{.*}}{{/|\\}}map-file.s.tmp4.a(map-file.s.tmp4.o)
|
||||
// CHECK-NEXT: 0000000000201014 0000000000000000 0 baz
|
||||
// CHECK-NEXT: 0000000000201000 000000000000000e 4 {{.*}}{{/|\\}}map-file.s.tmp1.o:(.text)
|
||||
// CHECK-NEXT: 0000000000201000 0000000000000000 0 _start
|
||||
// CHECK-NEXT: 0000000000201005 0000000000000000 0 f(int)
|
||||
// CHECK-NEXT: 000000000020100e 0000000000000000 0 local
|
||||
// CHECK-NEXT: 0000000000201010 0000000000000002 4 {{.*}}{{/|\\}}map-file.s.tmp2.o:(.text)
|
||||
// CHECK-NEXT: 0000000000201010 0000000000000000 0 foo
|
||||
// CHECK-NEXT: 0000000000201011 0000000000000000 0 bar
|
||||
// CHECK-NEXT: 0000000000201012 0000000000000000 1 {{.*}}{{/|\\}}map-file.s.tmp2.o:(.text.zed)
|
||||
// CHECK-NEXT: 0000000000201012 0000000000000000 0 zed
|
||||
// CHECK-NEXT: 0000000000201014 0000000000000000 4 {{.*}}{{/|\\}}map-file.s.tmp3.o:(.text)
|
||||
// CHECK-NEXT: 0000000000201014 0000000000000000 0 bah
|
||||
// CHECK-NEXT: 0000000000201014 0000000000000001 4 {{.*}}{{/|\\}}map-file.s.tmp4.a(map-file.s.tmp4.o):(.text)
|
||||
// CHECK-NEXT: 0000000000201014 0000000000000000 0 baz
|
||||
// CHECK-NEXT: 0000000000202000 0000000000000004 16 .bss
|
||||
// CHECK-NEXT: 0000000000202000 0000000000000004 16 COMMON
|
||||
// CHECK-NEXT: 0000000000202000 0000000000000004 16 <internal>:(COMMON)
|
||||
// CHECK-NEXT: 0000000000000000 0000000000000008 1 .comment
|
||||
// CHECK-NEXT: 0000000000000000 0000000000000008 1 .comment
|
||||
// CHECK-NEXT: 0000000000000000 0000000000000008 1 <internal>:(.comment)
|
||||
// CHECK-NEXT: 0000000000000000 00000000000000f0 8 .symtab
|
||||
// CHECK-NEXT: 0000000000000000 00000000000000f0 8 .symtab
|
||||
// CHECK-NEXT: 0000000000000000 00000000000000f0 8 <internal>:(.symtab)
|
||||
// CHECK-NEXT: 0000000000000000 0000000000000039 1 .shstrtab
|
||||
// CHECK-NEXT: 0000000000000000 0000000000000039 1 .shstrtab
|
||||
// CHECK-NEXT: 0000000000000000 0000000000000039 1 <internal>:(.shstrtab)
|
||||
// CHECK-NEXT: 0000000000000000 000000000000002f 1 .strtab
|
||||
// CHECK-NEXT: 0000000000000000 000000000000002f 1 .strtab
|
||||
// CHECK-NEXT: 0000000000000000 000000000000002f 1 <internal>:(.strtab)
|
||||
|
||||
// RUN: not ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -Map=/ 2>&1 \
|
||||
// RUN: | FileCheck -check-prefix=FAIL %s
|
||||
|
@ -1,7 +1,12 @@
|
||||
// REQUIRES: x86
|
||||
// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=x86_64-pc-linux
|
||||
// RUN: not ld.lld %t.o -o %t 2>&1 | FileCheck %s
|
||||
// CHECK: relocations pointing to SHF_MERGE are not supported
|
||||
// RUN: ld.lld %t.o -o %t -shared
|
||||
// RUN: llvm-objdump -section-headers %t | FileCheck %s
|
||||
|
||||
.section .foo,"aM",@progbits,4
|
||||
.long bar
|
||||
// Test that we accept this by just not merging the section.
|
||||
// CHECK: .foo 00000008
|
||||
|
||||
bar:
|
||||
.section .foo,"aM",@progbits,8
|
||||
.long bar - .
|
||||
.long bar - .
|
||||
|
Loading…
x
Reference in New Issue
Block a user