aboutsummaryrefslogtreecommitdiffstats
path: root/COFF
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2017-01-02 19:19:15 +0000
committerDimitry Andric <dim@FreeBSD.org>2017-01-02 19:19:15 +0000
commitd93e1dfac8711cfed1a9d9cd1876a788b83945cd (patch)
tree5896fa6c02a262a6148b215487e545d937de58b7 /COFF
parent8d43286d630f9224de07809ea253e83ebb9cdee6 (diff)
downloadsrc-d93e1dfac8711cfed1a9d9cd1876a788b83945cd.tar.gz
src-d93e1dfac8711cfed1a9d9cd1876a788b83945cd.zip
Vendor import of lld trunk r290819:vendor/lld/lld-trunk-r290819
Notes
Notes: svn path=/vendor/lld/dist/; revision=311125 svn path=/vendor/lld/lld-trunk-r290819/; revision=311126; tag=vendor/lld/lld-trunk-r290819
Diffstat (limited to 'COFF')
-rw-r--r--COFF/CMakeLists.txt18
-rw-r--r--COFF/Chunks.cpp32
-rw-r--r--COFF/Chunks.h7
-rw-r--r--COFF/Config.h28
-rw-r--r--COFF/DLL.cpp2
-rw-r--r--COFF/Driver.cpp428
-rw-r--r--COFF/Driver.h38
-rw-r--r--COFF/DriverUtils.cpp194
-rw-r--r--COFF/Error.cpp21
-rw-r--r--COFF/Error.h17
-rw-r--r--COFF/ICF.cpp278
-rw-r--r--COFF/InputFiles.cpp179
-rw-r--r--COFF/InputFiles.h68
-rw-r--r--COFF/Librarian.cpp39
-rw-r--r--COFF/MarkLive.cpp6
-rw-r--r--COFF/Memory.h52
-rw-r--r--COFF/ModuleDef.cpp12
-rw-r--r--COFF/Options.td8
-rw-r--r--COFF/PDB.cpp214
-rw-r--r--COFF/PDB.h25
-rw-r--r--COFF/Strings.cpp30
-rw-r--r--COFF/Strings.h23
-rw-r--r--COFF/SymbolTable.cpp494
-rw-r--r--COFF/SymbolTable.h66
-rw-r--r--COFF/Symbols.cpp178
-rw-r--r--COFF/Symbols.h142
-rw-r--r--COFF/Writer.cpp198
-rw-r--r--COFF/Writer.h4
28 files changed, 1694 insertions, 1107 deletions
diff --git a/COFF/CMakeLists.txt b/COFF/CMakeLists.txt
index ad5b6fda1693..70a33b9fdd81 100644
--- a/COFF/CMakeLists.txt
+++ b/COFF/CMakeLists.txt
@@ -2,6 +2,10 @@ set(LLVM_TARGET_DEFINITIONS Options.td)
tablegen(LLVM Options.inc -gen-opt-parser-defs)
add_public_tablegen_target(COFFOptionsTableGen)
+if(NOT LLD_BUILT_STANDALONE)
+ set(tablegen_deps intrinsics_gen)
+endif()
+
add_lld_library(lldCOFF
Chunks.cpp
DLL.cpp
@@ -14,6 +18,7 @@ add_lld_library(lldCOFF
MarkLive.cpp
ModuleDef.cpp
PDB.cpp
+ Strings.cpp
SymbolTable.cpp
Symbols.cpp
Writer.cpp
@@ -21,6 +26,9 @@ add_lld_library(lldCOFF
LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
Core
+ DebugInfoCodeView
+ DebugInfoMSF
+ DebugInfoPDB
LTO
LibDriver
Object
@@ -30,7 +38,11 @@ add_lld_library(lldCOFF
Option
Support
- LINK_LIBS ${PTHREAD_LIB}
- )
+ LINK_LIBS
+ lldCore
+ ${PTHREAD_LIB}
-add_dependencies(lldCOFF COFFOptionsTableGen intrinsics_gen)
+ DEPENDS
+ COFFOptionsTableGen
+ ${tablegen_deps}
+ )
diff --git a/COFF/Chunks.cpp b/COFF/Chunks.cpp
index 1c1b18176aa2..7f0dfa92ec10 100644
--- a/COFF/Chunks.cpp
+++ b/COFF/Chunks.cpp
@@ -28,7 +28,7 @@ namespace lld {
namespace coff {
SectionChunk::SectionChunk(ObjectFile *F, const coff_section *H)
- : Chunk(SectionKind), Repl(this), File(F), Header(H),
+ : Chunk(SectionKind), Repl(this), Header(H), File(F),
Relocs(File->getCOFFObj()->getRelocations(Header)),
NumRelocs(std::distance(Relocs.begin(), Relocs.end())) {
// Initialize SectionName.
@@ -81,11 +81,23 @@ void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, Defined *Sym,
}
static void applyMOV(uint8_t *Off, uint16_t V) {
- or16(Off, ((V & 0x800) >> 1) | ((V >> 12) & 0xf));
- or16(Off + 2, ((V & 0x700) << 4) | (V & 0xff));
+ write16le(Off, (read16le(Off) & 0xfbf0) | ((V & 0x800) >> 1) | ((V >> 12) & 0xf));
+ write16le(Off + 2, (read16le(Off + 2) & 0x8f00) | ((V & 0x700) << 4) | (V & 0xff));
+}
+
+static uint16_t readMOV(uint8_t *Off) {
+ uint16_t Opcode1 = read16le(Off);
+ uint16_t Opcode2 = read16le(Off + 2);
+ uint16_t Imm = (Opcode2 & 0x00ff) | ((Opcode2 >> 4) & 0x0700);
+ Imm |= ((Opcode1 << 1) & 0x0800) | ((Opcode1 & 0x000f) << 12);
+ return Imm;
}
static void applyMOV32T(uint8_t *Off, uint32_t V) {
+ uint16_t ImmW = readMOV(Off); // read MOVW operand
+ uint16_t ImmT = readMOV(Off + 4); // read MOVT operand
+ uint32_t Imm = ImmW | (ImmT << 16);
+ V += Imm; // add the immediate offset
applyMOV(Off, V); // set MOVW operand
applyMOV(Off + 4, V >> 16); // set MOVT operand
}
@@ -99,11 +111,14 @@ static void applyBranch20T(uint8_t *Off, int32_t V) {
}
static void applyBranch24T(uint8_t *Off, int32_t V) {
+ if (!isInt<25>(V))
+ fatal("relocation out of range");
uint32_t S = V < 0 ? 1 : 0;
uint32_t J1 = ((~V >> 23) & 1) ^ S;
uint32_t J2 = ((~V >> 22) & 1) ^ S;
or16(Off, (S << 10) | ((V >> 12) & 0x3ff));
- or16(Off + 2, (J1 << 13) | (J2 << 11) | ((V >> 1) & 0x7ff));
+ // Clear out the J1 and J2 bits which may be set.
+ write16le(Off + 2, (read16le(Off + 2) & 0xd000) | (J1 << 13) | (J2 << 11) | ((V >> 1) & 0x7ff));
}
void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, Defined *Sym,
@@ -119,6 +134,7 @@ void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, Defined *Sym,
case IMAGE_REL_ARM_BRANCH20T: applyBranch20T(Off, S - P - 4); break;
case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(Off, S - P - 4); break;
case IMAGE_REL_ARM_BLX23T: applyBranch24T(Off, S - P - 4); break;
+ case IMAGE_REL_ARM_SECREL: add32(Off, Sym->getSecrel()); break;
default:
fatal("unsupported relocation type");
}
@@ -134,7 +150,7 @@ void SectionChunk::writeTo(uint8_t *Buf) const {
// Apply relocations.
for (const coff_relocation &Rel : Relocs) {
uint8_t *Off = Buf + OutputSectionOff + Rel.VirtualAddress;
- SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex)->repl();
+ SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex);
Defined *Sym = cast<Defined>(Body);
uint64_t P = RVA + Rel.VirtualAddress;
switch (Config->Machine) {
@@ -187,7 +203,7 @@ void SectionChunk::getBaserels(std::vector<Baserel> *Res) {
uint8_t Ty = getBaserelType(Rel);
if (Ty == IMAGE_REL_BASED_ABSOLUTE)
continue;
- SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex)->repl();
+ SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex);
if (isa<DefinedAbsolute>(Body))
continue;
Res->emplace_back(RVA + Rel.VirtualAddress, Ty);
@@ -210,7 +226,7 @@ void SectionChunk::printDiscardedMessage() const {
// Removed by dead-stripping. If it's removed by ICF, ICF already
// printed out the name, so don't repeat that here.
if (Sym && this == Repl)
- llvm::outs() << "Discarded " << Sym->getName() << "\n";
+ outs() << "Discarded " << Sym->getName() << "\n";
}
StringRef SectionChunk::getDebugName() {
@@ -233,7 +249,7 @@ void SectionChunk::replace(SectionChunk *Other) {
CommonChunk::CommonChunk(const COFFSymbolRef S) : Sym(S) {
// Common symbols are aligned on natural boundaries up to 32 bytes.
// This is what MSVC link.exe does.
- Align = std::min(uint64_t(32), NextPowerOf2(Sym.getValue()));
+ Align = std::min(uint64_t(32), PowerOf2Ceil(Sym.getValue()));
}
uint32_t CommonChunk::getPermissions() const {
diff --git a/COFF/Chunks.h b/COFF/Chunks.h
index cd0e2e69ef5d..59e36b84c9b0 100644
--- a/COFF/Chunks.h
+++ b/COFF/Chunks.h
@@ -17,7 +17,6 @@
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Object/COFF.h"
-#include <atomic>
#include <utility>
#include <vector>
@@ -29,7 +28,6 @@ using llvm::object::COFFSymbolRef;
using llvm::object::SectionRef;
using llvm::object::coff_relocation;
using llvm::object::coff_section;
-using llvm::sys::fs::file_magic;
class Baserel;
class Defined;
@@ -187,11 +185,12 @@ public:
// Auxiliary Format 5: Section Definitions. Used for ICF.
uint32_t Checksum = 0;
+ const coff_section *Header;
+
private:
// A file this chunk was created from.
ObjectFile *File;
- const coff_section *Header;
StringRef SectionName;
std::vector<SectionChunk *> AssocChildren;
llvm::iterator_range<const coff_relocation *> Relocs;
@@ -202,7 +201,7 @@ private:
// Used for ICF (Identical COMDAT Folding)
void replace(SectionChunk *Other);
- std::atomic<uint64_t> GroupID = { 0 };
+ uint32_t Color[2] = {0, 0};
// Sym points to a section symbol if this is a COMDAT chunk.
DefinedRegular *Sym = nullptr;
diff --git a/COFF/Config.h b/COFF/Config.h
index a5472e937fa1..0fa3338aa28c 100644
--- a/COFF/Config.h
+++ b/COFF/Config.h
@@ -26,7 +26,8 @@ using llvm::StringRef;
class DefinedAbsolute;
class DefinedRelative;
class StringChunk;
-class Undefined;
+struct Symbol;
+class SymbolBody;
// Short aliases.
static const auto AMD64 = llvm::COFF::IMAGE_FILE_MACHINE_AMD64;
@@ -37,7 +38,7 @@ static const auto I386 = llvm::COFF::IMAGE_FILE_MACHINE_I386;
struct Export {
StringRef Name; // N in /export:N or /export:E=N
StringRef ExtName; // E in /export:E=N
- Undefined *Sym = nullptr;
+ SymbolBody *Sym = nullptr;
uint16_t Ordinal = 0;
bool Noname = false;
bool Data = false;
@@ -61,6 +62,13 @@ struct Export {
}
};
+enum class DebugType {
+ None = 0x0,
+ CV = 0x1, /// CodeView
+ PData = 0x2, /// Procedure Data
+ Fixup = 0x4, /// Relocation Table
+};
+
// Global configuration.
struct Configuration {
enum ManifestKind { SideBySide, Embed, No };
@@ -69,7 +77,7 @@ struct Configuration {
llvm::COFF::MachineTypes Machine = IMAGE_FILE_MACHINE_UNKNOWN;
bool Verbose = false;
WindowsSubsystem Subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN;
- Undefined *Entry = nullptr;
+ SymbolBody *Entry = nullptr;
bool NoEntry = false;
std::string OutputFile;
bool DoGC = true;
@@ -78,9 +86,11 @@ struct Configuration {
bool Force = false;
bool Debug = false;
bool WriteSymtab = true;
+ unsigned DebugTypes = static_cast<unsigned>(DebugType::None);
+ StringRef PDBPath;
// Symbols in this set are considered as live by the garbage collector.
- std::set<Undefined *> GCRoot;
+ std::set<SymbolBody *> GCRoot;
std::set<StringRef> NoDefaultLibs;
bool NoDefaultLibAll = false;
@@ -91,11 +101,11 @@ struct Configuration {
std::vector<Export> Exports;
std::set<std::string> DelayLoads;
std::map<std::string, int> DLLOrder;
- Undefined *DelayLoadHelper = nullptr;
+ SymbolBody *DelayLoadHelper = nullptr;
// Used for SafeSEH.
- DefinedRelative *SEHTable = nullptr;
- DefinedAbsolute *SEHCount = nullptr;
+ Symbol *SEHTable = nullptr;
+ Symbol *SEHCount = nullptr;
// Used for /opt:lldlto=N
unsigned LTOOptLevel = 2;
@@ -141,6 +151,10 @@ struct Configuration {
bool TerminalServerAware = true;
bool LargeAddressAware = false;
bool HighEntropyVA = false;
+
+ // This is for debugging.
+ bool DebugPdb = false;
+ bool DumpPdb = false;
};
extern Configuration *Config;
diff --git a/COFF/DLL.cpp b/COFF/DLL.cpp
index 9ac370c11d59..f93dc5cde44c 100644
--- a/COFF/DLL.cpp
+++ b/COFF/DLL.cpp
@@ -324,7 +324,7 @@ public:
if (E.ForwardChunk) {
write32le(P, E.ForwardChunk->getRVA());
} else {
- write32le(P, cast<Defined>(E.Sym->repl())->getRVA());
+ write32le(P, cast<Defined>(E.Sym)->getRVA());
}
}
}
diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp
index bb6a60e4fc4c..dc3a00ba55ed 100644
--- a/COFF/Driver.cpp
+++ b/COFF/Driver.cpp
@@ -7,15 +7,17 @@
//
//===----------------------------------------------------------------------===//
-#include "Config.h"
#include "Driver.h"
+#include "Config.h"
#include "Error.h"
#include "InputFiles.h"
+#include "Memory.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "Writer.h"
#include "lld/Driver/Driver.h"
#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringSwitch.h"
#include "llvm/LibDriver/LibDriver.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
@@ -28,6 +30,13 @@
#include <algorithm>
#include <memory>
+#ifdef _MSC_VER
+// <future> depends on <eh.h> for __uncaught_exception.
+#include <eh.h>
+#endif
+
+#include <future>
+
using namespace llvm;
using namespace llvm::COFF;
using llvm::sys::Process;
@@ -41,11 +50,13 @@ namespace coff {
Configuration *Config;
LinkerDriver *Driver;
-bool link(llvm::ArrayRef<const char *> Args) {
- Configuration C;
- LinkerDriver D;
- Config = &C;
- Driver = &D;
+BumpPtrAllocator BAlloc;
+StringSaver Saver{BAlloc};
+std::vector<SpecificAllocBase *> SpecificAllocBase::Instances;
+
+bool link(ArrayRef<const char *> Args) {
+ Config = make<Configuration>();
+ Driver = make<LinkerDriver>();
Driver->link(Args);
return true;
}
@@ -58,26 +69,123 @@ static std::string getOutputPath(StringRef Path) {
return (S.substr(0, S.rfind('.')) + E).str();
}
-// Opens a file. Path has to be resolved already.
-// Newly created memory buffers are owned by this driver.
-MemoryBufferRef LinkerDriver::openFile(StringRef Path) {
- std::unique_ptr<MemoryBuffer> MB =
- check(MemoryBuffer::getFile(Path), "could not open " + Path);
- MemoryBufferRef MBRef = MB->getMemBufferRef();
- OwningMBs.push_back(std::move(MB)); // take ownership
+// ErrorOr is not default constructible, so it cannot be used as the type
+// parameter of a future.
+// FIXME: We could open the file in createFutureForFile and avoid needing to
+// return an error here, but for the moment that would cost us a file descriptor
+// (a limited resource on Windows) for the duration that the future is pending.
+typedef std::pair<std::unique_ptr<MemoryBuffer>, std::error_code> MBErrPair;
+
+// Create a std::future that opens and maps a file using the best strategy for
+// the host platform.
+static std::future<MBErrPair> createFutureForFile(std::string Path) {
+#if LLVM_ON_WIN32
+ // On Windows, file I/O is relatively slow so it is best to do this
+ // asynchronously.
+ auto Strategy = std::launch::async;
+#else
+ auto Strategy = std::launch::deferred;
+#endif
+ return std::async(Strategy, [=]() {
+ auto MBOrErr = MemoryBuffer::getFile(Path);
+ if (!MBOrErr)
+ return MBErrPair{nullptr, MBOrErr.getError()};
+ return MBErrPair{std::move(*MBOrErr), std::error_code()};
+ });
+}
+
+MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> MB) {
+ MemoryBufferRef MBRef = *MB;
+ OwningMBs.push_back(std::move(MB));
+
+ if (Driver->Cpio)
+ Driver->Cpio->append(relativeToRoot(MBRef.getBufferIdentifier()),
+ MBRef.getBuffer());
+
return MBRef;
}
-static std::unique_ptr<InputFile> createFile(MemoryBufferRef MB) {
+void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> MB) {
+ MemoryBufferRef MBRef = takeBuffer(std::move(MB));
+
// File type is detected by contents, not by file extension.
- file_magic Magic = identify_magic(MB.getBuffer());
+ file_magic Magic = identify_magic(MBRef.getBuffer());
+ if (Magic == file_magic::windows_resource) {
+ Resources.push_back(MBRef);
+ return;
+ }
+
+ FilePaths.push_back(MBRef.getBufferIdentifier());
if (Magic == file_magic::archive)
- return std::unique_ptr<InputFile>(new ArchiveFile(MB));
+ return Symtab.addFile(make<ArchiveFile>(MBRef));
if (Magic == file_magic::bitcode)
- return std::unique_ptr<InputFile>(new BitcodeFile(MB));
+ return Symtab.addFile(make<BitcodeFile>(MBRef));
+ if (Magic == file_magic::coff_cl_gl_object)
+ fatal(MBRef.getBufferIdentifier() + ": is not a native COFF file. "
+ "Recompile without /GL");
+ Symtab.addFile(make<ObjectFile>(MBRef));
+}
+
+void LinkerDriver::enqueuePath(StringRef Path) {
+ auto Future =
+ std::make_shared<std::future<MBErrPair>>(createFutureForFile(Path));
+ std::string PathStr = Path;
+ enqueueTask([=]() {
+ auto MBOrErr = Future->get();
+ if (MBOrErr.second)
+ fatal(MBOrErr.second, "could not open " + PathStr);
+ Driver->addBuffer(std::move(MBOrErr.first));
+ });
+
if (Config->OutputFile == "")
- Config->OutputFile = getOutputPath(MB.getBufferIdentifier());
- return std::unique_ptr<InputFile>(new ObjectFile(MB));
+ Config->OutputFile = getOutputPath(Path);
+}
+
+void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName,
+ StringRef ParentName) {
+ file_magic Magic = identify_magic(MB.getBuffer());
+ if (Magic == file_magic::coff_import_library) {
+ Symtab.addFile(make<ImportFile>(MB));
+ return;
+ }
+
+ InputFile *Obj;
+ if (Magic == file_magic::coff_object)
+ Obj = make<ObjectFile>(MB);
+ else if (Magic == file_magic::bitcode)
+ Obj = make<BitcodeFile>(MB);
+ else
+ fatal("unknown file type: " + MB.getBufferIdentifier());
+
+ Obj->ParentName = ParentName;
+ Symtab.addFile(Obj);
+ if (Config->Verbose)
+ outs() << "Loaded " << toString(Obj) << " for " << SymName << "\n";
+}
+
+void LinkerDriver::enqueueArchiveMember(const Archive::Child &C,
+ StringRef SymName,
+ StringRef ParentName) {
+ if (!C.getParent()->isThin()) {
+ MemoryBufferRef MB = check(
+ C.getMemoryBufferRef(),
+ "could not get the buffer for the member defining symbol " + SymName);
+ enqueueTask([=]() { Driver->addArchiveBuffer(MB, SymName, ParentName); });
+ return;
+ }
+
+ auto Future = std::make_shared<std::future<MBErrPair>>(createFutureForFile(
+ check(C.getFullName(),
+ "could not get the filename for the member defining symbol " +
+ SymName)));
+ enqueueTask([=]() {
+ auto MBOrErr = Future->get();
+ if (MBOrErr.second)
+ fatal(MBOrErr.second,
+ "could not get the buffer for the member defining " + SymName);
+ Driver->addArchiveBuffer(takeBuffer(std::move(MBOrErr.first)), SymName,
+ ParentName);
+ });
}
static bool isDecorated(StringRef Sym) {
@@ -87,7 +195,7 @@ static bool isDecorated(StringRef Sym) {
// Parses .drectve section contents and returns a list of files
// specified by /defaultlib.
void LinkerDriver::parseDirectives(StringRef S) {
- llvm::opt::InputArgList Args = Parser.parse(S);
+ opt::InputArgList Args = Parser.parse(S);
for (auto *Arg : Args) {
switch (Arg->getOption().getID()) {
@@ -95,10 +203,8 @@ void LinkerDriver::parseDirectives(StringRef S) {
parseAlternateName(Arg->getValue());
break;
case OPT_defaultlib:
- if (Optional<StringRef> Path = findLib(Arg->getValue())) {
- MemoryBufferRef MB = openFile(*Path);
- Symtab.addFile(createFile(MB));
- }
+ if (Optional<StringRef> Path = findLib(Arg->getValue()))
+ enqueuePath(*Path);
break;
case OPT_export: {
Export E = parseExport(Arg->getValue());
@@ -135,19 +241,19 @@ void LinkerDriver::parseDirectives(StringRef S) {
// Find file from search paths. You can omit ".obj", this function takes
// care of that. Note that the returned path is not guaranteed to exist.
StringRef LinkerDriver::doFindFile(StringRef Filename) {
- bool hasPathSep = (Filename.find_first_of("/\\") != StringRef::npos);
- if (hasPathSep)
+ bool HasPathSep = (Filename.find_first_of("/\\") != StringRef::npos);
+ if (HasPathSep)
return Filename;
- bool hasExt = (Filename.find('.') != StringRef::npos);
+ bool HasExt = (Filename.find('.') != StringRef::npos);
for (StringRef Dir : SearchPaths) {
SmallString<128> Path = Dir;
- llvm::sys::path::append(Path, Filename);
- if (llvm::sys::fs::exists(Path.str()))
- return Alloc.save(Path.str());
- if (!hasExt) {
+ sys::path::append(Path, Filename);
+ if (sys::fs::exists(Path.str()))
+ return Saver.save(Path.str());
+ if (!HasExt) {
Path.append(".obj");
- if (llvm::sys::fs::exists(Path.str()))
- return Alloc.save(Path.str());
+ if (sys::fs::exists(Path.str()))
+ return Saver.save(Path.str());
}
}
return Filename;
@@ -166,9 +272,9 @@ Optional<StringRef> LinkerDriver::findFile(StringRef Filename) {
// Find library file from search path.
StringRef LinkerDriver::doFindLib(StringRef Filename) {
// Add ".lib" to Filename if that has no file extension.
- bool hasExt = (Filename.find('.') != StringRef::npos);
- if (!hasExt)
- Filename = Alloc.save(Filename + ".lib");
+ bool HasExt = (Filename.find('.') != StringRef::npos);
+ if (!HasExt)
+ Filename = Saver.save(Filename + ".lib");
return doFindFile(Filename);
}
@@ -178,11 +284,12 @@ StringRef LinkerDriver::doFindLib(StringRef Filename) {
Optional<StringRef> LinkerDriver::findLib(StringRef Filename) {
if (Config->NoDefaultLibAll)
return None;
+ if (!VisitedLibs.insert(Filename.lower()).second)
+ return None;
StringRef Path = doFindLib(Filename);
if (Config->NoDefaultLibs.count(Path))
return None;
- bool Seen = !VisitedFiles.insert(Path.lower()).second;
- if (Seen)
+ if (!VisitedFiles.insert(Path.lower()).second)
return None;
return Path;
}
@@ -192,7 +299,7 @@ void LinkerDriver::addLibSearchPaths() {
Optional<std::string> EnvOpt = Process::GetEnv("LIB");
if (!EnvOpt.hasValue())
return;
- StringRef Env = Alloc.save(*EnvOpt);
+ StringRef Env = Saver.save(*EnvOpt);
while (!Env.empty()) {
StringRef Path;
std::tie(Path, Env) = Env.split(';');
@@ -200,17 +307,17 @@ void LinkerDriver::addLibSearchPaths() {
}
}
-Undefined *LinkerDriver::addUndefined(StringRef Name) {
- Undefined *U = Symtab.addUndefined(Name);
- Config->GCRoot.insert(U);
- return U;
+SymbolBody *LinkerDriver::addUndefined(StringRef Name) {
+ SymbolBody *B = Symtab.addUndefined(Name);
+ Config->GCRoot.insert(B);
+ return B;
}
// Symbol names are mangled by appending "_" prefix on x86.
StringRef LinkerDriver::mangle(StringRef Sym) {
assert(Config->Machine != IMAGE_FILE_MACHINE_UNKNOWN);
if (Config->Machine == I386)
- return Alloc.save("_" + Sym);
+ return Saver.save("_" + Sym);
return Sym;
}
@@ -225,7 +332,7 @@ StringRef LinkerDriver::findDefaultEntry() {
};
for (auto E : Entries) {
StringRef Entry = Symtab.findMangle(mangle(E[0]));
- if (!Entry.empty() && !isa<Undefined>(Symtab.find(Entry)->Body))
+ if (!Entry.empty() && !isa<Undefined>(Symtab.find(Entry)->body()))
return mangle(E[1]);
}
return "";
@@ -247,7 +354,83 @@ static uint64_t getDefaultImageBase() {
return Config->DLL ? 0x10000000 : 0x400000;
}
-void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
+static std::string createResponseFile(const opt::InputArgList &Args,
+ ArrayRef<StringRef> FilePaths,
+ ArrayRef<StringRef> SearchPaths) {
+ SmallString<0> Data;
+ raw_svector_ostream OS(Data);
+
+ for (auto *Arg : Args) {
+ switch (Arg->getOption().getID()) {
+ case OPT_linkrepro:
+ case OPT_INPUT:
+ case OPT_defaultlib:
+ case OPT_libpath:
+ break;
+ default:
+ OS << stringize(Arg) << "\n";
+ }
+ }
+
+ for (StringRef Path : SearchPaths) {
+ std::string RelPath = relativeToRoot(Path);
+ OS << "/libpath:" << quote(RelPath) << "\n";
+ }
+
+ for (StringRef Path : FilePaths)
+ OS << quote(relativeToRoot(Path)) << "\n";
+
+ return Data.str();
+}
+
+static unsigned getDefaultDebugType(const opt::InputArgList &Args) {
+ unsigned DebugTypes = static_cast<unsigned>(DebugType::CV);
+ if (Args.hasArg(OPT_driver))
+ DebugTypes |= static_cast<unsigned>(DebugType::PData);
+ if (Args.hasArg(OPT_profile))
+ DebugTypes |= static_cast<unsigned>(DebugType::Fixup);
+ return DebugTypes;
+}
+
+static unsigned parseDebugType(StringRef Arg) {
+ SmallVector<StringRef, 3> Types;
+ Arg.split(Types, ',', /*KeepEmpty=*/false);
+
+ unsigned DebugTypes = static_cast<unsigned>(DebugType::None);
+ for (StringRef Type : Types)
+ DebugTypes |= StringSwitch<unsigned>(Type.lower())
+ .Case("cv", static_cast<unsigned>(DebugType::CV))
+ .Case("pdata", static_cast<unsigned>(DebugType::PData))
+ .Case("fixup", static_cast<unsigned>(DebugType::Fixup));
+ return DebugTypes;
+}
+
+static std::string getMapFile(const opt::InputArgList &Args) {
+ auto *Arg = Args.getLastArg(OPT_lldmap, OPT_lldmap_file);
+ if (!Arg)
+ return "";
+ if (Arg->getOption().getID() == OPT_lldmap_file)
+ return Arg->getValue();
+
+ assert(Arg->getOption().getID() == OPT_lldmap);
+ StringRef OutFile = Config->OutputFile;
+ return (OutFile.substr(0, OutFile.rfind('.')) + ".map").str();
+}
+
+void LinkerDriver::enqueueTask(std::function<void()> Task) {
+ TaskQueue.push_back(std::move(Task));
+}
+
+bool LinkerDriver::run() {
+ bool DidWork = !TaskQueue.empty();
+ while (!TaskQueue.empty()) {
+ TaskQueue.front()();
+ TaskQueue.pop_front();
+ }
+ return DidWork;
+}
+
+void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// If the first command line argument is "/lib", link.exe acts like lib.exe.
// We call our own implementation of lib.exe that understands bitcode files.
if (ArgsArr.size() > 1 && StringRef(ArgsArr[1]).equals_lower("/lib")) {
@@ -257,15 +440,15 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
}
// Needed for LTO.
- llvm::InitializeAllTargetInfos();
- llvm::InitializeAllTargets();
- llvm::InitializeAllTargetMCs();
- llvm::InitializeAllAsmParsers();
- llvm::InitializeAllAsmPrinters();
- llvm::InitializeAllDisassemblers();
+ InitializeAllTargetInfos();
+ InitializeAllTargets();
+ InitializeAllTargetMCs();
+ InitializeAllAsmParsers();
+ InitializeAllAsmPrinters();
+ InitializeAllDisassemblers();
// Parse command line options.
- llvm::opt::InputArgList Args = Parser.parseLINK(ArgsArr.slice(1));
+ opt::InputArgList Args = Parser.parseLINK(ArgsArr.slice(1));
// Handle /help
if (Args.hasArg(OPT_help)) {
@@ -273,6 +456,17 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
return;
}
+ if (auto *Arg = Args.getLastArg(OPT_linkrepro)) {
+ SmallString<64> Path = StringRef(Arg->getValue());
+ sys::path::append(Path, "repro");
+ ErrorOr<CpioFile *> F = CpioFile::create(Path);
+ if (F)
+ Cpio.reset(*F);
+ else
+ errs() << "/linkrepro: failed to open " << Path
+ << ".cpio: " << F.getError().message() << '\n';
+ }
+
if (Args.filtered_begin(OPT_INPUT) == Args.filtered_end())
fatal("no input files");
@@ -295,8 +489,17 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
Config->Force = true;
// Handle /debug
- if (Args.hasArg(OPT_debug))
+ if (Args.hasArg(OPT_debug)) {
Config->Debug = true;
+ Config->DebugTypes =
+ Args.hasArg(OPT_debugtype)
+ ? parseDebugType(Args.getLastArg(OPT_debugtype)->getValue())
+ : getDefaultDebugType(Args);
+ }
+
+ // Create a dummy PDB file to satisfy build sytem rules.
+ if (auto *Arg = Args.getLastArg(OPT_pdb))
+ Config->PDBPath = Arg->getValue();
// Handle /noentry
if (Args.hasArg(OPT_noentry)) {
@@ -447,72 +650,43 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
Config->TerminalServerAware = false;
if (Args.hasArg(OPT_nosymtab))
Config->WriteSymtab = false;
+ Config->DumpPdb = Args.hasArg(OPT_dumppdb);
+ Config->DebugPdb = Args.hasArg(OPT_debugpdb);
// Create a list of input files. Files can be given as arguments
// for /defaultlib option.
- std::vector<StringRef> Paths;
std::vector<MemoryBufferRef> MBs;
for (auto *Arg : Args.filtered(OPT_INPUT))
if (Optional<StringRef> Path = findFile(Arg->getValue()))
- Paths.push_back(*Path);
+ enqueuePath(*Path);
for (auto *Arg : Args.filtered(OPT_defaultlib))
if (Optional<StringRef> Path = findLib(Arg->getValue()))
- Paths.push_back(*Path);
- for (StringRef Path : Paths)
- MBs.push_back(openFile(Path));
+ enqueuePath(*Path);
// Windows specific -- Create a resource file containing a manifest file.
- if (Config->Manifest == Configuration::Embed) {
- std::unique_ptr<MemoryBuffer> MB = createManifestRes();
- MBs.push_back(MB->getMemBufferRef());
- OwningMBs.push_back(std::move(MB)); // take ownership
- }
+ if (Config->Manifest == Configuration::Embed)
+ addBuffer(createManifestRes());
- // Windows specific -- Input files can be Windows resource files (.res files).
- // We invoke cvtres.exe to convert resource files to a regular COFF file
- // then link the result file normally.
- std::vector<MemoryBufferRef> Resources;
- auto NotResource = [](MemoryBufferRef MB) {
- return identify_magic(MB.getBuffer()) != file_magic::windows_resource;
- };
- auto It = std::stable_partition(MBs.begin(), MBs.end(), NotResource);
- if (It != MBs.end()) {
- Resources.insert(Resources.end(), It, MBs.end());
- MBs.erase(It, MBs.end());
- }
+ // Read all input files given via the command line.
+ run();
- // Read all input files given via the command line. Note that step()
- // doesn't read files that are specified by directive sections.
- for (MemoryBufferRef MB : MBs)
- Symtab.addFile(createFile(MB));
- Symtab.step();
-
- // Determine machine type and check if all object files are
- // for the same CPU type. Note that this needs to be done before
- // any call to mangle().
- for (std::unique_ptr<InputFile> &File : Symtab.getFiles()) {
- MachineTypes MT = File->getMachineType();
- if (MT == IMAGE_FILE_MACHINE_UNKNOWN)
- continue;
- if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) {
- Config->Machine = MT;
- continue;
- }
- if (Config->Machine != MT)
- fatal(File->getShortName() + ": machine type " + machineToStr(MT) +
- " conflicts with " + machineToStr(Config->Machine));
- }
+ // We should have inferred a machine type by now from the input files, but if
+ // not we assume x64.
if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) {
- llvm::errs() << "warning: /machine is not specified. x64 is assumed.\n";
+ errs() << "warning: /machine is not specified. x64 is assumed.\n";
Config->Machine = AMD64;
}
- // Windows specific -- Convert Windows resource files to a COFF file.
- if (!Resources.empty()) {
- std::unique_ptr<MemoryBuffer> MB = convertResToCOFF(Resources);
- Symtab.addFile(createFile(MB->getMemBufferRef()));
- OwningMBs.push_back(std::move(MB)); // take ownership
- }
+ // Windows specific -- Input files can be Windows resource files (.res files).
+ // We invoke cvtres.exe to convert resource files to a regular COFF file
+ // then link the result file normally.
+ if (!Resources.empty())
+ addBuffer(convertResToCOFF(Resources));
+
+ if (Cpio)
+ Cpio->append("response.txt",
+ createResponseFile(Args, FilePaths,
+ ArrayRef<StringRef>(SearchPaths).slice(1)));
// Handle /largeaddressaware
if (Config->is64() || Args.hasArg(OPT_largeaddressaware))
@@ -537,7 +711,7 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
fatal("entry point must be defined");
Config->Entry = addUndefined(S);
if (Config->Verbose)
- llvm::outs() << "Entry name inferred: " << S << "\n";
+ outs() << "Entry name inferred: " << S << "\n";
}
// Handle /export
@@ -545,18 +719,19 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
Export E = parseExport(Arg->getValue());
if (Config->Machine == I386) {
if (!isDecorated(E.Name))
- E.Name = Alloc.save("_" + E.Name);
+ E.Name = Saver.save("_" + E.Name);
if (!E.ExtName.empty() && !isDecorated(E.ExtName))
- E.ExtName = Alloc.save("_" + E.ExtName);
+ E.ExtName = Saver.save("_" + E.ExtName);
}
Config->Exports.push_back(E);
}
// Handle /def
if (auto *Arg = Args.getLastArg(OPT_deffile)) {
- MemoryBufferRef MB = openFile(Arg->getValue());
// parseModuleDefs mutates Config object.
- parseModuleDefs(MB, &Alloc);
+ parseModuleDefs(
+ takeBuffer(check(MemoryBuffer::getFile(Arg->getValue()),
+ Twine("could not open ") + Arg->getValue())));
}
// Handle /delayload
@@ -585,14 +760,10 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
Symtab.addAbsolute(mangle("__guard_fids_count"), 0);
Symtab.addAbsolute(mangle("__guard_flags"), 0x100);
- // Read as much files as we can from directives sections.
- Symtab.run();
-
- // Resolve auxiliary symbols until we get a convergence.
- // (Trying to resolve a symbol may trigger a Lazy symbol to load a new file.
- // A new file may contain a directive section to add new command line options.
- // That's why we have to repeat until converge.)
- for (;;) {
+ // This code may add new undefined symbols to the link, which may enqueue more
+ // symbol resolution tasks, so we need to continue executing tasks until we
+ // converge.
+ do {
// Windows specific -- if entry point is not found,
// search for its mangled names.
if (Config->Entry)
@@ -615,7 +786,7 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
Symbol *Sym = Symtab.find(From);
if (!Sym)
continue;
- if (auto *U = dyn_cast<Undefined>(Sym->Body))
+ if (auto *U = dyn_cast<Undefined>(Sym->body()))
if (!U->WeakAlias)
U->WeakAlias = Symtab.addUndefined(To);
}
@@ -623,18 +794,15 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
// Windows specific -- if __load_config_used can be resolved, resolve it.
if (Symtab.findUnderscore("_load_config_used"))
addUndefined(mangle("_load_config_used"));
-
- if (Symtab.queueEmpty())
- break;
- Symtab.run();
- }
+ } while (run());
// Do LTO by compiling bitcode input files to a set of native COFF files then
// link those files.
Symtab.addCombinedLTOObjects();
+ run();
// Make sure we have resolved all symbols.
- Symtab.reportRemainingUndefines(/*Resolve=*/true);
+ Symtab.reportRemainingUndefines();
// Windows specific -- if no /subsystem is given, we need to infer
// that from entry point name.
@@ -662,10 +830,6 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
if (Config->Manifest == Configuration::SideBySide)
createSideBySideManifest();
- // Create a dummy PDB file to satisfy build sytem rules.
- if (auto *Arg = Args.getLastArg(OPT_pdb))
- createPDB(Arg->getValue());
-
// Identify unreferenced COMDAT sections.
if (Config->DoGC)
markLive(Symtab.getChunks());
@@ -679,13 +843,15 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
// Create a symbol map file containing symbol VAs and their names
// to help debugging.
- if (auto *Arg = Args.getLastArg(OPT_lldmap)) {
+ std::string MapFile = getMapFile(Args);
+ if (!MapFile.empty()) {
std::error_code EC;
- llvm::raw_fd_ostream Out(Arg->getValue(), EC, OpenFlags::F_Text);
+ raw_fd_ostream Out(MapFile, EC, OpenFlags::F_Text);
if (EC)
- fatal(EC, "could not create the symbol map");
+ fatal(EC, "could not create the symbol map " + MapFile);
Symtab.printMap(Out);
}
+
// Call exit to avoid calling destructors.
exit(0);
}
diff --git a/COFF/Driver.h b/COFF/Driver.h
index 23969ee802fb..e8114640edec 100644
--- a/COFF/Driver.h
+++ b/COFF/Driver.h
@@ -13,12 +13,13 @@
#include "Config.h"
#include "SymbolTable.h"
#include "lld/Core/LLVM.h"
+#include "lld/Core/Reproduce.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/Archive.h"
#include "llvm/Object/COFF.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
-#include "llvm/Support/StringSaver.h"
#include <memory>
#include <set>
#include <vector>
@@ -42,7 +43,6 @@ void doICF(const std::vector<Chunk *> &Chunks);
class ArgParser {
public:
- ArgParser() : Alloc(AllocAux) {}
// Parses command line options.
llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> Args);
@@ -56,25 +56,26 @@ private:
std::vector<const char *> tokenize(StringRef S);
std::vector<const char *> replaceResponseFiles(std::vector<const char *>);
-
- llvm::BumpPtrAllocator AllocAux;
- llvm::StringSaver Alloc;
};
class LinkerDriver {
public:
- LinkerDriver() : Alloc(AllocAux) {}
+ LinkerDriver() { coff::Symtab = &Symtab; }
void link(llvm::ArrayRef<const char *> Args);
// Used by the resolver to parse .drectve section contents.
void parseDirectives(StringRef S);
+ // Used by ArchiveFile to enqueue members.
+ void enqueueArchiveMember(const Archive::Child &C, StringRef SymName,
+ StringRef ParentName);
+
private:
- llvm::BumpPtrAllocator AllocAux;
- llvm::StringSaver Alloc;
ArgParser Parser;
SymbolTable Symtab;
+ std::unique_ptr<CpioFile> Cpio; // for /linkrepro
+
// Opens a file. Path has to be resolved already.
MemoryBufferRef openFile(StringRef Path);
@@ -90,8 +91,9 @@ private:
// Library search path. The first element is always "" (current directory).
std::vector<StringRef> SearchPaths;
std::set<std::string> VisitedFiles;
+ std::set<std::string> VisitedLibs;
- Undefined *addUndefined(StringRef Sym);
+ SymbolBody *addUndefined(StringRef Sym);
StringRef mangle(StringRef Sym);
// Windows specific -- "main" is not the only main function in Windows.
@@ -104,12 +106,26 @@ private:
StringRef findDefaultEntry();
WindowsSubsystem inferSubsystem();
+ MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> MB);
+ void addBuffer(std::unique_ptr<MemoryBuffer> MB);
+ void addArchiveBuffer(MemoryBufferRef MBRef, StringRef SymName,
+ StringRef ParentName);
+
+ void enqueuePath(StringRef Path);
+
+ void enqueueTask(std::function<void()> Task);
+ bool run();
+
// Driver is the owner of all opened files.
// InputFiles have MemoryBufferRefs to them.
std::vector<std::unique_ptr<MemoryBuffer>> OwningMBs;
+
+ std::list<std::function<void()>> TaskQueue;
+ std::vector<StringRef> FilePaths;
+ std::vector<MemoryBufferRef> Resources;
};
-void parseModuleDefs(MemoryBufferRef MB, llvm::StringSaver *Alloc);
+void parseModuleDefs(MemoryBufferRef MB);
void writeImportLibrary();
// Functions below this line are defined in DriverUtils.cpp.
@@ -161,8 +177,6 @@ void checkFailIfMismatch(StringRef Arg);
std::unique_ptr<MemoryBuffer>
convertResToCOFF(const std::vector<MemoryBufferRef> &MBs);
-void createPDB(StringRef Path);
-
// Create enum with OPT_xxx values for each option in Options.td
enum {
OPT_INVALID = 0,
diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp
index 5d7dc2bc65af..14dd004f1c04 100644
--- a/COFF/DriverUtils.cpp
+++ b/COFF/DriverUtils.cpp
@@ -16,6 +16,7 @@
#include "Config.h"
#include "Driver.h"
#include "Error.h"
+#include "Memory.h"
#include "Symbols.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringSwitch.h"
@@ -43,29 +44,29 @@ namespace {
class Executor {
public:
explicit Executor(StringRef S) : Saver(Alloc), Prog(Saver.save(S)) {}
- void add(StringRef S) { Args.push_back(Saver.save(S)); }
- void add(std::string &S) { Args.push_back(Saver.save(S)); }
- void add(Twine S) { Args.push_back(Saver.save(S)); }
- void add(const char *S) { Args.push_back(Saver.save(S)); }
+ void add(StringRef S) { Args.push_back(Saver.save(S).data()); }
+ void add(std::string &S) { Args.push_back(Saver.save(S).data()); }
+ void add(Twine S) { Args.push_back(Saver.save(S).data()); }
+ void add(const char *S) { Args.push_back(Saver.save(S).data()); }
void run() {
- ErrorOr<std::string> ExeOrErr = llvm::sys::findProgramByName(Prog);
+ ErrorOr<std::string> ExeOrErr = sys::findProgramByName(Prog);
if (auto EC = ExeOrErr.getError())
fatal(EC, "unable to find " + Prog + " in PATH: ");
- const char *Exe = Saver.save(*ExeOrErr);
+ const char *Exe = Saver.save(*ExeOrErr).data();
Args.insert(Args.begin(), Exe);
Args.push_back(nullptr);
- if (llvm::sys::ExecuteAndWait(Args[0], Args.data()) != 0) {
+ if (sys::ExecuteAndWait(Args[0], Args.data()) != 0) {
for (const char *S : Args)
if (S)
- llvm::errs() << S << " ";
+ errs() << S << " ";
fatal("ExecuteAndWait failed");
}
}
private:
- llvm::BumpPtrAllocator Alloc;
- llvm::StringSaver Saver;
+ BumpPtrAllocator Alloc;
+ StringSaver Saver;
StringRef Prog;
std::vector<const char *> Args;
};
@@ -75,10 +76,8 @@ private:
// Returns /machine's value.
MachineTypes getMachineType(StringRef S) {
MachineTypes MT = StringSwitch<MachineTypes>(S.lower())
- .Case("x64", AMD64)
- .Case("amd64", AMD64)
- .Case("x86", I386)
- .Case("i386", I386)
+ .Cases("x64", "amd64", AMD64)
+ .Cases("x86", "i386", I386)
.Case("arm", ARMNT)
.Default(IMAGE_FILE_MACHINE_UNKNOWN);
if (MT != IMAGE_FILE_MACHINE_UNKNOWN)
@@ -168,8 +167,8 @@ void parseMerge(StringRef S) {
if (!Inserted) {
StringRef Existing = Pair.first->second;
if (Existing != To)
- llvm::errs() << "warning: " << S << ": already merged into "
- << Existing << "\n";
+ errs() << "warning: " << S << ": already merged into " << Existing
+ << "\n";
}
}
@@ -279,18 +278,54 @@ static void quoteAndPrint(raw_ostream &Out, StringRef S) {
}
}
+// An RAII temporary file class that automatically removes a temporary file.
+namespace {
+class TemporaryFile {
+public:
+ TemporaryFile(StringRef Prefix, StringRef Extn) {
+ SmallString<128> S;
+ if (auto EC = sys::fs::createTemporaryFile("lld-" + Prefix, Extn, S))
+ fatal(EC, "cannot create a temporary file");
+ Path = S.str();
+ }
+
+ TemporaryFile(TemporaryFile &&Obj) {
+ std::swap(Path, Obj.Path);
+ }
+
+ ~TemporaryFile() {
+ if (Path.empty())
+ return;
+ if (sys::fs::remove(Path))
+ fatal("failed to remove " + Path);
+ }
+
+ // Returns a memory buffer of this temporary file.
+ // Note that this function does not leave the file open,
+ // so it is safe to remove the file immediately after this function
+ // is called (you cannot remove an opened file on Windows.)
+ std::unique_ptr<MemoryBuffer> getMemoryBuffer() {
+ // IsVolatileSize=true forces MemoryBuffer to not use mmap().
+ return check(MemoryBuffer::getFile(Path, /*FileSize=*/-1,
+ /*RequiresNullTerminator=*/false,
+ /*IsVolatileSize=*/true),
+ "could not open " + Path);
+ }
+
+ std::string Path;
+};
+}
+
// Create the default manifest file as a temporary file.
-static std::string createDefaultXml() {
+TemporaryFile createDefaultXml() {
// Create a temporary file.
- SmallString<128> Path;
- if (auto EC = sys::fs::createTemporaryFile("tmp", "manifest", Path))
- fatal(EC, "cannot create a temporary file");
+ TemporaryFile File("defaultxml", "manifest");
// Open the temporary file for writing.
std::error_code EC;
- llvm::raw_fd_ostream OS(Path, EC, sys::fs::F_Text);
+ raw_fd_ostream OS(File.Path, EC, sys::fs::F_Text);
if (EC)
- fatal(EC, "failed to open " + Path);
+ fatal(EC, "failed to open " + File.Path);
// Emit the XML. Note that we do *not* verify that the XML attributes are
// syntactically correct. This is intentional for link.exe compatibility.
@@ -316,56 +351,48 @@ static std::string createDefaultXml() {
}
OS << "</assembly>\n";
OS.close();
- return StringRef(Path);
+ return File;
}
static std::string readFile(StringRef Path) {
std::unique_ptr<MemoryBuffer> MB =
check(MemoryBuffer::getFile(Path), "could not open " + Path);
- std::unique_ptr<MemoryBuffer> Buf(std::move(MB));
- return Buf->getBuffer();
+ return MB->getBuffer();
}
static std::string createManifestXml() {
// Create the default manifest file.
- std::string Path1 = createDefaultXml();
+ TemporaryFile File1 = createDefaultXml();
if (Config->ManifestInput.empty())
- return readFile(Path1);
+ return readFile(File1.Path);
// If manifest files are supplied by the user using /MANIFESTINPUT
// option, we need to merge them with the default manifest.
- SmallString<128> Path2;
- if (auto EC = sys::fs::createTemporaryFile("tmp", "manifest", Path2))
- fatal(EC, "cannot create a temporary file");
- FileRemover Remover1(Path1);
- FileRemover Remover2(Path2);
+ TemporaryFile File2("user", "manifest");
Executor E("mt.exe");
E.add("/manifest");
- E.add(Path1);
+ E.add(File1.Path);
for (StringRef Filename : Config->ManifestInput) {
E.add("/manifest");
E.add(Filename);
}
E.add("/nologo");
- E.add("/out:" + StringRef(Path2));
+ E.add("/out:" + StringRef(File2.Path));
E.run();
- return readFile(Path2);
+ return readFile(File2.Path);
}
// Create a resource file containing a manifest XML.
std::unique_ptr<MemoryBuffer> createManifestRes() {
// Create a temporary file for the resource script file.
- SmallString<128> RCPath;
- if (auto EC = sys::fs::createTemporaryFile("tmp", "rc", RCPath))
- fatal(EC, "cannot create a temporary file");
- FileRemover RCRemover(RCPath);
+ TemporaryFile RCFile("manifest", "rc");
// Open the temporary file for writing.
std::error_code EC;
- llvm::raw_fd_ostream Out(RCPath, EC, sys::fs::F_Text);
+ raw_fd_ostream Out(RCFile.Path, EC, sys::fs::F_Text);
if (EC)
- fatal(EC, "failed to open " + RCPath);
+ fatal(EC, "failed to open " + RCFile.Path);
// Write resource script to the RC file.
Out << "#define LANG_ENGLISH 9\n"
@@ -379,17 +406,15 @@ std::unique_ptr<MemoryBuffer> createManifestRes() {
Out.close();
// Create output resource file.
- SmallString<128> ResPath;
- if (auto EC = sys::fs::createTemporaryFile("tmp", "res", ResPath))
- fatal(EC, "cannot create a temporary file");
+ TemporaryFile ResFile("output-resource", "res");
Executor E("rc.exe");
E.add("/fo");
- E.add(ResPath.str());
+ E.add(ResFile.Path);
E.add("/nologo");
- E.add(RCPath.str());
+ E.add(RCFile.Path);
E.run();
- return check(MemoryBuffer::getFile(ResPath), "could not open " + ResPath);
+ return ResFile.getMemoryBuffer();
}
void createSideBySideManifest() {
@@ -397,7 +422,7 @@ void createSideBySideManifest() {
if (Path == "")
Path = Config->OutputFile + ".manifest";
std::error_code EC;
- llvm::raw_fd_ostream Out(Path, EC, llvm::sys::fs::F_Text);
+ raw_fd_ostream Out(Path, EC, sys::fs::F_Text);
if (EC)
fatal(EC, "failed to create manifest");
Out << createManifestXml();
@@ -485,12 +510,14 @@ void fixupExports() {
}
for (Export &E : Config->Exports) {
+ SymbolBody *Sym = E.Sym;
if (!E.ForwardTo.empty()) {
E.SymbolName = E.Name;
- } else if (Undefined *U = cast_or_null<Undefined>(E.Sym->WeakAlias)) {
- E.SymbolName = U->getName();
} else {
- E.SymbolName = E.Sym->getName();
+ if (auto *U = dyn_cast<Undefined>(Sym))
+ if (U->WeakAlias)
+ Sym = U->WeakAlias;
+ E.SymbolName = Sym->getName();
}
}
@@ -515,7 +542,7 @@ void fixupExports() {
Export *Existing = Pair.first->second;
if (E == *Existing || E.Name != Existing->Name)
continue;
- llvm::errs() << "warning: duplicate /export option: " << E.Name << "\n";
+ errs() << "warning: duplicate /export option: " << E.Name << "\n";
}
Config->Exports = std::move(V);
@@ -555,20 +582,39 @@ void checkFailIfMismatch(StringRef Arg) {
std::unique_ptr<MemoryBuffer>
convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) {
// Create an output file path.
- SmallString<128> Path;
- if (auto EC = llvm::sys::fs::createTemporaryFile("resource", "obj", Path))
- fatal(EC, "could not create temporary file");
+ TemporaryFile File("resource-file", "obj");
// Execute cvtres.exe.
Executor E("cvtres.exe");
E.add("/machine:" + machineToStr(Config->Machine));
E.add("/readonly");
E.add("/nologo");
- E.add("/out:" + Path);
- for (MemoryBufferRef MB : MBs)
- E.add(MB.getBufferIdentifier());
+ E.add("/out:" + Twine(File.Path));
+
+ // We must create new files because the memory buffers we have may have no
+ // underlying file still existing on the disk.
+ // It happens if it was created from a TemporaryFile, which usually delete
+ // the file just after creating the MemoryBuffer.
+ std::vector<TemporaryFile> ResFiles;
+ ResFiles.reserve(MBs.size());
+ for (MemoryBufferRef MB : MBs) {
+ // We store the temporary file in a vector to avoid deletion
+ // before running cvtres
+ ResFiles.emplace_back("resource-file", "res");
+ TemporaryFile& ResFile = ResFiles.back();
+ // Write the content of the resource in a temporary file
+ std::error_code EC;
+ raw_fd_ostream OS(ResFile.Path, EC, sys::fs::F_None);
+ if (EC)
+ fatal(EC, "failed to open " + ResFile.Path);
+ OS << MB.getBuffer();
+ OS.close();
+
+ E.add(ResFile.Path);
+ }
+
E.run();
- return check(MemoryBuffer::getFile(Path), "could not open " + Path);
+ return File.getMemoryBuffer();
}
// Create OptTable
@@ -595,7 +641,7 @@ public:
};
// Parses a given list of options.
-llvm::opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) {
+opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) {
// First, replace respnose files (@<file>-style options).
std::vector<const char *> Argv = replaceResponseFiles(ArgsArr);
@@ -603,28 +649,28 @@ llvm::opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) {
COFFOptTable Table;
unsigned MissingIndex;
unsigned MissingCount;
- llvm::opt::InputArgList Args =
- Table.ParseArgs(Argv, MissingIndex, MissingCount);
+ opt::InputArgList Args = Table.ParseArgs(Argv, MissingIndex, MissingCount);
// Print the real command line if response files are expanded.
if (Args.hasArg(OPT_verbose) && ArgsArr.size() != Argv.size()) {
- llvm::outs() << "Command line:";
+ outs() << "Command line:";
for (const char *S : Argv)
- llvm::outs() << " " << S;
- llvm::outs() << "\n";
+ outs() << " " << S;
+ outs() << "\n";
}
if (MissingCount)
- fatal("missing arg value for \"" + Twine(Args.getArgString(MissingIndex)) +
- "\", expected " + Twine(MissingCount) +
- (MissingCount == 1 ? " argument." : " arguments."));
+ fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument");
for (auto *Arg : Args.filtered(OPT_UNKNOWN))
- llvm::errs() << "ignoring unknown argument: " << Arg->getSpelling() << "\n";
+ errs() << "ignoring unknown argument: " << Arg->getSpelling() << "\n";
return Args;
}
-llvm::opt::InputArgList ArgParser::parseLINK(ArrayRef<const char *> Args) {
- // Concatenate LINK env and given arguments and parse them.
+// link.exe has an interesting feature. If LINK environment exists,
+// its contents are handled as a command line string. So you can pass
+// extra arguments using the environment variable.
+opt::InputArgList ArgParser::parseLINK(ArrayRef<const char *> Args) {
+ // Concatenate LINK env and command line arguments, and then parse them.
Optional<std::string> Env = Process::GetEnv("LINK");
if (!Env)
return parse(Args);
@@ -635,8 +681,7 @@ llvm::opt::InputArgList ArgParser::parseLINK(ArrayRef<const char *> Args) {
std::vector<const char *> ArgParser::tokenize(StringRef S) {
SmallVector<const char *, 16> Tokens;
- StringSaver Saver(AllocAux);
- llvm::cl::TokenizeWindowsCommandLine(S, Saver, Tokens);
+ cl::TokenizeWindowsCommandLine(S, Saver, Tokens);
return std::vector<const char *>(Tokens.begin(), Tokens.end());
}
@@ -645,14 +690,13 @@ std::vector<const char *> ArgParser::tokenize(StringRef S) {
std::vector<const char *>
ArgParser::replaceResponseFiles(std::vector<const char *> Argv) {
SmallVector<const char *, 256> Tokens(Argv.data(), Argv.data() + Argv.size());
- StringSaver Saver(AllocAux);
ExpandResponseFiles(Saver, TokenizeWindowsCommandLine, Tokens);
return std::vector<const char *>(Tokens.begin(), Tokens.end());
}
void printHelp(const char *Argv0) {
COFFOptTable Table;
- Table.PrintHelp(llvm::outs(), Argv0, "LLVM Linker", false);
+ Table.PrintHelp(outs(), Argv0, "LLVM Linker", false);
}
} // namespace coff
diff --git a/COFF/Error.cpp b/COFF/Error.cpp
index 602a8544ce2b..b2bd557413df 100644
--- a/COFF/Error.cpp
+++ b/COFF/Error.cpp
@@ -11,14 +11,31 @@
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/Process.h"
#include "llvm/Support/raw_ostream.h"
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+#include <unistd.h>
+#endif
+
+using namespace llvm;
+
namespace lld {
namespace coff {
void fatal(const Twine &Msg) {
- llvm::errs() << Msg << "\n";
- exit(1);
+ if (sys::Process::StandardErrHasColors()) {
+ errs().changeColor(raw_ostream::RED, /*bold=*/true);
+ errs() << "error: ";
+ errs().resetColor();
+ } else {
+ errs() << "error: ";
+ }
+ errs() << Msg << "\n";
+
+ outs().flush();
+ errs().flush();
+ _exit(1);
}
void fatal(std::error_code EC, const Twine &Msg) {
diff --git a/COFF/Error.h b/COFF/Error.h
index c9f64c662580..47549327db2b 100644
--- a/COFF/Error.h
+++ b/COFF/Error.h
@@ -32,6 +32,23 @@ template <class T> T check(Expected<T> E, const Twine &Prefix) {
return std::move(*E);
}
+template <class T> T check(ErrorOr<T> EO) {
+ if (!EO)
+ fatal(EO.getError().message());
+ return std::move(*EO);
+}
+
+template <class T> T check(Expected<T> E) {
+ if (!E) {
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(E.takeError(), OS, "");
+ OS.flush();
+ fatal(Buf);
+ }
+ return std::move(*E);
+}
+
} // namespace coff
} // namespace lld
diff --git a/COFF/ICF.cpp b/COFF/ICF.cpp
index a2c5a90334d0..196fbe2610ea 100644
--- a/COFF/ICF.cpp
+++ b/COFF/ICF.cpp
@@ -7,43 +7,19 @@
//
//===----------------------------------------------------------------------===//
//
-// Identical COMDAT Folding is a feature to merge COMDAT sections not by
-// name (which is regular COMDAT handling) but by contents. If two COMDAT
-// sections have the same data, relocations, attributes, etc., then the two
-// are considered identical and merged by the linker. This optimization
-// makes outputs smaller.
+// ICF is short for Identical Code Folding. That is a size optimization to
+// identify and merge two or more read-only sections (typically functions)
+// that happened to have the same contents. It usually reduces output size
+// by a few percent.
//
-// ICF is theoretically a problem of reducing graphs by merging as many
-// identical subgraphs as possible, if we consider sections as vertices and
-// relocations as edges. This may be a bit more complicated problem than you
-// might think. The order of processing sections matters since merging two
-// sections can make other sections, whose relocations now point to the same
-// section, mergeable. Graphs may contain cycles, which is common in COFF.
-// We need a sophisticated algorithm to do this properly and efficiently.
+// On Windows, ICF is enabled by default.
//
-// What we do in this file is this. We split sections into groups. Sections
-// in the same group are considered identical.
-//
-// First, all sections are grouped by their "constant" values. Constant
-// values are values that are never changed by ICF, such as section contents,
-// section name, number of relocations, type and offset of each relocation,
-// etc. Because we do not care about some relocation targets in this step,
-// two sections in the same group may not be identical, but at least two
-// sections in different groups can never be identical.
-//
-// Then, we try to split each group by relocation targets. Relocations are
-// considered identical if and only if the relocation targets are in the
-// same group. Splitting a group may make more groups to be splittable,
-// because two relocations that were previously considered identical might
-// now point to different groups. We repeat this step until the convergence
-// is obtained.
-//
-// This algorithm is so-called "optimistic" algorithm described in
-// http://research.google.com/pubs/pub36912.html.
+// See ELF/ICF.cpp for the details about the algortihm.
//
//===----------------------------------------------------------------------===//
#include "Chunks.h"
+#include "Error.h"
#include "Symbols.h"
#include "lld/Core/Parallel.h"
#include "llvm/ADT/Hashing.h"
@@ -58,29 +34,34 @@ using namespace llvm;
namespace lld {
namespace coff {
-typedef std::vector<SectionChunk *>::iterator ChunkIterator;
-typedef bool (*Comparator)(const SectionChunk *, const SectionChunk *);
-
class ICF {
public:
void run(const std::vector<Chunk *> &V);
private:
- static uint64_t getHash(SectionChunk *C);
- static bool equalsConstant(const SectionChunk *A, const SectionChunk *B);
- static bool equalsVariable(const SectionChunk *A, const SectionChunk *B);
- bool forEachGroup(std::vector<SectionChunk *> &Chunks, Comparator Eq);
- bool segregate(ChunkIterator Begin, ChunkIterator End, Comparator Eq);
+ void segregate(size_t Begin, size_t End, bool Constant);
- std::atomic<uint64_t> NextID = { 1 };
-};
+ bool equalsConstant(const SectionChunk *A, const SectionChunk *B);
+ bool equalsVariable(const SectionChunk *A, const SectionChunk *B);
-// Entry point to ICF.
-void doICF(const std::vector<Chunk *> &Chunks) {
- ICF().run(Chunks);
-}
+ uint32_t getHash(SectionChunk *C);
+ bool isEligible(SectionChunk *C);
+
+ size_t findBoundary(size_t Begin, size_t End);
+
+ void forEachColorRange(size_t Begin, size_t End,
+ std::function<void(size_t, size_t)> Fn);
+
+ void forEachColor(std::function<void(size_t, size_t)> Fn);
+
+ std::vector<SectionChunk *> Chunks;
+ int Cnt = 0;
+ std::atomic<uint32_t> NextId = {1};
+ std::atomic<bool> Repeat = {false};
+};
-uint64_t ICF::getHash(SectionChunk *C) {
+// Returns a hash value for S.
+uint32_t ICF::getHash(SectionChunk *C) {
return hash_combine(C->getPermissions(),
hash_value(C->SectionName),
C->NumRelocs,
@@ -89,16 +70,44 @@ uint64_t ICF::getHash(SectionChunk *C) {
C->Checksum);
}
-bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
- if (A->AssocChildren.size() != B->AssocChildren.size() ||
- A->NumRelocs != B->NumRelocs) {
- return false;
+// Returns true if section S is subject of ICF.
+bool ICF::isEligible(SectionChunk *C) {
+ bool Global = C->Sym && C->Sym->isExternal();
+ bool Writable = C->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
+ return C->isCOMDAT() && C->isLive() && Global && !Writable;
+}
+
+// Split a range into smaller ranges by recoloring sections
+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
+ // second group.
+ auto Bound = std::stable_partition(
+ Chunks.begin() + Begin + 1, Chunks.begin() + End, [&](SectionChunk *S) {
+ if (Constant)
+ return equalsConstant(Chunks[Begin], S);
+ return equalsVariable(Chunks[Begin], S);
+ });
+ size_t Mid = Bound - Chunks.begin();
+
+ // 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;
+
+ // If we created a group, we need to iterate the main loop again.
+ if (Mid != End)
+ Repeat = true;
+
+ Begin = Mid;
}
+}
- // Compare associative sections.
- for (size_t I = 0, E = A->AssocChildren.size(); I != E; ++I)
- if (A->AssocChildren[I]->GroupID != B->AssocChildren[I]->GroupID)
- return false;
+// Compare "non-moving" part of two sections, namely everything
+// except relocation targets.
+bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
+ if (A->NumRelocs != B->NumRelocs)
+ return false;
// Compare relocations.
auto Eq = [&](const coff_relocation &R1, const coff_relocation &R2) {
@@ -106,14 +115,14 @@ bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
R1.VirtualAddress != R2.VirtualAddress) {
return false;
}
- SymbolBody *B1 = A->File->getSymbolBody(R1.SymbolTableIndex)->repl();
- SymbolBody *B2 = B->File->getSymbolBody(R2.SymbolTableIndex)->repl();
+ SymbolBody *B1 = A->File->getSymbolBody(R1.SymbolTableIndex);
+ SymbolBody *B2 = B->File->getSymbolBody(R2.SymbolTableIndex);
if (B1 == B2)
return true;
if (auto *D1 = dyn_cast<DefinedRegular>(B1))
if (auto *D2 = dyn_cast<DefinedRegular>(B2))
return D1->getValue() == D2->getValue() &&
- D1->getChunk()->GroupID == D2->getChunk()->GroupID;
+ D1->getChunk()->Color[Cnt % 2] == D2->getChunk()->Color[Cnt % 2];
return false;
};
if (!std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq))
@@ -128,54 +137,57 @@ bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
A->getContents() == B->getContents();
}
+// Compare "moving" part of two sections, namely relocation targets.
bool ICF::equalsVariable(const SectionChunk *A, const SectionChunk *B) {
- // Compare associative sections.
- for (size_t I = 0, E = A->AssocChildren.size(); I != E; ++I)
- if (A->AssocChildren[I]->GroupID != B->AssocChildren[I]->GroupID)
- return false;
-
// Compare relocations.
auto Eq = [&](const coff_relocation &R1, const coff_relocation &R2) {
- SymbolBody *B1 = A->File->getSymbolBody(R1.SymbolTableIndex)->repl();
- SymbolBody *B2 = B->File->getSymbolBody(R2.SymbolTableIndex)->repl();
+ SymbolBody *B1 = A->File->getSymbolBody(R1.SymbolTableIndex);
+ SymbolBody *B2 = B->File->getSymbolBody(R2.SymbolTableIndex);
if (B1 == B2)
return true;
if (auto *D1 = dyn_cast<DefinedRegular>(B1))
if (auto *D2 = dyn_cast<DefinedRegular>(B2))
- return D1->getChunk()->GroupID == D2->getChunk()->GroupID;
+ return D1->getChunk()->Color[Cnt % 2] == D2->getChunk()->Color[Cnt % 2];
return false;
};
return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq);
}
-bool ICF::segregate(ChunkIterator Begin, ChunkIterator End, Comparator Eq) {
- bool R = false;
- for (auto It = Begin;;) {
- SectionChunk *Head = *It;
- auto Bound = std::partition(It + 1, End, [&](SectionChunk *SC) {
- return Eq(Head, SC);
- });
- if (Bound == End)
- return R;
- uint64_t ID = NextID++;
- std::for_each(It, Bound, [&](SectionChunk *SC) { SC->GroupID = ID; });
- It = Bound;
- R = true;
+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])
+ return I;
+ return End;
+}
+
+void ICF::forEachColorRange(size_t Begin, size_t End,
+ std::function<void(size_t, size_t)> Fn) {
+ if (Begin > 0)
+ Begin = findBoundary(Begin - 1, End);
+
+ while (Begin < End) {
+ size_t Mid = findBoundary(Begin, Chunks.size());
+ Fn(Begin, Mid);
+ Begin = Mid;
}
}
-bool ICF::forEachGroup(std::vector<SectionChunk *> &Chunks, Comparator Eq) {
- bool R = false;
- for (auto It = Chunks.begin(), End = Chunks.end(); It != End;) {
- SectionChunk *Head = *It;
- auto Bound = std::find_if(It + 1, End, [&](SectionChunk *SC) {
- return SC->GroupID != Head->GroupID;
- });
- if (segregate(It, Bound, Eq))
- R = true;
- It = Bound;
+// Call Fn on each color group.
+void ICF::forEachColor(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);
+ return;
}
- return R;
+
+ // Split sections into 256 shards and call Fn in parallel.
+ 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);
+ });
+ forEachColorRange(Step * NumShards, Chunks.size(), Fn);
}
// Merge identical COMDAT sections.
@@ -183,62 +195,62 @@ bool ICF::forEachGroup(std::vector<SectionChunk *> &Chunks, Comparator Eq) {
// contents and relocations are all the same.
void ICF::run(const std::vector<Chunk *> &Vec) {
// Collect only mergeable sections and group by hash value.
- parallel_for_each(Vec.begin(), Vec.end(), [&](Chunk *C) {
- if (auto *SC = dyn_cast<SectionChunk>(C)) {
- bool Global = SC->Sym && SC->Sym->isExternal();
- bool Writable = SC->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
- if (SC->isCOMDAT() && SC->isLive() && Global && !Writable)
- SC->GroupID = getHash(SC) | (uint64_t(1) << 63);
- }
- });
- std::vector<SectionChunk *> Chunks;
for (Chunk *C : Vec) {
- if (auto *SC = dyn_cast<SectionChunk>(C)) {
- if (SC->GroupID) {
- Chunks.push_back(SC);
- } else {
- SC->GroupID = NextID++;
- }
+ auto *SC = dyn_cast<SectionChunk>(C);
+ if (!SC)
+ continue;
+
+ if (isEligible(SC)) {
+ // Set MSB to 1 to avoid collisions with non-hash colors.
+ SC->Color[0] = getHash(SC) | (1 << 31);
+ Chunks.push_back(SC);
+ } else {
+ SC->Color[0] = NextId++;
}
}
+ if (Chunks.empty())
+ return;
+
// From now on, sections in Chunks are ordered so that sections in
// the same group are consecutive in the vector.
- std::sort(Chunks.begin(), Chunks.end(),
- [](SectionChunk *A, SectionChunk *B) {
- return A->GroupID < B->GroupID;
- });
-
- // Split groups until we get a convergence.
- int Cnt = 1;
- forEachGroup(Chunks, equalsConstant);
-
- for (;;) {
- if (!forEachGroup(Chunks, equalsVariable))
- break;
+ std::stable_sort(Chunks.begin(), Chunks.end(),
+ [](SectionChunk *A, SectionChunk *B) {
+ return A->Color[0] < B->Color[0];
+ });
+
+ // Compare static contents and assign unique IDs for each static content.
+ forEachColor([&](size_t Begin, size_t End) { segregate(Begin, End, true); });
+ ++Cnt;
+
+ // Split groups by comparing relocations until convergence is obtained.
+ do {
+ Repeat = false;
+ forEachColor(
+ [&](size_t Begin, size_t End) { segregate(Begin, End, false); });
++Cnt;
- }
+ } while (Repeat);
+
if (Config->Verbose)
- llvm::outs() << "\nICF needed " << Cnt << " iterations.\n";
-
- // Merge sections in the same group.
- for (auto It = Chunks.begin(), End = Chunks.end(); It != End;) {
- SectionChunk *Head = *It++;
- auto Bound = std::find_if(It, End, [&](SectionChunk *SC) {
- return Head->GroupID != SC->GroupID;
- });
- if (It == Bound)
- continue;
+ outs() << "\nICF needed " << Cnt << " iterations\n";
+
+ // Merge sections in the same colors.
+ forEachColor([&](size_t Begin, size_t End) {
+ if (End - Begin == 1)
+ return;
+
if (Config->Verbose)
- llvm::outs() << "Selected " << Head->getDebugName() << "\n";
- while (It != Bound) {
- SectionChunk *SC = *It++;
+ outs() << "Selected " << Chunks[Begin]->getDebugName() << "\n";
+ for (size_t I = Begin + 1; I < End; ++I) {
if (Config->Verbose)
- llvm::outs() << " Removed " << SC->getDebugName() << "\n";
- Head->replace(SC);
+ outs() << " Removed " << Chunks[I]->getDebugName() << "\n";
+ Chunks[Begin]->replace(Chunks[I]);
}
- }
+ });
}
+// Entry point to ICF.
+void doICF(const std::vector<Chunk *> &Chunks) { ICF().run(Chunks); }
+
} // namespace coff
} // namespace lld
diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp
index ff26826371fa..0a97c2185f89 100644
--- a/COFF/InputFiles.cpp
+++ b/COFF/InputFiles.cpp
@@ -7,11 +7,15 @@
//
//===----------------------------------------------------------------------===//
+#include "InputFiles.h"
#include "Chunks.h"
#include "Config.h"
+#include "Driver.h"
#include "Error.h"
-#include "InputFiles.h"
+#include "Memory.h"
+#include "SymbolTable.h"
#include "Symbols.h"
+#include "llvm-c/lto.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Triple.h"
#include "llvm/ADT/Twine.h"
@@ -26,88 +30,58 @@
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Target/TargetOptions.h"
-#include "llvm-c/lto.h"
#include <cstring>
#include <system_error>
#include <utility>
+using namespace llvm;
using namespace llvm::COFF;
using namespace llvm::object;
using namespace llvm::support::endian;
using llvm::Triple;
using llvm::support::ulittle32_t;
+using llvm::sys::fs::file_magic;
+using llvm::sys::fs::identify_magic;
namespace lld {
namespace coff {
-int InputFile::NextIndex = 0;
-llvm::LLVMContext BitcodeFile::Context;
-
-// Returns the last element of a path, which is supposed to be a filename.
-static StringRef getBasename(StringRef Path) {
- size_t Pos = Path.find_last_of("\\/");
- if (Pos == StringRef::npos)
- return Path;
- return Path.substr(Pos + 1);
-}
+LLVMContext BitcodeFile::Context;
-// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".
-std::string InputFile::getShortName() {
- if (ParentName == "")
- return getName().lower();
- std::string Res = (getBasename(ParentName) + "(" +
- getBasename(getName()) + ")").str();
- return StringRef(Res).lower();
-}
+ArchiveFile::ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
void ArchiveFile::parse() {
// Parse a MemoryBufferRef as an archive file.
- File = check(Archive::create(MB), "failed to parse static library");
-
- // Allocate a buffer for Lazy objects.
- size_t NumSyms = File->getNumberOfSymbols();
- LazySymbols.reserve(NumSyms);
+ File = check(Archive::create(MB), toString(this));
// Read the symbol table to construct Lazy objects.
for (const Archive::Symbol &Sym : File->symbols())
- LazySymbols.emplace_back(this, Sym);
-
- // Seen is a map from member files to boolean values. Initially
- // all members are mapped to false, which indicates all these files
- // are not read yet.
- Error Err;
- for (auto &Child : File->children(Err))
- Seen[Child.getChildOffset()].clear();
- if (Err)
- fatal(Err, "failed to parse static library");
+ Symtab->addLazy(this, Sym);
}
// Returns a buffer pointing to a member file containing a given symbol.
-// This function is thread-safe.
-MemoryBufferRef ArchiveFile::getMember(const Archive::Symbol *Sym) {
+void ArchiveFile::addMember(const Archive::Symbol *Sym) {
const Archive::Child &C =
check(Sym->getMember(),
"could not get the member for symbol " + Sym->getName());
// Return an empty buffer if we have already returned the same buffer.
- if (Seen[C.getChildOffset()].test_and_set())
- return MemoryBufferRef();
- return check(C.getMemoryBufferRef(),
- "could not get the buffer for the member defining symbol " +
- Sym->getName());
+ if (!Seen.insert(C.getChildOffset()).second)
+ return;
+
+ Driver->enqueueArchiveMember(C, Sym->getName(), getName());
}
void ObjectFile::parse() {
// Parse a memory buffer as a COFF file.
- std::unique_ptr<Binary> Bin =
- check(createBinary(MB), "failed to parse object file");
+ std::unique_ptr<Binary> Bin = check(createBinary(MB), toString(this));
if (auto *Obj = dyn_cast<COFFObjectFile>(Bin.get())) {
Bin.release();
COFFObj.reset(Obj);
} else {
- fatal(getName() + " is not a COFF file");
+ fatal(toString(this) + " is not a COFF file");
}
// Read section and symbol tables.
@@ -137,13 +111,28 @@ void ObjectFile::initializeChunks() {
Directives = std::string((const char *)Data.data(), Data.size());
continue;
}
- // Skip non-DWARF debug info. MSVC linker converts the sections into
- // a PDB file, but we don't support that.
- if (Name == ".debug" || Name.startswith(".debug$"))
- continue;
- // We want to preserve DWARF debug sections only when /debug is on.
+
+ // Object files may have DWARF debug info or MS CodeView debug info
+ // (or both).
+ //
+ // DWARF sections don't need any special handling from the perspective
+ // of the linker; they are just a data section containing relocations.
+ // We can just link them to complete debug info.
+ //
+ // CodeView needs a linker support. We need to interpret and debug
+ // info, and then write it to a separate .pdb file.
+
+ // Ignore debug info unless /debug is given.
if (!Config->Debug && Name.startswith(".debug"))
continue;
+
+ // CodeView sections are stored to a different vector because they are
+ // not linked in the regular manner.
+ if (Name == ".debug" || Name.startswith(".debug$")) {
+ DebugChunks.push_back(new (Alloc) SectionChunk(this, Sec));
+ continue;
+ }
+
if (Sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
continue;
auto *C = new (Alloc) SectionChunk(this, Sec);
@@ -156,12 +145,14 @@ void ObjectFile::initializeSymbols() {
uint32_t NumSymbols = COFFObj->getNumberOfSymbols();
SymbolBodies.reserve(NumSymbols);
SparseSymbolBodies.resize(NumSymbols);
- llvm::SmallVector<std::pair<Undefined *, uint32_t>, 8> WeakAliases;
+ SmallVector<std::pair<SymbolBody *, uint32_t>, 8> WeakAliases;
int32_t LastSectionNumber = 0;
for (uint32_t I = 0; I < NumSymbols; ++I) {
// Get a COFFSymbolRef object.
- COFFSymbolRef Sym =
- check(COFFObj->getSymbol(I), "broken object file: " + getName());
+ ErrorOr<COFFSymbolRef> SymOrErr = COFFObj->getSymbol(I);
+ if (!SymOrErr)
+ fatal(SymOrErr.getError(), "broken object file: " + toString(this));
+ COFFSymbolRef Sym = *SymOrErr;
const void *AuxP = nullptr;
if (Sym.getNumberOfAuxSymbols())
@@ -175,7 +166,7 @@ void ObjectFile::initializeSymbols() {
Body = createUndefined(Sym);
uint32_t TagIndex =
static_cast<const coff_aux_weak_external *>(AuxP)->TagIndex;
- WeakAliases.emplace_back((Undefined *)Body, TagIndex);
+ WeakAliases.emplace_back(Body, TagIndex);
} else {
Body = createDefined(Sym, AuxP, IsFirst);
}
@@ -186,23 +177,30 @@ void ObjectFile::initializeSymbols() {
I += Sym.getNumberOfAuxSymbols();
LastSectionNumber = Sym.getSectionNumber();
}
- for (auto WeakAlias : WeakAliases)
- WeakAlias.first->WeakAlias = SparseSymbolBodies[WeakAlias.second];
+ for (auto WeakAlias : WeakAliases) {
+ auto *U = dyn_cast<Undefined>(WeakAlias.first);
+ if (!U)
+ continue;
+ // Report an error if two undefined symbols have different weak aliases.
+ if (U->WeakAlias && U->WeakAlias != SparseSymbolBodies[WeakAlias.second])
+ Symtab->reportDuplicate(U->symbol(), this);
+ U->WeakAlias = SparseSymbolBodies[WeakAlias.second];
+ }
}
-Undefined *ObjectFile::createUndefined(COFFSymbolRef Sym) {
+SymbolBody *ObjectFile::createUndefined(COFFSymbolRef Sym) {
StringRef Name;
COFFObj->getSymbolName(Sym, Name);
- return new (Alloc) Undefined(Name);
+ return Symtab->addUndefined(Name, this, Sym.isWeakExternal())->body();
}
-Defined *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
- bool IsFirst) {
+SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
+ bool IsFirst) {
StringRef Name;
if (Sym.isCommon()) {
auto *C = new (Alloc) CommonChunk(Sym);
Chunks.push_back(C);
- return new (Alloc) DefinedCommon(this, Sym, C);
+ return Symtab->addCommon(this, Sym, C)->body();
}
if (Sym.isAbsolute()) {
COFFObj->getSymbolName(Sym, Name);
@@ -215,7 +213,10 @@ Defined *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
SEHCompat = true;
return nullptr;
}
- return new (Alloc) DefinedAbsolute(Name, Sym);
+ if (Sym.isExternal())
+ return Symtab->addAbsolute(Name, Sym)->body();
+ else
+ return new (Alloc) DefinedAbsolute(Name, Sym);
}
int32_t SectionNumber = Sym.getSectionNumber();
if (SectionNumber == llvm::COFF::IMAGE_SYM_DEBUG)
@@ -223,12 +224,12 @@ Defined *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
// Reserved sections numbers don't have contents.
if (llvm::COFF::isReservedSectionNumber(SectionNumber))
- fatal("broken object file: " + getName());
+ fatal("broken object file: " + toString(this));
// This symbol references a section which is not present in the section
// header.
if ((uint32_t)SectionNumber >= SparseChunks.size())
- fatal("broken object file: " + getName());
+ fatal("broken object file: " + toString(this));
// Nothing else to do without a section chunk.
auto *SC = cast_or_null<SectionChunk>(SparseChunks[SectionNumber]);
@@ -245,7 +246,11 @@ Defined *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
SC->Checksum = Aux->CheckSum;
}
- auto *B = new (Alloc) DefinedRegular(this, Sym, SC);
+ DefinedRegular *B;
+ if (Sym.isExternal())
+ B = cast<DefinedRegular>(Symtab->addRegular(this, Sym, SC)->body());
+ else
+ B = new (Alloc) DefinedRegular(this, Sym, SC);
if (SC->isCOMDAT() && Sym.getValue() == 0 && !AuxP)
SC->setSymbol(B);
@@ -307,28 +312,29 @@ void ImportFile::parse() {
ExtName = ExtName.substr(0, ExtName.find('@'));
break;
}
- ImpSym = new (Alloc) DefinedImportData(DLLName, ImpName, ExtName, Hdr);
- SymbolBodies.push_back(ImpSym);
+
+ this->Hdr = Hdr;
+ ExternalName = ExtName;
+
+ ImpSym = cast<DefinedImportData>(
+ Symtab->addImportData(ImpName, 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
// DLL functions just like regular non-DLL functions.)
if (Hdr->getType() != llvm::COFF::IMPORT_CODE)
return;
- ThunkSym = new (Alloc) DefinedImportThunk(Name, ImpSym, Hdr->Machine);
- SymbolBodies.push_back(ThunkSym);
+ ThunkSym = cast<DefinedImportThunk>(
+ Symtab->addImportThunk(Name, ImpSym, Hdr->Machine)->body());
}
void BitcodeFile::parse() {
- // Usually parse() is thread-safe, but bitcode file is an exception.
- std::lock_guard<std::mutex> Lock(Mu);
-
Context.enableDebugTypeODRUniquing();
ErrorOr<std::unique_ptr<LTOModule>> ModOrErr = LTOModule::createFromBuffer(
Context, MB.getBufferStart(), MB.getBufferSize(), llvm::TargetOptions());
M = check(std::move(ModOrErr), "could not create LTO module");
- llvm::StringSaver Saver(Alloc);
+ StringSaver Saver(Alloc);
for (unsigned I = 0, E = M->getSymbolCount(); I != E; ++I) {
lto_symbol_attributes Attrs = M->getSymbolAttributes(I);
if ((Attrs & LTO_SYMBOL_SCOPE_MASK) == LTO_SYMBOL_SCOPE_INTERNAL)
@@ -337,15 +343,15 @@ void BitcodeFile::parse() {
StringRef SymName = Saver.save(M->getSymbolName(I));
int SymbolDef = Attrs & LTO_SYMBOL_DEFINITION_MASK;
if (SymbolDef == LTO_SYMBOL_DEFINITION_UNDEFINED) {
- SymbolBodies.push_back(new (Alloc) Undefined(SymName));
+ SymbolBodies.push_back(Symtab->addUndefined(SymName, this, false)->body());
} else {
bool Replaceable =
(SymbolDef == LTO_SYMBOL_DEFINITION_TENTATIVE || // common
(Attrs & LTO_SYMBOL_COMDAT) || // comdat
(SymbolDef == LTO_SYMBOL_DEFINITION_WEAK && // weak external
(Attrs & LTO_SYMBOL_ALIAS)));
- SymbolBodies.push_back(new (Alloc) DefinedBitcode(this, SymName,
- Replaceable));
+ SymbolBodies.push_back(
+ Symtab->addBitcode(this, SymName, Replaceable)->body());
}
}
@@ -367,7 +373,26 @@ MachineTypes BitcodeFile::getMachineType() {
}
}
-std::mutex BitcodeFile::Mu;
+// Returns the last element of a path, which is supposed to be a filename.
+static StringRef getBasename(StringRef Path) {
+ size_t Pos = Path.find_last_of("\\/");
+ if (Pos == StringRef::npos)
+ return Path;
+ return Path.substr(Pos + 1);
+}
+
+// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".
+std::string toString(InputFile *File) {
+ if (!File)
+ return "(internal)";
+ if (File->ParentName.empty())
+ return File->getName().lower();
+
+ std::string Res =
+ (getBasename(File->ParentName) + "(" + getBasename(File->getName()) + ")")
+ .str();
+ return StringRef(Res).lower();
+}
} // namespace coff
} // namespace lld
diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h
index 0ec01b5075f9..498a1743e985 100644
--- a/COFF/InputFiles.h
+++ b/COFF/InputFiles.h
@@ -12,13 +12,13 @@
#include "lld/Core/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/LTO/legacy/LTOModule.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/StringSaver.h"
#include <memory>
-#include <mutex>
#include <set>
#include <vector>
@@ -31,6 +31,7 @@ using llvm::COFF::MachineTypes;
using llvm::object::Archive;
using llvm::object::COFFObjectFile;
using llvm::object::COFFSymbolRef;
+using llvm::object::coff_import_header;
using llvm::object::coff_section;
class Chunk;
@@ -38,6 +39,8 @@ class Defined;
class DefinedImportData;
class DefinedImportThunk;
class Lazy;
+class SectionChunk;
+struct Symbol;
class SymbolBody;
class Undefined;
@@ -51,67 +54,44 @@ public:
// Returns the filename.
StringRef getName() { return MB.getBufferIdentifier(); }
- // Returns symbols defined by this file.
- virtual std::vector<SymbolBody *> &getSymbols() = 0;
-
// Reads a file (the constructor doesn't do that).
virtual void parse() = 0;
// Returns the CPU type this file was compiled to.
virtual MachineTypes getMachineType() { return IMAGE_FILE_MACHINE_UNKNOWN; }
- // Returns a short, human-friendly filename. If this is a member of
- // an archive file, a returned value includes parent's filename.
- // Used for logging or debugging.
- std::string getShortName();
-
- // Sets a parent filename if this file is created from an archive.
- void setParentName(StringRef N) { ParentName = N; }
+ // An archive file name if this file is created from an archive.
+ StringRef ParentName;
// Returns .drectve section contents if exist.
StringRef getDirectives() { return StringRef(Directives).trim(); }
- // Each file has a unique index. The index number is used to
- // resolve ties in symbol resolution.
- int Index;
- static int NextIndex;
-
protected:
- InputFile(Kind K, MemoryBufferRef M)
- : Index(NextIndex++), MB(M), FileKind(K) {}
+ InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {}
MemoryBufferRef MB;
std::string Directives;
private:
const Kind FileKind;
- StringRef ParentName;
};
// .lib or .a file.
class ArchiveFile : public InputFile {
public:
- explicit ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
+ explicit ArchiveFile(MemoryBufferRef M);
static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
void parse() override;
- // Returns a memory buffer for a given symbol. An empty memory buffer
- // is returned if we have already returned the same memory buffer.
- // (So that we don't instantiate same members more than once.)
- MemoryBufferRef getMember(const Archive::Symbol *Sym);
-
- llvm::MutableArrayRef<Lazy> getLazySymbols() { return LazySymbols; }
-
- // All symbols returned by ArchiveFiles are of Lazy type.
- std::vector<SymbolBody *> &getSymbols() override {
- llvm_unreachable("internal fatal");
- }
+ // Enqueues an archive member load for the given symbol. If we've already
+ // enqueued a load for the same archive member, this function does nothing,
+ // which ensures that we don't load the same member more than once.
+ void addMember(const Archive::Symbol *Sym);
private:
std::unique_ptr<Archive> File;
std::string Filename;
- std::vector<Lazy> LazySymbols;
- std::map<uint64_t, std::atomic_flag> Seen;
+ llvm::DenseSet<uint64_t> Seen;
};
// .obj or .o file. This may be a member of an archive file.
@@ -122,7 +102,8 @@ public:
void parse() override;
MachineTypes getMachineType() override;
std::vector<Chunk *> &getChunks() { return Chunks; }
- std::vector<SymbolBody *> &getSymbols() override { return SymbolBodies; }
+ std::vector<SectionChunk *> &getDebugChunks() { return DebugChunks; }
+ std::vector<SymbolBody *> &getSymbols() { return SymbolBodies; }
// Returns a SymbolBody object for the SymbolIndex'th symbol in the
// underlying object file.
@@ -146,8 +127,8 @@ private:
void initializeSymbols();
void initializeSEH();
- Defined *createDefined(COFFSymbolRef Sym, const void *Aux, bool IsFirst);
- Undefined *createUndefined(COFFSymbolRef Sym);
+ SymbolBody *createDefined(COFFSymbolRef Sym, const void *Aux, bool IsFirst);
+ SymbolBody *createUndefined(COFFSymbolRef Sym);
std::unique_ptr<COFFObjectFile> COFFObj;
llvm::BumpPtrAllocator Alloc;
@@ -157,6 +138,9 @@ private:
// chunks and non-section chunks for common symbols.
std::vector<Chunk *> Chunks;
+ // CodeView debug info sections.
+ std::vector<SectionChunk *> DebugChunks;
+
// This vector contains the same chunks as Chunks, but they are
// indexed such that you can get a SectionChunk by section index.
// Nonexistent section indices are filled with null pointers.
@@ -182,7 +166,6 @@ public:
explicit ImportFile(MemoryBufferRef M)
: InputFile(ImportKind, M), StringAlloc(StringAllocAux) {}
static bool classof(const InputFile *F) { return F->kind() == ImportKind; }
- std::vector<SymbolBody *> &getSymbols() override { return SymbolBodies; }
DefinedImportData *ImpSym = nullptr;
DefinedImportThunk *ThunkSym = nullptr;
@@ -191,10 +174,14 @@ public:
private:
void parse() override;
- std::vector<SymbolBody *> SymbolBodies;
llvm::BumpPtrAllocator Alloc;
llvm::BumpPtrAllocator StringAllocAux;
llvm::StringSaver StringAlloc;
+
+public:
+ StringRef ExternalName;
+ const coff_import_header *Hdr;
+ Chunk *Location = nullptr;
};
// Used for LTO.
@@ -202,7 +189,7 @@ class BitcodeFile : public InputFile {
public:
explicit BitcodeFile(MemoryBufferRef M) : InputFile(BitcodeKind, M) {}
static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
- std::vector<SymbolBody *> &getSymbols() override { return SymbolBodies; }
+ std::vector<SymbolBody *> &getSymbols() { return SymbolBodies; }
MachineTypes getMachineType() override;
std::unique_ptr<LTOModule> takeModule() { return std::move(M); }
@@ -214,9 +201,10 @@ private:
std::vector<SymbolBody *> SymbolBodies;
llvm::BumpPtrAllocator Alloc;
std::unique_ptr<LTOModule> M;
- static std::mutex Mu;
};
+std::string toString(InputFile *File);
+
} // namespace coff
} // namespace lld
diff --git a/COFF/Librarian.cpp b/COFF/Librarian.cpp
index 25fb4a87b3eb..4c597fad7345 100644
--- a/COFF/Librarian.cpp
+++ b/COFF/Librarian.cpp
@@ -54,7 +54,7 @@ static uint16_t getImgRelRelocation() {
}
}
-template <class T> void append(std::vector<uint8_t> &B, const T &Data) {
+template <class T> static void append(std::vector<uint8_t> &B, const T &Data) {
size_t S = B.size();
B.resize(S + sizeof(T));
memcpy(&B[S], &Data, sizeof(T));
@@ -352,15 +352,16 @@ ObjectFactory::createNullImportDescriptor(std::vector<uint8_t> &Buffer) {
NewArchiveMember ObjectFactory::createNullThunk(std::vector<uint8_t> &Buffer) {
static const uint32_t NumberOfSections = 2;
static const uint32_t NumberOfSymbols = 1;
+ uint32_t VASize = is32bit() ? 4 : 8;
// COFF Header
coff_file_header Header{
u16(Config->Machine), u16(NumberOfSections), u32(0),
u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) +
// .idata$5
- sizeof(export_address_table_entry) +
+ VASize +
// .idata$4
- sizeof(export_address_table_entry)),
+ VASize),
u32(NumberOfSymbols), u16(0),
u16(is32bit() ? IMAGE_FILE_32BIT_MACHINE : 0),
};
@@ -371,36 +372,40 @@ NewArchiveMember ObjectFactory::createNullThunk(std::vector<uint8_t> &Buffer) {
{{'.', 'i', 'd', 'a', 't', 'a', '$', '5'},
u32(0),
u32(0),
- u32(sizeof(export_address_table_entry)),
+ u32(VASize),
u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)),
u32(0),
u32(0),
u16(0),
u16(0),
- u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA |
- IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)},
+ u32((is32bit() ? IMAGE_SCN_ALIGN_4BYTES : IMAGE_SCN_ALIGN_8BYTES) |
+ IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ |
+ IMAGE_SCN_MEM_WRITE)},
{{'.', 'i', 'd', 'a', 't', 'a', '$', '4'},
u32(0),
u32(0),
- u32(sizeof(export_address_table_entry)),
+ u32(VASize),
u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) +
- sizeof(export_address_table_entry)),
+ VASize),
u32(0),
u32(0),
u16(0),
u16(0),
- u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA |
- IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)},
+ u32((is32bit() ? IMAGE_SCN_ALIGN_4BYTES : IMAGE_SCN_ALIGN_8BYTES) |
+ IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ |
+ IMAGE_SCN_MEM_WRITE)},
};
append(Buffer, SectionTable);
- // .idata$5
- static const export_address_table_entry ILT{u32(0)};
- append(Buffer, ILT);
+ // .idata$5, ILT
+ append(Buffer, u32(0));
+ if (!is32bit())
+ append(Buffer, u32(0));
- // .idata$4
- static const export_address_table_entry IAT{u32(0)};
- append(Buffer, IAT);
+ // .idata$4, IAT
+ append(Buffer, u32(0));
+ if (!is32bit())
+ append(Buffer, u32(0));
// Symbol Table
coff_symbol16 SymbolTable[NumberOfSymbols] = {
@@ -458,7 +463,7 @@ void lld::coff::writeImportLibrary() {
std::vector<NewArchiveMember> Members;
std::string Path = getImplibPath();
- std::string DLLName = llvm::sys::path::filename(Config->OutputFile);
+ std::string DLLName = sys::path::filename(Config->OutputFile);
ObjectFactory OF(DLLName);
std::vector<uint8_t> ImportDescriptor;
diff --git a/COFF/MarkLive.cpp b/COFF/MarkLive.cpp
index 0870986ad81a..0156d238b672 100644
--- a/COFF/MarkLive.cpp
+++ b/COFF/MarkLive.cpp
@@ -38,8 +38,8 @@ void markLive(const std::vector<Chunk *> &Chunks) {
};
// Add GC root chunks.
- for (Undefined *U : Config->GCRoot)
- if (auto *D = dyn_cast<DefinedRegular>(U->repl()))
+ for (SymbolBody *B : Config->GCRoot)
+ if (auto *D = dyn_cast<DefinedRegular>(B))
Enqueue(D->getChunk());
while (!Worklist.empty()) {
@@ -48,7 +48,7 @@ void markLive(const std::vector<Chunk *> &Chunks) {
// Mark all symbols listed in the relocation table for this section.
for (SymbolBody *S : SC->symbols())
- if (auto *D = dyn_cast<DefinedRegular>(S->repl()))
+ if (auto *D = dyn_cast<DefinedRegular>(S))
Enqueue(D->getChunk());
// Mark associative sections if any.
diff --git a/COFF/Memory.h b/COFF/Memory.h
new file mode 100644
index 000000000000..526f11344a09
--- /dev/null
+++ b/COFF/Memory.h
@@ -0,0 +1,52 @@
+//===- Memory.h -------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// See ELF/Memory.h
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_COFF_MEMORY_H
+#define LLD_COFF_MEMORY_H
+
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/StringSaver.h"
+#include <vector>
+
+namespace lld {
+namespace coff {
+
+extern llvm::BumpPtrAllocator BAlloc;
+extern llvm::StringSaver Saver;
+
+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;
+};
+
+template <typename T, typename... U> T *make(U &&... Args) {
+ static SpecificAlloc<T> Alloc;
+ return new (Alloc.Alloc.Allocate()) T(std::forward<U>(Args)...);
+}
+
+inline void freeArena() {
+ for (SpecificAllocBase *Alloc : SpecificAllocBase::Instances)
+ Alloc->reset();
+ BAlloc.Reset();
+}
+}
+}
+
+#endif
diff --git a/COFF/ModuleDef.cpp b/COFF/ModuleDef.cpp
index 5e393f45d184..a273b6f535db 100644
--- a/COFF/ModuleDef.cpp
+++ b/COFF/ModuleDef.cpp
@@ -18,6 +18,7 @@
#include "Config.h"
#include "Error.h"
+#include "Memory.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/StringSaver.h"
@@ -113,7 +114,7 @@ private:
class Parser {
public:
- explicit Parser(StringRef S, StringSaver *A) : Lex(S), Alloc(A) {}
+ explicit Parser(StringRef S) : Lex(S) {}
void parse() {
do {
@@ -197,9 +198,9 @@ private:
if (Config->Machine == I386) {
if (!isDecorated(E.Name))
- E.Name = Alloc->save("_" + E.Name);
+ E.Name = Saver.save("_" + E.Name);
if (!E.ExtName.empty() && !isDecorated(E.ExtName))
- E.ExtName = Alloc->save("_" + E.ExtName);
+ E.ExtName = Saver.save("_" + E.ExtName);
}
for (;;) {
@@ -278,14 +279,11 @@ private:
Lexer Lex;
Token Tok;
std::vector<Token> Stack;
- StringSaver *Alloc;
};
} // anonymous namespace
-void parseModuleDefs(MemoryBufferRef MB, StringSaver *Alloc) {
- Parser(MB.getBuffer(), Alloc).parse();
-}
+void parseModuleDefs(MemoryBufferRef MB) { Parser(MB.getBuffer()).parse(); }
} // namespace coff
} // namespace lld
diff --git a/COFF/Options.td b/COFF/Options.td
index e5c9c5b4635b..9dfbcc8e188c 100644
--- a/COFF/Options.td
+++ b/COFF/Options.td
@@ -27,6 +27,7 @@ def failifmismatch : P<"failifmismatch", "">;
def heap : P<"heap", "Size of the heap">;
def implib : P<"implib", "Import library name">;
def libpath : P<"libpath", "Additional library search path">;
+def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">;
def machine : P<"machine", "Specify target platform">;
def merge : P<"merge", "Combine sections">;
def mllvm : P<"mllvm", "Options to pass to LLVM">;
@@ -61,7 +62,9 @@ def deffile : Joined<["/", "-"], "def:">,
HelpText<"Use module-definition file">;
def debug : F<"debug">, HelpText<"Embed a symbol table in the image">;
+def debugtype : P<"debugtype", "Debug Info Options">;
def dll : F<"dll">, HelpText<"Create a DLL">;
+def driver : P<"driver", "Generate a Windows NT Kernel Mode Driver">;
def nodefaultlib_all : F<"nodefaultlib">;
def noentry : F<"noentry">;
def profile : F<"profile">;
@@ -91,7 +94,10 @@ def help_q : Flag<["/?", "-?"], "">, Alias<help>;
def nosymtab : F<"nosymtab">;
// Flags for debugging
-def lldmap : Joined<["/", "-"], "lldmap:">;
+def debugpdb : F<"debugpdb">;
+def dumppdb : Joined<["/", "-"], "dumppdb">;
+def lldmap : F<"lldmap">;
+def lldmap_file : Joined<["/", "-"], "lldmap:">;
//==============================================================================
// The flags below do nothing. They are defined only for link.exe compatibility.
diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp
index 7606ccc680d3..56d5a3651143 100644
--- a/COFF/PDB.cpp
+++ b/COFF/PDB.cpp
@@ -7,55 +7,187 @@
//
//===----------------------------------------------------------------------===//
-#include "Driver.h"
+#include "PDB.h"
+#include "Chunks.h"
+#include "Config.h"
#include "Error.h"
+#include "SymbolTable.h"
#include "Symbols.h"
+#include "llvm/DebugInfo/CodeView/SymbolDumper.h"
+#include "llvm/DebugInfo/CodeView/TypeDumper.h"
+#include "llvm/DebugInfo/MSF/ByteStream.h"
+#include "llvm/DebugInfo/MSF/MSFBuilder.h"
+#include "llvm/DebugInfo/MSF/MSFCommon.h"
+#include "llvm/DebugInfo/PDB/Raw/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Raw/DbiStreamBuilder.h"
+#include "llvm/DebugInfo/PDB/Raw/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h"
+#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Raw/PDBFileBuilder.h"
+#include "llvm/DebugInfo/PDB/Raw/TpiStream.h"
+#include "llvm/DebugInfo/PDB/Raw/TpiStreamBuilder.h"
+#include "llvm/Object/COFF.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/ScopedPrinter.h"
#include <memory>
+using namespace lld;
+using namespace lld::coff;
using namespace llvm;
+using namespace llvm::codeview;
using namespace llvm::support;
using namespace llvm::support::endian;
-const int PageSize = 4096;
-const uint8_t Magic[32] = "Microsoft C/C++ MSF 7.00\r\n\032DS\0\0";
-
-namespace {
-struct PDBHeader {
- uint8_t Magic[32];
- ulittle32_t PageSize;
- ulittle32_t FpmPage;
- ulittle32_t PageCount;
- ulittle32_t RootSize;
- ulittle32_t Reserved;
- ulittle32_t RootPointer;
-};
-}
-
-void lld::coff::createPDB(StringRef Path) {
- // Create a file.
- size_t FileSize = PageSize * 3;
- ErrorOr<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
- FileOutputBuffer::create(Path, FileSize);
- if (auto EC = BufferOrErr.getError())
- fatal(EC, "failed to open " + Path);
- std::unique_ptr<FileOutputBuffer> Buffer = std::move(*BufferOrErr);
-
- // Write the file header.
- uint8_t *Buf = Buffer->getBufferStart();
- auto *Hdr = reinterpret_cast<PDBHeader *>(Buf);
- memcpy(Hdr->Magic, Magic, sizeof(Magic));
- Hdr->PageSize = PageSize;
- // I don't know what FpmPage field means, but it must not be 0.
- Hdr->FpmPage = 1;
- Hdr->PageCount = FileSize / PageSize;
- // Root directory is empty, containing only the length field.
- Hdr->RootSize = 4;
- // Root directory is on page 1.
- Hdr->RootPointer = 1;
-
- // Write the root directory. Root stream is on page 2.
- write32le(Buf + PageSize, 2);
- Buffer->commit();
+using llvm::object::coff_section;
+
+static ExitOnError ExitOnErr;
+
+// Returns a list of all SectionChunks.
+static std::vector<coff_section> getInputSections(SymbolTable *Symtab) {
+ std::vector<coff_section> V;
+ for (Chunk *C : Symtab->getChunks())
+ if (auto *SC = dyn_cast<SectionChunk>(C))
+ V.push_back(*SC->Header);
+ return V;
+}
+
+static SectionChunk *findByName(std::vector<SectionChunk *> &Sections,
+ StringRef Name) {
+ for (SectionChunk *C : Sections)
+ if (C->getSectionName() == Name)
+ return C;
+ return nullptr;
+}
+
+static ArrayRef<uint8_t> getDebugT(ObjectFile *File) {
+ SectionChunk *Sec = findByName(File->getDebugChunks(), ".debug$T");
+ if (!Sec)
+ return {};
+
+ // First 4 bytes are section magic.
+ ArrayRef<uint8_t> Data = Sec->getContents();
+ if (Data.size() < 4)
+ fatal(".debug$T too short");
+ if (read32le(Data.data()) != COFF::DEBUG_SECTION_MAGIC)
+ fatal(".debug$T has an invalid magic");
+ return Data.slice(4);
+}
+
+static void dumpDebugT(ScopedPrinter &W, ObjectFile *File) {
+ ArrayRef<uint8_t> Data = getDebugT(File);
+ if (Data.empty())
+ return;
+
+ msf::ByteStream Stream(Data);
+ CVTypeDumper TypeDumper(&W, false);
+ if (auto EC = TypeDumper.dump(Data))
+ fatal(EC, "CVTypeDumper::dump failed");
+}
+
+static void dumpDebugS(ScopedPrinter &W, ObjectFile *File) {
+ SectionChunk *Sec = findByName(File->getDebugChunks(), ".debug$S");
+ if (!Sec)
+ return;
+
+ msf::ByteStream Stream(Sec->getContents());
+ CVSymbolArray Symbols;
+ msf::StreamReader Reader(Stream);
+ if (auto EC = Reader.readArray(Symbols, Reader.getLength()))
+ fatal(EC, "StreamReader.readArray<CVSymbolArray> failed");
+
+ CVTypeDumper TypeDumper(&W, false);
+ CVSymbolDumper SymbolDumper(W, TypeDumper, nullptr, false);
+ if (auto EC = SymbolDumper.dump(Symbols))
+ fatal(EC, "CVSymbolDumper::dump failed");
+}
+
+// Dump CodeView debug info. This is for debugging.
+static void dumpCodeView(SymbolTable *Symtab) {
+ ScopedPrinter W(outs());
+
+ for (ObjectFile *File : Symtab->ObjectFiles) {
+ dumpDebugT(W, File);
+ dumpDebugS(W, File);
+ }
+}
+
+static void addTypeInfo(SymbolTable *Symtab,
+ pdb::TpiStreamBuilder &TpiBuilder) {
+ for (ObjectFile *File : Symtab->ObjectFiles) {
+ ArrayRef<uint8_t> Data = getDebugT(File);
+ if (Data.empty())
+ continue;
+
+ msf::ByteStream Stream(Data);
+ codeview::CVTypeArray Records;
+ msf::StreamReader Reader(Stream);
+ if (auto EC = Reader.readArray(Records, Reader.getLength()))
+ fatal(EC, "Reader.readArray failed");
+ for (const codeview::CVType &Rec : Records)
+ TpiBuilder.addTypeRecord(Rec);
+ }
+}
+
+// Creates a PDB file.
+void coff::createPDB(StringRef Path, SymbolTable *Symtab,
+ ArrayRef<uint8_t> SectionTable) {
+ if (Config->DumpPdb)
+ dumpCodeView(Symtab);
+
+ BumpPtrAllocator Alloc;
+ pdb::PDBFileBuilder Builder(Alloc);
+ ExitOnErr(Builder.initialize(4096)); // 4096 is blocksize
+
+ // Create streams in MSF for predefined streams, namely
+ // PDB, TPI, DBI and IPI.
+ for (int I = 0; I < (int)pdb::kSpecialStreamCount; ++I)
+ ExitOnErr(Builder.getMsfBuilder().addStream(0));
+
+ // Add an Info stream.
+ auto &InfoBuilder = Builder.getInfoBuilder();
+ InfoBuilder.setAge(1);
+
+ // Should be a random number, 0 for now.
+ InfoBuilder.setGuid({});
+
+ // Should be the current time, but set 0 for reproducibilty.
+ InfoBuilder.setSignature(0);
+ InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70);
+
+ // Add an empty DPI stream.
+ auto &DbiBuilder = Builder.getDbiBuilder();
+ DbiBuilder.setVersionHeader(pdb::PdbDbiV110);
+
+ // Add an empty TPI stream.
+ auto &TpiBuilder = Builder.getTpiBuilder();
+ TpiBuilder.setVersionHeader(pdb::PdbTpiV80);
+ if (Config->DebugPdb)
+ addTypeInfo(Symtab, TpiBuilder);
+
+ // Add an empty IPI stream.
+ auto &IpiBuilder = Builder.getIpiBuilder();
+ IpiBuilder.setVersionHeader(pdb::PdbTpiV80);
+
+ // Add Section Contributions.
+ std::vector<pdb::SectionContrib> Contribs =
+ pdb::DbiStreamBuilder::createSectionContribs(getInputSections(Symtab));
+ DbiBuilder.setSectionContribs(Contribs);
+
+ // Add Section Map stream.
+ ArrayRef<object::coff_section> Sections = {
+ (const object::coff_section *)SectionTable.data(),
+ SectionTable.size() / sizeof(object::coff_section)};
+ std::vector<pdb::SecMapEntry> SectionMap =
+ pdb::DbiStreamBuilder::createSectionMap(Sections);
+ DbiBuilder.setSectionMap(SectionMap);
+
+ ExitOnErr(DbiBuilder.addModuleInfo("", "* Linker *"));
+
+ // Add COFF section header stream.
+ ExitOnErr(
+ DbiBuilder.addDbgStream(pdb::DbgHeaderType::SectionHdr, SectionTable));
+
+ // Write to a file.
+ ExitOnErr(Builder.commit(Path));
}
diff --git a/COFF/PDB.h b/COFF/PDB.h
new file mode 100644
index 000000000000..091e90fa1ef1
--- /dev/null
+++ b/COFF/PDB.h
@@ -0,0 +1,25 @@
+//===- PDB.h ----------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_COFF_PDB_H
+#define LLD_COFF_PDB_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace lld {
+namespace coff {
+class SymbolTable;
+
+void createPDB(llvm::StringRef Path, SymbolTable *Symtab,
+ llvm::ArrayRef<uint8_t> SectionTable);
+}
+}
+
+#endif
diff --git a/COFF/Strings.cpp b/COFF/Strings.cpp
new file mode 100644
index 000000000000..d0558413f673
--- /dev/null
+++ b/COFF/Strings.cpp
@@ -0,0 +1,30 @@
+//===- Strings.cpp -------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Strings.h"
+
+#if defined(_MSC_VER)
+#include <Windows.h>
+#include <DbgHelp.h>
+#pragma comment(lib, "dbghelp.lib")
+#endif
+
+using namespace lld;
+using namespace lld::coff;
+using namespace llvm;
+
+Optional<std::string> coff::demangle(StringRef S) {
+#if defined(_MSC_VER)
+ char Buf[4096];
+ if (S.startswith("?"))
+ if (size_t Len = UnDecorateSymbolName(S.str().c_str(), Buf, sizeof(Buf), 0))
+ return std::string(Buf, Len);
+#endif
+ return None;
+}
diff --git a/COFF/Strings.h b/COFF/Strings.h
new file mode 100644
index 000000000000..1f85f3e2da5c
--- /dev/null
+++ b/COFF/Strings.h
@@ -0,0 +1,23 @@
+//===- Strings.h ------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_COFF_STRINGS_H
+#define LLD_COFF_STRINGS_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+namespace lld {
+namespace coff {
+llvm::Optional<std::string> demangle(llvm::StringRef S);
+}
+}
+
+#endif
diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp
index df9da4c36650..9cc0b75c1510 100644
--- a/COFF/SymbolTable.cpp
+++ b/COFF/SymbolTable.cpp
@@ -7,12 +7,12 @@
//
//===----------------------------------------------------------------------===//
+#include "SymbolTable.h"
#include "Config.h"
#include "Driver.h"
#include "Error.h"
-#include "SymbolTable.h"
+#include "Memory.h"
#include "Symbols.h"
-#include "lld/Core/Parallel.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/LTO/legacy/LTOCodeGenerator.h"
#include "llvm/Support/Debug.h"
@@ -24,222 +24,265 @@ using namespace llvm;
namespace lld {
namespace coff {
-void SymbolTable::addFile(std::unique_ptr<InputFile> FileP) {
-#if LLVM_ENABLE_THREADS
- std::launch Policy = std::launch::async;
-#else
- std::launch Policy = std::launch::deferred;
-#endif
+SymbolTable *Symtab;
- InputFile *File = FileP.get();
- Files.push_back(std::move(FileP));
- if (auto *F = dyn_cast<ArchiveFile>(File)) {
- ArchiveQueue.push_back(
- std::async(Policy, [=]() { F->parse(); return F; }));
- return;
+void SymbolTable::addFile(InputFile *File) {
+ if (Config->Verbose)
+ outs() << "Reading " << toString(File) << "\n";
+ File->parse();
+
+ MachineTypes MT = File->getMachineType();
+ if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) {
+ Config->Machine = MT;
+ } else if (MT != IMAGE_FILE_MACHINE_UNKNOWN && Config->Machine != MT) {
+ fatal(toString(File) + ": machine type " + machineToStr(MT) +
+ " conflicts with " + machineToStr(Config->Machine));
}
- ObjectQueue.push_back(
- std::async(Policy, [=]() { File->parse(); return File; }));
+
if (auto *F = dyn_cast<ObjectFile>(File)) {
ObjectFiles.push_back(F);
} else if (auto *F = dyn_cast<BitcodeFile>(File)) {
BitcodeFiles.push_back(F);
- } else {
- ImportFiles.push_back(cast<ImportFile>(File));
+ } else if (auto *F = dyn_cast<ImportFile>(File)) {
+ ImportFiles.push_back(F);
}
-}
-void SymbolTable::step() {
- if (queueEmpty())
+ StringRef S = File->getDirectives();
+ if (S.empty())
return;
- readObjects();
- readArchives();
-}
-void SymbolTable::run() {
- while (!queueEmpty())
- step();
-}
-
-void SymbolTable::readArchives() {
- if (ArchiveQueue.empty())
- return;
-
- // Add lazy symbols to the symbol table. Lazy symbols that conflict
- // with existing undefined symbols are accumulated in LazySyms.
- std::vector<Symbol *> LazySyms;
- for (std::future<ArchiveFile *> &Future : ArchiveQueue) {
- ArchiveFile *File = Future.get();
- if (Config->Verbose)
- llvm::outs() << "Reading " << File->getShortName() << "\n";
- for (Lazy &Sym : File->getLazySymbols())
- addLazy(&Sym, &LazySyms);
- }
- ArchiveQueue.clear();
-
- // Add archive member files to ObjectQueue that should resolve
- // existing undefined symbols.
- for (Symbol *Sym : LazySyms)
- addMemberFile(cast<Lazy>(Sym->Body));
-}
-
-void SymbolTable::readObjects() {
- if (ObjectQueue.empty())
- return;
-
- // Add defined and undefined symbols to the symbol table.
- std::vector<StringRef> Directives;
- for (size_t I = 0; I < ObjectQueue.size(); ++I) {
- InputFile *File = ObjectQueue[I].get();
- if (Config->Verbose)
- llvm::outs() << "Reading " << File->getShortName() << "\n";
- // Adding symbols may add more files to ObjectQueue
- // (but not to ArchiveQueue).
- for (SymbolBody *Sym : File->getSymbols())
- if (Sym->isExternal())
- addSymbol(Sym);
- StringRef S = File->getDirectives();
- if (!S.empty()) {
- Directives.push_back(S);
- if (Config->Verbose)
- llvm::outs() << "Directives: " << File->getShortName()
- << ": " << S << "\n";
- }
- }
- ObjectQueue.clear();
-
- // Parse directive sections. This may add files to
- // ArchiveQueue and ObjectQueue.
- for (StringRef S : Directives)
- Driver->parseDirectives(S);
-}
-
-bool SymbolTable::queueEmpty() {
- return ArchiveQueue.empty() && ObjectQueue.empty();
+ if (Config->Verbose)
+ outs() << "Directives: " << toString(File) << ": " << S << "\n";
+ Driver->parseDirectives(S);
}
-void SymbolTable::reportRemainingUndefines(bool Resolve) {
- llvm::SmallPtrSet<SymbolBody *, 8> Undefs;
+void SymbolTable::reportRemainingUndefines() {
+ SmallPtrSet<SymbolBody *, 8> Undefs;
for (auto &I : Symtab) {
Symbol *Sym = I.second;
- auto *Undef = dyn_cast<Undefined>(Sym->Body);
+ auto *Undef = dyn_cast<Undefined>(Sym->body());
if (!Undef)
continue;
+ if (!Sym->IsUsedInRegularObj)
+ continue;
StringRef Name = Undef->getName();
// A weak alias may have been resolved, so check for that.
if (Defined *D = Undef->getWeakAlias()) {
- if (Resolve)
- Sym->Body = D;
+ // We resolve weak aliases by replacing the alias's SymbolBody with the
+ // target's SymbolBody. This causes all SymbolBody pointers referring to
+ // the old symbol to instead refer to the new symbol. However, we can't
+ // just blindly copy sizeof(Symbol::Body) bytes from D to Sym->Body
+ // because D may be an internal symbol, and internal symbols are stored as
+ // "unparented" SymbolBodies. For that reason we need to check which type
+ // of symbol we are dealing with and copy the correct number of bytes.
+ if (isa<DefinedRegular>(D))
+ memcpy(Sym->Body.buffer, D, sizeof(DefinedRegular));
+ else if (isa<DefinedAbsolute>(D))
+ memcpy(Sym->Body.buffer, D, sizeof(DefinedAbsolute));
+ else
+ // No other internal symbols are possible.
+ Sym->Body = D->symbol()->Body;
continue;
}
// If we can resolve a symbol by removing __imp_ prefix, do that.
// This odd rule is for compatibility with MSVC linker.
if (Name.startswith("__imp_")) {
Symbol *Imp = find(Name.substr(strlen("__imp_")));
- if (Imp && isa<Defined>(Imp->Body)) {
- if (!Resolve)
- continue;
- auto *D = cast<Defined>(Imp->Body);
- auto *S = new (Alloc) DefinedLocalImport(Name, D);
- LocalImportChunks.push_back(S->getChunk());
- Sym->Body = S;
+ if (Imp && isa<Defined>(Imp->body())) {
+ auto *D = cast<Defined>(Imp->body());
+ replaceBody<DefinedLocalImport>(Sym, Name, D);
+ LocalImportChunks.push_back(
+ cast<DefinedLocalImport>(Sym->body())->getChunk());
continue;
}
}
// Remaining undefined symbols are not fatal if /force is specified.
// They are replaced with dummy defined symbols.
- if (Config->Force && Resolve)
- Sym->Body = new (Alloc) DefinedAbsolute(Name, 0);
- Undefs.insert(Sym->Body);
+ if (Config->Force)
+ replaceBody<DefinedAbsolute>(Sym, Name, 0);
+ Undefs.insert(Sym->body());
}
if (Undefs.empty())
return;
- for (Undefined *U : Config->GCRoot)
- if (Undefs.count(U->repl()))
- llvm::errs() << "<root>: undefined symbol: " << U->getName() << "\n";
- for (std::unique_ptr<InputFile> &File : Files)
- if (!isa<ArchiveFile>(File.get()))
- for (SymbolBody *Sym : File->getSymbols())
- if (Undefs.count(Sym->repl()))
- llvm::errs() << File->getShortName() << ": undefined symbol: "
- << Sym->getName() << "\n";
+ for (SymbolBody *B : Config->GCRoot)
+ if (Undefs.count(B))
+ errs() << "<root>: undefined symbol: " << B->getName() << "\n";
+ for (ObjectFile *File : ObjectFiles)
+ for (SymbolBody *Sym : File->getSymbols())
+ if (Undefs.count(Sym))
+ errs() << toString(File) << ": undefined symbol: " << Sym->getName()
+ << "\n";
if (!Config->Force)
fatal("link failed");
}
-void SymbolTable::addLazy(Lazy *New, std::vector<Symbol *> *Accum) {
- Symbol *Sym = insert(New);
- if (Sym->Body == New)
- return;
- SymbolBody *Existing = Sym->Body;
- if (isa<Defined>(Existing))
- return;
- if (Lazy *L = dyn_cast<Lazy>(Existing))
- if (L->getFileIndex() < New->getFileIndex())
- return;
- Sym->Body = New;
- New->setBackref(Sym);
- if (isa<Undefined>(Existing))
- Accum->push_back(Sym);
+std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
+ Symbol *&Sym = Symtab[CachedHashStringRef(Name)];
+ if (Sym)
+ return {Sym, false};
+ Sym = make<Symbol>();
+ Sym->IsUsedInRegularObj = false;
+ Sym->PendingArchiveLoad = false;
+ return {Sym, true};
}
-void SymbolTable::addSymbol(SymbolBody *New) {
- // Find an existing symbol or create and insert a new one.
- assert(isa<Defined>(New) || isa<Undefined>(New));
- Symbol *Sym = insert(New);
- if (Sym->Body == New)
- return;
- SymbolBody *Existing = Sym->Body;
-
- // If we have an undefined symbol and a lazy symbol,
- // let the lazy symbol to read a member file.
- if (auto *L = dyn_cast<Lazy>(Existing)) {
- // Undefined symbols with weak aliases need not to be resolved,
- // since they would be replaced with weak aliases if they remain
- // undefined.
- if (auto *U = dyn_cast<Undefined>(New)) {
- if (!U->WeakAlias) {
- addMemberFile(L);
- return;
- }
+Symbol *SymbolTable::addUndefined(StringRef Name, InputFile *F,
+ bool IsWeakAlias) {
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(Name);
+ if (!F || !isa<BitcodeFile>(F))
+ S->IsUsedInRegularObj = true;
+ if (WasInserted || (isa<Lazy>(S->body()) && IsWeakAlias)) {
+ replaceBody<Undefined>(S, Name);
+ return S;
+ }
+ if (auto *L = dyn_cast<Lazy>(S->body())) {
+ if (!S->PendingArchiveLoad) {
+ S->PendingArchiveLoad = true;
+ L->File->addMember(&L->Sym);
}
- Sym->Body = New;
+ }
+ return S;
+}
+
+void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) {
+ StringRef Name = Sym.getName();
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(Name);
+ if (WasInserted) {
+ replaceBody<Lazy>(S, F, Sym);
return;
}
+ auto *U = dyn_cast<Undefined>(S->body());
+ if (!U || U->WeakAlias || S->PendingArchiveLoad)
+ return;
+ S->PendingArchiveLoad = true;
+ F->addMember(&Sym);
+}
+
+void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) {
+ fatal("duplicate symbol: " + toString(*Existing->body()) + " in " +
+ toString(Existing->body()->getFile()) + " and in " +
+ (NewFile ? toString(NewFile) : "(internal)"));
+}
- // compare() returns -1, 0, or 1 if the lhs symbol is less preferable,
- // equivalent (conflicting), or more preferable, respectively.
- int Comp = Existing->compare(New);
- if (Comp == 0)
- fatal("duplicate symbol: " + Existing->getDebugName() + " and " +
- New->getDebugName());
- if (Comp < 0)
- Sym->Body = New;
+Symbol *SymbolTable::addAbsolute(StringRef N, COFFSymbolRef Sym) {
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(N);
+ S->IsUsedInRegularObj = true;
+ if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
+ replaceBody<DefinedAbsolute>(S, N, Sym);
+ else if (!isa<DefinedCOFF>(S->body()))
+ reportDuplicate(S, nullptr);
+ return S;
}
-Symbol *SymbolTable::insert(SymbolBody *New) {
- Symbol *&Sym = Symtab[New->getName()];
- if (Sym) {
- New->setBackref(Sym);
- return Sym;
+Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) {
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(N);
+ S->IsUsedInRegularObj = true;
+ if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
+ replaceBody<DefinedAbsolute>(S, N, VA);
+ else if (!isa<DefinedCOFF>(S->body()))
+ reportDuplicate(S, nullptr);
+ return S;
+}
+
+Symbol *SymbolTable::addRelative(StringRef N, uint64_t VA) {
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(N);
+ S->IsUsedInRegularObj = true;
+ if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
+ replaceBody<DefinedRelative>(S, N, VA);
+ else if (!isa<DefinedCOFF>(S->body()))
+ reportDuplicate(S, nullptr);
+ return S;
+}
+
+Symbol *SymbolTable::addRegular(ObjectFile *F, COFFSymbolRef Sym,
+ SectionChunk *C) {
+ StringRef Name;
+ F->getCOFFObj()->getSymbolName(Sym, Name);
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(Name);
+ S->IsUsedInRegularObj = true;
+ if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
+ replaceBody<DefinedRegular>(S, F, Sym, C);
+ else if (auto *R = dyn_cast<DefinedRegular>(S->body())) {
+ if (!C->isCOMDAT() || !R->isCOMDAT())
+ reportDuplicate(S, F);
+ } else if (auto *B = dyn_cast<DefinedBitcode>(S->body())) {
+ if (B->IsReplaceable)
+ replaceBody<DefinedRegular>(S, F, Sym, C);
+ else if (!C->isCOMDAT())
+ reportDuplicate(S, F);
+ } else
+ replaceBody<DefinedRegular>(S, F, Sym, C);
+ return S;
+}
+
+Symbol *SymbolTable::addBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable) {
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(N);
+ if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body())) {
+ replaceBody<DefinedBitcode>(S, F, N, IsReplaceable);
+ return S;
}
- Sym = new (Alloc) Symbol(New);
- New->setBackref(Sym);
- return Sym;
+ if (isa<DefinedCommon>(S->body()))
+ return S;
+ if (IsReplaceable)
+ if (isa<DefinedRegular>(S->body()) || isa<DefinedBitcode>(S->body()))
+ return S;
+ reportDuplicate(S, F);
+ return S;
}
-// Reads an archive member file pointed by a given symbol.
-void SymbolTable::addMemberFile(Lazy *Body) {
- std::unique_ptr<InputFile> File = Body->getMember();
+Symbol *SymbolTable::addCommon(ObjectFile *F, COFFSymbolRef Sym,
+ CommonChunk *C) {
+ StringRef Name;
+ F->getCOFFObj()->getSymbolName(Sym, Name);
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(Name);
+ S->IsUsedInRegularObj = true;
+ if (WasInserted || !isa<DefinedCOFF>(S->body()))
+ replaceBody<DefinedCommon>(S, F, Sym, C);
+ else if (auto *DC = dyn_cast<DefinedCommon>(S->body()))
+ if (Sym.getValue() > DC->getSize())
+ replaceBody<DefinedCommon>(S, F, Sym, C);
+ return S;
+}
- // getMember returns an empty buffer if the member was already
- // read from the library.
- if (!File)
- return;
- if (Config->Verbose)
- llvm::outs() << "Loaded " << File->getShortName() << " for "
- << Body->getName() << "\n";
- addFile(std::move(File));
+Symbol *SymbolTable::addImportData(StringRef N, ImportFile *F) {
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(N);
+ S->IsUsedInRegularObj = true;
+ if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
+ replaceBody<DefinedImportData>(S, N, F);
+ else if (!isa<DefinedCOFF>(S->body()))
+ reportDuplicate(S, nullptr);
+ return S;
+}
+
+Symbol *SymbolTable::addImportThunk(StringRef Name, DefinedImportData *ID,
+ uint16_t Machine) {
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(Name);
+ S->IsUsedInRegularObj = true;
+ if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
+ replaceBody<DefinedImportThunk>(S, Name, ID, Machine);
+ else if (!isa<DefinedCOFF>(S->body()))
+ reportDuplicate(S, nullptr);
+ return S;
}
std::vector<Chunk *> SymbolTable::getChunks() {
@@ -252,7 +295,7 @@ std::vector<Chunk *> SymbolTable::getChunks() {
}
Symbol *SymbolTable::find(StringRef Name) {
- auto It = Symtab.find(Name);
+ auto It = Symtab.find(CachedHashStringRef(Name));
if (It == Symtab.end())
return nullptr;
return It->second;
@@ -266,7 +309,7 @@ Symbol *SymbolTable::findUnderscore(StringRef Name) {
StringRef SymbolTable::findByPrefix(StringRef Prefix) {
for (auto Pair : Symtab) {
- StringRef Name = Pair.first;
+ StringRef Name = Pair.first.val();
if (Name.startswith(Prefix))
return Name;
}
@@ -275,7 +318,7 @@ StringRef SymbolTable::findByPrefix(StringRef Prefix) {
StringRef SymbolTable::findMangle(StringRef Name) {
if (Symbol *Sym = find(Name))
- if (!isa<Undefined>(Sym->Body))
+ if (!isa<Undefined>(Sym->body()))
return Name;
if (Config->Machine != I386)
return findByPrefix(("?" + Name + "@@Y").str());
@@ -289,39 +332,22 @@ StringRef SymbolTable::findMangle(StringRef Name) {
return findByPrefix(("?" + Name.substr(1) + "@@Y").str());
}
-void SymbolTable::mangleMaybe(Undefined *U) {
- if (U->WeakAlias)
- return;
- if (!isa<Undefined>(U->repl()))
+void SymbolTable::mangleMaybe(SymbolBody *B) {
+ auto *U = dyn_cast<Undefined>(B);
+ if (!U || U->WeakAlias)
return;
StringRef Alias = findMangle(U->getName());
if (!Alias.empty())
U->WeakAlias = addUndefined(Alias);
}
-Undefined *SymbolTable::addUndefined(StringRef Name) {
- auto *New = new (Alloc) Undefined(Name);
- addSymbol(New);
- if (auto *U = dyn_cast<Undefined>(New->repl()))
- return U;
- return New;
-}
-
-DefinedRelative *SymbolTable::addRelative(StringRef Name, uint64_t VA) {
- auto *New = new (Alloc) DefinedRelative(Name, VA);
- addSymbol(New);
- return New;
-}
-
-DefinedAbsolute *SymbolTable::addAbsolute(StringRef Name, uint64_t VA) {
- auto *New = new (Alloc) DefinedAbsolute(Name, VA);
- addSymbol(New);
- return New;
+SymbolBody *SymbolTable::addUndefined(StringRef Name) {
+ return addUndefined(Name, nullptr, false)->body();
}
void SymbolTable::printMap(llvm::raw_ostream &OS) {
for (ObjectFile *File : ObjectFiles) {
- OS << File->getShortName() << ":\n";
+ OS << toString(File) << ":\n";
for (SymbolBody *Body : File->getSymbols())
if (auto *R = dyn_cast<DefinedRegular>(Body))
if (R->getChunk()->isLive())
@@ -330,84 +356,32 @@ void SymbolTable::printMap(llvm::raw_ostream &OS) {
}
}
-void SymbolTable::addCombinedLTOObject(ObjectFile *Obj) {
- for (SymbolBody *Body : Obj->getSymbols()) {
- if (!Body->isExternal())
- continue;
- // We should not see any new undefined symbols at this point, but we'll
- // diagnose them later in reportRemainingUndefines().
- StringRef Name = Body->getName();
- Symbol *Sym = insert(Body);
- SymbolBody *Existing = Sym->Body;
-
- if (Existing == Body)
- continue;
-
- if (isa<DefinedBitcode>(Existing)) {
- Sym->Body = Body;
- continue;
- }
- if (auto *L = dyn_cast<Lazy>(Existing)) {
- // We may see new references to runtime library symbols such as __chkstk
- // here. These symbols must be wholly defined in non-bitcode files.
- addMemberFile(L);
- continue;
- }
-
- int Comp = Existing->compare(Body);
- if (Comp == 0)
- fatal("LTO: unexpected duplicate symbol: " + Name);
- if (Comp < 0)
- Sym->Body = Body;
- }
-}
-
void SymbolTable::addCombinedLTOObjects() {
if (BitcodeFiles.empty())
return;
- // Diagnose any undefined symbols early, but do not resolve weak externals,
- // as resolution breaks the invariant that each Symbol points to a unique
- // SymbolBody, which we rely on to replace DefinedBitcode symbols correctly.
- reportRemainingUndefines(/*Resolve=*/false);
-
// Create an object file and add it to the symbol table by replacing any
// DefinedBitcode symbols with the definitions in the object file.
LTOCodeGenerator CG(BitcodeFile::Context);
CG.setOptLevel(Config->LTOOptLevel);
- std::vector<ObjectFile *> Objs = createLTOObjects(&CG);
-
- for (ObjectFile *Obj : Objs)
- addCombinedLTOObject(Obj);
-
- size_t NumBitcodeFiles = BitcodeFiles.size();
- run();
- if (BitcodeFiles.size() != NumBitcodeFiles)
- fatal("LTO: late loaded symbol created new bitcode reference");
+ for (ObjectFile *Obj : createLTOObjects(&CG))
+ Obj->parse();
}
// Combine and compile bitcode files and then return the result
// as a vector of regular COFF object files.
std::vector<ObjectFile *> SymbolTable::createLTOObjects(LTOCodeGenerator *CG) {
- // All symbols referenced by non-bitcode objects must be preserved.
- for (ObjectFile *File : ObjectFiles)
- for (SymbolBody *Body : File->getSymbols())
- if (auto *S = dyn_cast<DefinedBitcode>(Body->repl()))
- CG->addMustPreserveSymbol(S->getName());
-
- // Likewise for bitcode symbols which we initially resolved to non-bitcode.
+ // All symbols referenced by non-bitcode objects, including GC roots, must be
+ // preserved. We must also replace bitcode symbols with undefined symbols so
+ // that they may be replaced with real definitions without conflicting.
for (BitcodeFile *File : BitcodeFiles)
- for (SymbolBody *Body : File->getSymbols())
- if (isa<DefinedBitcode>(Body) && !isa<DefinedBitcode>(Body->repl()))
+ for (SymbolBody *Body : File->getSymbols()) {
+ if (!isa<DefinedBitcode>(Body))
+ continue;
+ if (Body->symbol()->IsUsedInRegularObj)
CG->addMustPreserveSymbol(Body->getName());
-
- // Likewise for other symbols that must be preserved.
- for (Undefined *U : Config->GCRoot) {
- if (auto *S = dyn_cast<DefinedBitcode>(U->repl()))
- CG->addMustPreserveSymbol(S->getName());
- else if (auto *S = dyn_cast_or_null<DefinedBitcode>(U->getWeakAlias()))
- CG->addMustPreserveSymbol(S->getName());
- }
+ replaceBody<Undefined>(Body->symbol(), Body->getName());
+ }
CG->setModule(BitcodeFiles[0]->takeModule());
for (unsigned I = 1, E = BitcodeFiles.size(); I != E; ++I)
@@ -434,10 +408,8 @@ std::vector<ObjectFile *> SymbolTable::createLTOObjects(LTOCodeGenerator *CG) {
std::vector<ObjectFile *> ObjFiles;
for (SmallString<0> &Obj : Objs) {
- auto *ObjFile = new ObjectFile(MemoryBufferRef(Obj, "<LTO object>"));
- Files.emplace_back(ObjFile);
+ auto *ObjFile = make<ObjectFile>(MemoryBufferRef(Obj, "<LTO object>"));
ObjectFiles.push_back(ObjFile);
- ObjFile->parse();
ObjFiles.push_back(ObjFile);
}
diff --git a/COFF/SymbolTable.h b/COFF/SymbolTable.h
index 8bf4387cdfff..703821f2e124 100644
--- a/COFF/SymbolTable.h
+++ b/COFF/SymbolTable.h
@@ -11,18 +11,12 @@
#define LLD_COFF_SYMBOL_TABLE_H
#include "InputFiles.h"
+#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/raw_ostream.h"
-#ifdef _MSC_VER
-// <future> depends on <eh.h> for __uncaught_exception.
-#include <eh.h>
-#endif
-
-#include <future>
-
namespace llvm {
struct LTOCodeGenerator;
}
@@ -31,8 +25,12 @@ namespace lld {
namespace coff {
class Chunk;
+class CommonChunk;
class Defined;
+class DefinedAbsolute;
+class DefinedRelative;
class Lazy;
+class SectionChunk;
class SymbolBody;
struct Symbol;
@@ -45,18 +43,17 @@ struct Symbol;
// conflicts. For example, obviously, a defined symbol is better than
// an undefined symbol. Or, if there's a conflict between a lazy and a
// undefined, it'll read an archive member to read a real definition
-// to replace the lazy symbol. The logic is implemented in resolve().
+// to replace the lazy symbol. The logic is implemented in the
+// add*() functions, which are called by input files as they are parsed.
+// There is one add* function per symbol type.
class SymbolTable {
public:
- void addFile(std::unique_ptr<InputFile> File);
- std::vector<std::unique_ptr<InputFile>> &getFiles() { return Files; }
- void step();
- void run();
- bool queueEmpty();
+ void addFile(InputFile *File);
- // Print an error message on undefined symbols. If Resolve is true, try to
- // resolve any undefined symbols and update the symbol table accordingly.
- void reportRemainingUndefines(bool Resolve);
+ // Try to resolve any undefined symbols and update the symbol table
+ // accordingly, then print an error message for any remaining undefined
+ // symbols.
+ void reportRemainingUndefines();
// Returns a list of chunks of selected symbols.
std::vector<Chunk *> getChunks();
@@ -69,7 +66,7 @@ public:
// mangled symbol. This function tries to find a mangled name
// for U from the symbol table, and if found, set the symbol as
// a weak alias for U.
- void mangleMaybe(Undefined *U);
+ void mangleMaybe(SymbolBody *B);
StringRef findMangle(StringRef Name);
// Print a layout map to OS.
@@ -88,37 +85,44 @@ public:
std::vector<ObjectFile *> ObjectFiles;
// Creates an Undefined symbol for a given name.
- Undefined *addUndefined(StringRef Name);
- DefinedRelative *addRelative(StringRef Name, uint64_t VA);
- DefinedAbsolute *addAbsolute(StringRef Name, uint64_t VA);
+ SymbolBody *addUndefined(StringRef Name);
+
+ Symbol *addRelative(StringRef N, uint64_t VA);
+ Symbol *addAbsolute(StringRef N, uint64_t VA);
+
+ Symbol *addUndefined(StringRef Name, InputFile *F, bool IsWeakAlias);
+ void addLazy(ArchiveFile *F, const Archive::Symbol Sym);
+ Symbol *addAbsolute(StringRef N, COFFSymbolRef S);
+ Symbol *addRegular(ObjectFile *F, COFFSymbolRef S, SectionChunk *C);
+ Symbol *addBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable);
+ Symbol *addCommon(ObjectFile *F, COFFSymbolRef S, CommonChunk *C);
+ Symbol *addImportData(StringRef N, ImportFile *F);
+ Symbol *addImportThunk(StringRef Name, DefinedImportData *S,
+ uint16_t Machine);
+
+ void reportDuplicate(Symbol *Existing, InputFile *NewFile);
// A list of chunks which to be added to .rdata.
std::vector<Chunk *> LocalImportChunks;
private:
- void readArchives();
+ void readArchive();
void readObjects();
- void addSymbol(SymbolBody *New);
- void addLazy(Lazy *New, std::vector<Symbol *> *Accum);
- Symbol *insert(SymbolBody *New);
+ std::pair<Symbol *, bool> insert(StringRef Name);
StringRef findByPrefix(StringRef Prefix);
- void addMemberFile(Lazy *Body);
void addCombinedLTOObject(ObjectFile *Obj);
std::vector<ObjectFile *> createLTOObjects(llvm::LTOCodeGenerator *CG);
- llvm::DenseMap<StringRef, Symbol *> Symtab;
-
- std::vector<std::unique_ptr<InputFile>> Files;
- std::vector<std::future<ArchiveFile *>> ArchiveQueue;
- std::vector<std::future<InputFile *>> ObjectQueue;
+ llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> Symtab;
std::vector<BitcodeFile *> BitcodeFiles;
std::vector<SmallString<0>> Objs;
- llvm::BumpPtrAllocator Alloc;
};
+extern SymbolTable *Symtab;
+
} // namespace coff
} // namespace lld
diff --git a/COFF/Symbols.cpp b/COFF/Symbols.cpp
index 6e2db6631ce7..6de85d581f49 100644
--- a/COFF/Symbols.cpp
+++ b/COFF/Symbols.cpp
@@ -7,16 +7,17 @@
//
//===----------------------------------------------------------------------===//
+#include "Symbols.h"
#include "Error.h"
#include "InputFiles.h"
-#include "Symbols.h"
+#include "Memory.h"
+#include "Strings.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
+using namespace llvm;
using namespace llvm::object;
-using llvm::sys::fs::identify_magic;
-using llvm::sys::fs::file_magic;
namespace lld {
namespace coff {
@@ -36,130 +37,14 @@ StringRef SymbolBody::getName() {
return Name;
}
-// Returns 1, 0 or -1 if this symbol should take precedence
-// over the Other, tie or lose, respectively.
-int SymbolBody::compare(SymbolBody *Other) {
- Kind LK = kind(), RK = Other->kind();
-
- // Normalize so that the smaller kind is on the left.
- if (LK > RK)
- return -Other->compare(this);
-
- // First handle comparisons between two different kinds.
- if (LK != RK) {
- if (RK > LastDefinedKind) {
- if (LK == LazyKind && cast<Undefined>(Other)->WeakAlias)
- return -1;
-
- // The LHS is either defined or lazy and so it wins.
- assert((LK <= LastDefinedKind || LK == LazyKind) && "Bad kind!");
- return 1;
- }
-
- // Bitcode has special complexities.
- if (RK == DefinedBitcodeKind) {
- auto *RHS = cast<DefinedBitcode>(Other);
-
- switch (LK) {
- case DefinedCommonKind:
- return 1;
-
- case DefinedRegularKind:
- // As an approximation, regular symbols win over bitcode symbols,
- // but we definitely have a conflict if the regular symbol is not
- // replaceable and neither is the bitcode symbol. We do not
- // replicate the rest of the symbol resolution logic here; symbol
- // resolution will be done accurately after lowering bitcode symbols
- // to regular symbols in addCombinedLTOObject().
- if (cast<DefinedRegular>(this)->isCOMDAT() || RHS->IsReplaceable)
- return 1;
-
- // Fallthrough to the default of a tie otherwise.
- default:
- return 0;
- }
- }
-
- // Either of the object file kind will trump a higher kind.
- if (LK <= LastDefinedCOFFKind)
- return 1;
-
- // The remaining kind pairs are ties amongst defined symbols.
- return 0;
- }
-
- // Now handle the case where the kinds are the same.
- switch (LK) {
- case DefinedRegularKind: {
- auto *LHS = cast<DefinedRegular>(this);
- auto *RHS = cast<DefinedRegular>(Other);
- if (LHS->isCOMDAT() && RHS->isCOMDAT())
- return LHS->getFileIndex() < RHS->getFileIndex() ? 1 : -1;
- return 0;
- }
-
- case DefinedCommonKind: {
- auto *LHS = cast<DefinedCommon>(this);
- auto *RHS = cast<DefinedCommon>(Other);
- if (LHS->getSize() == RHS->getSize())
- return LHS->getFileIndex() < RHS->getFileIndex() ? 1 : -1;
- return LHS->getSize() > RHS->getSize() ? 1 : -1;
- }
-
- case DefinedBitcodeKind: {
- auto *LHS = cast<DefinedBitcode>(this);
- auto *RHS = cast<DefinedBitcode>(Other);
- // If both are non-replaceable, we have a tie.
- if (!LHS->IsReplaceable && !RHS->IsReplaceable)
- return 0;
-
- // Non-replaceable symbols win, but even two replaceable symboles don't
- // tie. If both symbols are replaceable, choice is arbitrary.
- if (RHS->IsReplaceable && LHS->IsReplaceable)
- return uintptr_t(LHS) < uintptr_t(RHS) ? 1 : -1;
- return LHS->IsReplaceable ? -1 : 1;
- }
-
- case LazyKind: {
- // Don't tie, pick the earliest.
- auto *LHS = cast<Lazy>(this);
- auto *RHS = cast<Lazy>(Other);
- return LHS->getFileIndex() < RHS->getFileIndex() ? 1 : -1;
- }
-
- case UndefinedKind: {
- auto *LHS = cast<Undefined>(this);
- auto *RHS = cast<Undefined>(Other);
- // Tie if both undefined symbols have different weak aliases.
- if (LHS->WeakAlias && RHS->WeakAlias) {
- if (LHS->WeakAlias->getName() != RHS->WeakAlias->getName())
- return 0;
- return uintptr_t(LHS) < uintptr_t(RHS) ? 1 : -1;
- }
- return LHS->WeakAlias ? 1 : -1;
- }
-
- case DefinedLocalImportKind:
- case DefinedImportThunkKind:
- case DefinedImportDataKind:
- case DefinedAbsoluteKind:
- case DefinedRelativeKind:
- // These all simply tie.
- return 0;
- }
- llvm_unreachable("unknown symbol kind");
-}
-
-std::string SymbolBody::getDebugName() {
- std::string N = getName().str();
- if (auto *D = dyn_cast<DefinedCOFF>(this)) {
- N += " ";
- N += D->File->getShortName();
- } else if (auto *D = dyn_cast<DefinedBitcode>(this)) {
- N += " ";
- N += D->File->getShortName();
- }
- return N;
+InputFile *SymbolBody::getFile() {
+ if (auto *Sym = dyn_cast<DefinedCOFF>(this))
+ return Sym->File;
+ if (auto *Sym = dyn_cast<DefinedBitcode>(this))
+ return Sym->File;
+ if (auto *Sym = dyn_cast<Lazy>(this))
+ return Sym->File;
+ return nullptr;
}
COFFSymbolRef DefinedCOFF::getCOFFSymbol() {
@@ -174,44 +59,27 @@ DefinedImportThunk::DefinedImportThunk(StringRef Name, DefinedImportData *S,
uint16_t Machine)
: Defined(DefinedImportThunkKind, Name) {
switch (Machine) {
- case AMD64: Data.reset(new ImportThunkChunkX64(S)); return;
- case I386: Data.reset(new ImportThunkChunkX86(S)); return;
- case ARMNT: Data.reset(new ImportThunkChunkARM(S)); return;
+ case AMD64: Data = make<ImportThunkChunkX64>(S); return;
+ case I386: Data = make<ImportThunkChunkX86>(S); return;
+ case ARMNT: Data = make<ImportThunkChunkARM>(S); return;
default: llvm_unreachable("unknown machine type");
}
}
-std::unique_ptr<InputFile> Lazy::getMember() {
- MemoryBufferRef MBRef = File->getMember(&Sym);
-
- // getMember returns an empty buffer if the member was already
- // read from the library.
- if (MBRef.getBuffer().empty())
- return std::unique_ptr<InputFile>(nullptr);
-
- file_magic Magic = identify_magic(MBRef.getBuffer());
- if (Magic == file_magic::coff_import_library)
- return std::unique_ptr<InputFile>(new ImportFile(MBRef));
-
- std::unique_ptr<InputFile> Obj;
- if (Magic == file_magic::coff_object)
- Obj.reset(new ObjectFile(MBRef));
- else if (Magic == file_magic::bitcode)
- Obj.reset(new BitcodeFile(MBRef));
- else
- fatal("unknown file type: " + File->getName());
-
- Obj->setParentName(File->getName());
- return Obj;
-}
-
Defined *Undefined::getWeakAlias() {
// A weak alias may be a weak alias to another symbol, so check recursively.
for (SymbolBody *A = WeakAlias; A; A = cast<Undefined>(A)->WeakAlias)
- if (auto *D = dyn_cast<Defined>(A->repl()))
+ if (auto *D = dyn_cast<Defined>(A))
return D;
return nullptr;
}
+// Returns a symbol name for an error message.
+std::string toString(SymbolBody &B) {
+ if (Optional<std::string> S = demangle(B.getName()))
+ return ("\"" + *S + "\" (" + B.getName() + ")").str();
+ return B.getName();
+}
+
} // namespace coff
} // namespace lld
diff --git a/COFF/Symbols.h b/COFF/Symbols.h
index f96c1fb3cc1d..bc9ad4aa8aff 100644
--- a/COFF/Symbols.h
+++ b/COFF/Symbols.h
@@ -12,6 +12,7 @@
#include "Chunks.h"
#include "Config.h"
+#include "Memory.h"
#include "lld/Core/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Object/Archive.h"
@@ -32,15 +33,8 @@ class ArchiveFile;
class BitcodeFile;
class InputFile;
class ObjectFile;
-class SymbolBody;
-
-// A real symbol object, SymbolBody, is usually accessed indirectly
-// through a Symbol. There's always one Symbol for each symbol name.
-// The resolver updates SymbolBody pointers as it resolves symbols.
-struct Symbol {
- explicit Symbol(SymbolBody *P) : Body(P) {}
- SymbolBody *Body;
-};
+struct Symbol;
+class SymbolTable;
// The base class for real symbol classes.
class SymbolBody {
@@ -75,28 +69,19 @@ public:
// Returns the symbol name.
StringRef getName();
- // A SymbolBody has a backreference to a Symbol. Originally they are
- // doubly-linked. A backreference will never change. But the pointer
- // in the Symbol may be mutated by the resolver. If you have a
- // pointer P to a SymbolBody and are not sure whether the resolver
- // has chosen the object among other objects having the same name,
- // you can access P->Backref->Body to get the resolver's result.
- void setBackref(Symbol *P) { Backref = P; }
- SymbolBody *repl() { return Backref ? Backref->Body : this; }
-
- // Decides which symbol should "win" in the symbol table, this or
- // the Other. Returns 1 if this wins, -1 if the Other wins, or 0 if
- // they are duplicate (conflicting) symbols.
- int compare(SymbolBody *Other);
+ // Returns the file from which this symbol was created.
+ InputFile *getFile();
- // Returns a name of this symbol including source file name.
- // Used only for debugging and logging.
- std::string getDebugName();
+ Symbol *symbol();
+ const Symbol *symbol() const {
+ return const_cast<SymbolBody *>(this)->symbol();
+ }
protected:
+ friend SymbolTable;
explicit SymbolBody(Kind K, StringRef N = "")
: SymbolKind(K), IsExternal(true), IsCOMDAT(false),
- IsReplaceable(false), Name(N) {}
+ IsReplaceable(false), WrittenToSymtab(false), Name(N) {}
const unsigned SymbolKind : 8;
unsigned IsExternal : 1;
@@ -107,8 +92,12 @@ protected:
// This bit is used by the \c DefinedBitcode subclass.
unsigned IsReplaceable : 1;
+public:
+ // This bit is used by Writer::createSymbolAndStringTable().
+ unsigned WrittenToSymtab : 1;
+
+protected:
StringRef Name;
- Symbol *Backref = nullptr;
};
// The base class for any defined symbols, including absolute symbols,
@@ -149,12 +138,13 @@ public:
return S->kind() <= LastDefinedCOFFKind;
}
- int getFileIndex() { return File->Index; }
+ ObjectFile *getFile() { return File; }
COFFSymbolRef getCOFFSymbol();
-protected:
ObjectFile *File;
+
+protected:
const coff_symbol_generic *Sym;
};
@@ -194,7 +184,7 @@ public:
uint64_t getRVA() { return Data->getRVA(); }
private:
- friend SymbolBody;
+ friend SymbolTable;
uint64_t getSize() { return Sym->Value; }
CommonChunk *Data;
};
@@ -253,14 +243,12 @@ public:
static bool classof(const SymbolBody *S) { return S->kind() == LazyKind; }
- // Returns an object file for this symbol, or a nullptr if the file
- // was already returned.
- std::unique_ptr<InputFile> getMember();
+ ArchiveFile *File;
- int getFileIndex() { return File->Index; }
+private:
+ friend SymbolTable;
private:
- ArchiveFile *File;
const Archive::Symbol Sym;
};
@@ -293,26 +281,22 @@ public:
// table in an output. The former has "__imp_" prefix.
class DefinedImportData : public Defined {
public:
- DefinedImportData(StringRef D, StringRef N, StringRef E,
- const coff_import_header *H)
- : Defined(DefinedImportDataKind, N), DLLName(D), ExternalName(E), Hdr(H) {
+ DefinedImportData(StringRef N, ImportFile *F)
+ : Defined(DefinedImportDataKind, N), File(F) {
}
static bool classof(const SymbolBody *S) {
return S->kind() == DefinedImportDataKind;
}
- uint64_t getRVA() { return Location->getRVA(); }
- StringRef getDLLName() { return DLLName; }
- StringRef getExternalName() { return ExternalName; }
- void setLocation(Chunk *AddressTable) { Location = AddressTable; }
- uint16_t getOrdinal() { return Hdr->OrdinalHint; }
+ uint64_t getRVA() { return File->Location->getRVA(); }
+ StringRef getDLLName() { return File->DLLName; }
+ StringRef getExternalName() { return File->ExternalName; }
+ void setLocation(Chunk *AddressTable) { File->Location = AddressTable; }
+ uint16_t getOrdinal() { return File->Hdr->OrdinalHint; }
private:
- StringRef DLLName;
- StringRef ExternalName;
- const coff_import_header *Hdr;
- Chunk *Location = nullptr;
+ ImportFile *File;
};
// This class represents a symbol for a jump table entry which jumps
@@ -329,10 +313,10 @@ public:
}
uint64_t getRVA() { return Data->getRVA(); }
- Chunk *getChunk() { return Data.get(); }
+ Chunk *getChunk() { return Data; }
private:
- std::unique_ptr<Chunk> Data;
+ Chunk *Data;
};
// If you have a symbol "__imp_foo" in your object file, a symbol name
@@ -343,17 +327,17 @@ private:
class DefinedLocalImport : public Defined {
public:
DefinedLocalImport(StringRef N, Defined *S)
- : Defined(DefinedLocalImportKind, N), Data(S) {}
+ : Defined(DefinedLocalImportKind, N), Data(make<LocalImportChunk>(S)) {}
static bool classof(const SymbolBody *S) {
return S->kind() == DefinedLocalImportKind;
}
- uint64_t getRVA() { return Data.getRVA(); }
- Chunk *getChunk() { return &Data; }
+ uint64_t getRVA() { return Data->getRVA(); }
+ Chunk *getChunk() { return Data; }
private:
- LocalImportChunk Data;
+ LocalImportChunk *Data;
};
class DefinedBitcode : public Defined {
@@ -361,6 +345,11 @@ class DefinedBitcode : public Defined {
public:
DefinedBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable)
: Defined(DefinedBitcodeKind, N), File(F) {
+ // IsReplaceable tracks whether the bitcode symbol may be replaced with some
+ // other (defined, common or bitcode) symbol. This is the case for common,
+ // comdat and weak external symbols. We try to replace bitcode symbols with
+ // "real" symbols (see SymbolTable::add{Regular,Bitcode}), and resolve the
+ // result against the real symbol from the combined LTO object.
this->IsReplaceable = IsReplaceable;
}
@@ -368,7 +357,6 @@ public:
return S->kind() == DefinedBitcodeKind;
}
-private:
BitcodeFile *File;
};
@@ -397,6 +385,52 @@ inline uint64_t Defined::getRVA() {
llvm_unreachable("unknown symbol kind");
}
+// A real symbol object, SymbolBody, is usually stored within a Symbol. There's
+// always one Symbol for each symbol name. The resolver updates the SymbolBody
+// stored in the Body field of this object as it resolves symbols. Symbol also
+// holds computed properties of symbol names.
+struct Symbol {
+ // True if this symbol was referenced by a regular (non-bitcode) object.
+ unsigned IsUsedInRegularObj : 1;
+
+ // True if we've seen both a lazy and an undefined symbol with this symbol
+ // name, which means that we have enqueued an archive member load and should
+ // not load any more archive members to resolve the same symbol.
+ unsigned PendingArchiveLoad : 1;
+
+ // This field is used to store the Symbol's SymbolBody. This instantiation of
+ // AlignedCharArrayUnion gives us a struct with a char array field that is
+ // large and aligned enough to store any derived class of SymbolBody.
+ llvm::AlignedCharArrayUnion<DefinedRegular, DefinedCommon, DefinedAbsolute,
+ DefinedRelative, Lazy, Undefined,
+ DefinedImportData, DefinedImportThunk,
+ DefinedLocalImport, DefinedBitcode>
+ Body;
+
+ SymbolBody *body() {
+ return reinterpret_cast<SymbolBody *>(Body.buffer);
+ }
+ const SymbolBody *body() const { return const_cast<Symbol *>(this)->body(); }
+};
+
+template <typename T, typename... ArgT>
+void replaceBody(Symbol *S, ArgT &&... Arg) {
+ static_assert(sizeof(T) <= sizeof(S->Body), "Body too small");
+ static_assert(alignof(T) <= alignof(decltype(S->Body)),
+ "Body not aligned enough");
+ assert(static_cast<SymbolBody *>(static_cast<T *>(nullptr)) == nullptr &&
+ "Not a SymbolBody");
+ new (S->Body.buffer) T(std::forward<ArgT>(Arg)...);
+}
+
+inline Symbol *SymbolBody::symbol() {
+ assert(isExternal());
+ return reinterpret_cast<Symbol *>(reinterpret_cast<char *>(this) -
+ offsetof(Symbol, Body));
+}
+
+std::string toString(SymbolBody &B);
+
} // namespace coff
} // namespace lld
diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp
index d8077df95701..3e69aebbb424 100644
--- a/COFF/Writer.cpp
+++ b/COFF/Writer.cpp
@@ -7,13 +7,15 @@
//
//===----------------------------------------------------------------------===//
+#include "Writer.h"
#include "Config.h"
#include "DLL.h"
#include "Error.h"
#include "InputFiles.h"
+#include "Memory.h"
+#include "PDB.h"
#include "SymbolTable.h"
#include "Symbols.h"
-#include "Writer.h"
#include "lld/Core/Parallel.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
@@ -21,6 +23,7 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/RandomNumberGenerator.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstdio>
@@ -42,6 +45,61 @@ static const int DOSStubSize = 64;
static const int NumberfOfDataDirectory = 16;
namespace {
+
+class DebugDirectoryChunk : public Chunk {
+public:
+ DebugDirectoryChunk(const std::vector<std::unique_ptr<Chunk>> &R)
+ : Records(R) {}
+
+ size_t getSize() const override {
+ return Records.size() * sizeof(debug_directory);
+ }
+
+ void writeTo(uint8_t *B) const override {
+ auto *D = reinterpret_cast<debug_directory *>(B + OutputSectionOff);
+
+ for (const std::unique_ptr<Chunk> &Record : Records) {
+ D->Characteristics = 0;
+ D->TimeDateStamp = 0;
+ D->MajorVersion = 0;
+ D->MinorVersion = 0;
+ D->Type = COFF::IMAGE_DEBUG_TYPE_CODEVIEW;
+ D->SizeOfData = Record->getSize();
+ D->AddressOfRawData = Record->getRVA();
+ // TODO(compnerd) get the file offset
+ D->PointerToRawData = 0;
+
+ ++D;
+ }
+ }
+
+private:
+ const std::vector<std::unique_ptr<Chunk>> &Records;
+};
+
+class CVDebugRecordChunk : public Chunk {
+ size_t getSize() const override {
+ return sizeof(codeview::DebugInfo) + Config->PDBPath.size() + 1;
+ }
+
+ void writeTo(uint8_t *B) const override {
+ // Save off the DebugInfo entry to backfill the file signature (build id)
+ // in Writer::writeBuildId
+ DI = reinterpret_cast<codeview::DebugInfo *>(B + OutputSectionOff);
+
+ DI->Signature.CVSignature = OMF::Signature::PDB70;
+
+ // variable sized field (PDB Path)
+ auto *P = reinterpret_cast<char *>(B + OutputSectionOff + sizeof(*DI));
+ if (!Config->PDBPath.empty())
+ memcpy(P, Config->PDBPath.data(), Config->PDBPath.size());
+ P[Config->PDBPath.size()] = '\0';
+ }
+
+public:
+ mutable codeview::DebugInfo *DI = nullptr;
+};
+
// The writer writes a SymbolTable result to a file.
class Writer {
public:
@@ -62,6 +120,7 @@ private:
void setSectionPermissions();
void writeSections();
void sortExceptionTable();
+ void writeBuildId();
void applyRelocations();
llvm::Optional<coff_symbol16> createSymbol(Defined *D);
@@ -76,9 +135,7 @@ private:
std::map<StringRef, std::vector<DefinedImportData *>> binImports();
SymbolTable *Symtab;
- std::unique_ptr<llvm::FileOutputBuffer> Buffer;
- llvm::SpecificBumpPtrAllocator<OutputSection> CAlloc;
- llvm::SpecificBumpPtrAllocator<BaserelChunk> BAlloc;
+ std::unique_ptr<FileOutputBuffer> Buffer;
std::vector<OutputSection *> OutputSections;
std::vector<char> Strtab;
std::vector<llvm::object::coff_symbol16> OutputSymtab;
@@ -87,6 +144,11 @@ private:
EdataContents Edata;
std::unique_ptr<SEHTableChunk> SEHTable;
+ std::unique_ptr<Chunk> DebugDirectory;
+ std::vector<std::unique_ptr<Chunk>> DebugRecords;
+ CVDebugRecordChunk *BuildId = nullptr;
+ ArrayRef<uint8_t> SectionTable;
+
uint64_t FileSize;
uint32_t PointerToSymbolTable = 0;
uint64_t SizeOfImage;
@@ -239,6 +301,11 @@ void Writer::run() {
fixSafeSEHSymbols();
writeSections();
sortExceptionTable();
+ writeBuildId();
+
+ if (!Config->PDBPath.empty())
+ createPDB(Config->PDBPath, Symtab, SectionTable);
+
if (auto EC = Buffer->commit())
fatal(EC, "failed to write the output file");
}
@@ -274,7 +341,7 @@ void Writer::createSections() {
StringRef Name = getOutputSection(Pair.first);
OutputSection *&Sec = Sections[Name];
if (!Sec) {
- Sec = new (CAlloc.Allocate()) OutputSection(Name);
+ Sec = make<OutputSection>(Name);
OutputSections.push_back(Sec);
}
std::vector<Chunk *> &Chunks = Pair.second;
@@ -286,25 +353,46 @@ void Writer::createSections() {
}
void Writer::createMiscChunks() {
+ OutputSection *RData = createSection(".rdata");
+
// Create thunks for locally-dllimported symbols.
if (!Symtab->LocalImportChunks.empty()) {
- OutputSection *Sec = createSection(".rdata");
for (Chunk *C : Symtab->LocalImportChunks)
- Sec->addChunk(C);
+ RData->addChunk(C);
+ }
+
+ // Create Debug Information Chunks
+ if (Config->Debug) {
+ DebugDirectory = llvm::make_unique<DebugDirectoryChunk>(DebugRecords);
+
+ // TODO(compnerd) create a coffgrp entry if DebugType::CV is not enabled
+ if (Config->DebugTypes & static_cast<unsigned>(coff::DebugType::CV)) {
+ auto Chunk = llvm::make_unique<CVDebugRecordChunk>();
+
+ BuildId = Chunk.get();
+ DebugRecords.push_back(std::move(Chunk));
+ }
+
+ RData->addChunk(DebugDirectory.get());
+ for (const std::unique_ptr<Chunk> &C : DebugRecords)
+ RData->addChunk(C.get());
}
// Create SEH table. x86-only.
if (Config->Machine != I386)
return;
+
std::set<Defined *> Handlers;
+
for (lld::coff::ObjectFile *File : Symtab->ObjectFiles) {
if (!File->SEHCompat)
return;
for (SymbolBody *B : File->SEHandlers)
- Handlers.insert(cast<Defined>(B->repl()));
+ Handlers.insert(cast<Defined>(B));
}
+
SEHTable.reset(new SEHTableChunk(Handlers));
- createSection(".rdata")->addChunk(SEHTable.get());
+ RData->addChunk(SEHTable.get());
}
// Create .idata section for the DLL-imported symbol table.
@@ -340,7 +428,7 @@ void Writer::createImportTables() {
Sec->addChunk(C);
}
if (!DelayIdata.empty()) {
- Defined *Helper = cast<Defined>(Config->DelayLoadHelper->repl());
+ Defined *Helper = cast<Defined>(Config->DelayLoadHelper);
DelayIdata.create(Helper);
OutputSection *Sec = createSection(".didat");
for (Chunk *C : DelayIdata.getChunks())
@@ -383,6 +471,10 @@ size_t Writer::addEntryToStringTable(StringRef Str) {
}
Optional<coff_symbol16> Writer::createSymbol(Defined *Def) {
+ // Relative symbols are unrepresentable in a COFF symbol table.
+ if (isa<DefinedRelative>(Def))
+ return None;
+
if (auto *D = dyn_cast<DefinedRegular>(Def))
if (!D->getChunk()->isLive())
return None;
@@ -409,7 +501,6 @@ Optional<coff_symbol16> Writer::createSymbol(Defined *Def) {
switch (Def->kind()) {
case SymbolBody::DefinedAbsoluteKind:
- case SymbolBody::DefinedRelativeKind:
Sym.Value = Def->getRVA();
Sym.SectionNumber = IMAGE_SYM_ABSOLUTE;
break;
@@ -445,13 +536,11 @@ void Writer::createSymbolAndStringTable() {
for (lld::coff::ObjectFile *File : Symtab->ObjectFiles)
for (SymbolBody *B : File->getSymbols())
if (auto *D = dyn_cast<Defined>(B))
- if (Optional<coff_symbol16> Sym = createSymbol(D))
- OutputSymtab.push_back(*Sym);
-
- for (ImportFile *File : Symtab->ImportFiles)
- for (SymbolBody *B : File->getSymbols())
- if (Optional<coff_symbol16> Sym = createSymbol(cast<Defined>(B)))
- OutputSymtab.push_back(*Sym);
+ if (!D->WrittenToSymtab) {
+ D->WrittenToSymtab = true;
+ if (Optional<coff_symbol16> Sym = createSymbol(D))
+ OutputSymtab.push_back(*Sym);
+ }
OutputSection *LastSection = OutputSections.back();
// We position the symbol table to be adjacent to the end of the last section.
@@ -542,7 +631,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
PE->SizeOfImage = SizeOfImage;
PE->SizeOfHeaders = SizeOfHeaders;
if (!Config->NoEntry) {
- Defined *Entry = cast<Defined>(Config->Entry->repl());
+ Defined *Entry = cast<Defined>(Config->Entry);
PE->AddressOfEntryPoint = Entry->getRVA();
// Pointer to thumb code must have the LSB set, so adjust it.
if (Config->Machine == ARMNT)
@@ -584,33 +673,32 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
Dir[IAT].RelativeVirtualAddress = Idata.getIATRVA();
Dir[IAT].Size = Idata.getIATSize();
}
- if (!DelayIdata.empty()) {
- Dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress =
- DelayIdata.getDirRVA();
- Dir[DELAY_IMPORT_DESCRIPTOR].Size = DelayIdata.getDirSize();
- }
if (OutputSection *Sec = findSection(".rsrc")) {
Dir[RESOURCE_TABLE].RelativeVirtualAddress = Sec->getRVA();
Dir[RESOURCE_TABLE].Size = Sec->getVirtualSize();
}
- if (OutputSection *Sec = findSection(".reloc")) {
- Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = Sec->getRVA();
- Dir[BASE_RELOCATION_TABLE].Size = Sec->getVirtualSize();
- }
if (OutputSection *Sec = findSection(".pdata")) {
Dir[EXCEPTION_TABLE].RelativeVirtualAddress = Sec->getRVA();
Dir[EXCEPTION_TABLE].Size = Sec->getVirtualSize();
}
+ if (OutputSection *Sec = findSection(".reloc")) {
+ Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = Sec->getRVA();
+ Dir[BASE_RELOCATION_TABLE].Size = Sec->getVirtualSize();
+ }
if (Symbol *Sym = Symtab->findUnderscore("_tls_used")) {
- if (Defined *B = dyn_cast<Defined>(Sym->Body)) {
+ if (Defined *B = dyn_cast<Defined>(Sym->body())) {
Dir[TLS_TABLE].RelativeVirtualAddress = B->getRVA();
Dir[TLS_TABLE].Size = Config->is64()
? sizeof(object::coff_tls_directory64)
: sizeof(object::coff_tls_directory32);
}
}
+ if (Config->Debug) {
+ Dir[DEBUG_DIRECTORY].RelativeVirtualAddress = DebugDirectory->getRVA();
+ Dir[DEBUG_DIRECTORY].Size = DebugDirectory->getSize();
+ }
if (Symbol *Sym = Symtab->findUnderscore("_load_config_used")) {
- if (auto *B = dyn_cast<DefinedRegular>(Sym->Body)) {
+ if (auto *B = dyn_cast<DefinedRegular>(Sym->body())) {
SectionChunk *SC = B->getChunk();
assert(B->getRVA() >= SC->getRVA());
uint64_t OffsetInChunk = B->getRVA() - SC->getRVA();
@@ -626,12 +714,19 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
Dir[LOAD_CONFIG_TABLE].Size = LoadConfigSize;
}
}
+ if (!DelayIdata.empty()) {
+ Dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress =
+ DelayIdata.getDirRVA();
+ Dir[DELAY_IMPORT_DESCRIPTOR].Size = DelayIdata.getDirSize();
+ }
// Write section table
for (OutputSection *Sec : OutputSections) {
Sec->writeHeaderTo(Buf);
Buf += sizeof(coff_section);
}
+ SectionTable = ArrayRef<uint8_t>(
+ Buf - OutputSections.size() * sizeof(coff_section), Buf);
if (OutputSymtab.empty())
return;
@@ -660,8 +755,10 @@ void Writer::openFile(StringRef Path) {
void Writer::fixSafeSEHSymbols() {
if (!SEHTable)
return;
- Config->SEHTable->setRVA(SEHTable->getRVA());
- Config->SEHCount->setVA(SEHTable->getSize() / 4);
+ if (auto *T = dyn_cast<DefinedRelative>(Config->SEHTable->body()))
+ T->setRVA(SEHTable->getRVA());
+ if (auto *C = dyn_cast<DefinedAbsolute>(Config->SEHCount->body()))
+ C->setVA(SEHTable->getSize() / 4);
}
// Handles /section options to allow users to overwrite
@@ -715,6 +812,30 @@ void Writer::sortExceptionTable() {
errs() << "warning: don't know how to handle .pdata.\n";
}
+// Backfill the CVSignature in a PDB70 Debug Record. This backfilling allows us
+// to get reproducible builds.
+void Writer::writeBuildId() {
+ // There is nothing to backfill if BuildId was not setup.
+ if (BuildId == nullptr)
+ return;
+
+ MD5 Hash;
+ MD5::MD5Result Res;
+
+ Hash.update(ArrayRef<uint8_t>{Buffer->getBufferStart(),
+ Buffer->getBufferEnd()});
+ Hash.final(Res);
+
+ assert(BuildId->DI->Signature.CVSignature == OMF::Signature::PDB70 &&
+ "only PDB 7.0 is supported");
+ assert(sizeof(Res) == sizeof(BuildId->DI->PDB70.Signature) &&
+ "signature size mismatch");
+ memcpy(BuildId->DI->PDB70.Signature, Res,
+ sizeof(codeview::PDB70DebugInfo::Signature));
+ // TODO(compnerd) track the Age
+ BuildId->DI->PDB70.Age = 1;
+}
+
OutputSection *Writer::findSection(StringRef Name) {
for (OutputSection *Sec : OutputSections)
if (Sec->getName() == Name)
@@ -744,16 +865,13 @@ OutputSection *Writer::createSection(StringRef Name) {
uint32_t Perms = StringSwitch<uint32_t>(Name)
.Case(".bss", BSS | R | W)
.Case(".data", DATA | R | W)
- .Case(".didat", DATA | R)
- .Case(".edata", DATA | R)
- .Case(".idata", DATA | R)
- .Case(".rdata", DATA | R)
+ .Cases(".didat", ".edata", ".idata", ".rdata", DATA | R)
.Case(".reloc", DATA | DISCARDABLE | R)
.Case(".text", CODE | R | X)
.Default(0);
if (!Perms)
llvm_unreachable("unknown section name");
- auto Sec = new (CAlloc.Allocate()) OutputSection(Name);
+ auto Sec = make<OutputSection>(Name);
Sec->addPermissions(Perms);
OutputSections.push_back(Sec);
return Sec;
@@ -784,13 +902,11 @@ void Writer::addBaserelBlocks(OutputSection *Dest, std::vector<Baserel> &V) {
uint32_t P = V[J].RVA & Mask;
if (P == Page)
continue;
- BaserelChunk *Buf = BAlloc.Allocate();
- Dest->addChunk(new (Buf) BaserelChunk(Page, &V[I], &V[0] + J));
+ Dest->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J));
I = J;
Page = P;
}
if (I == J)
return;
- BaserelChunk *Buf = BAlloc.Allocate();
- Dest->addChunk(new (Buf) BaserelChunk(Page, &V[I], &V[0] + J));
+ Dest->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J));
}
diff --git a/COFF/Writer.h b/COFF/Writer.h
index 0473315ae50a..0d26090177d8 100644
--- a/COFF/Writer.h
+++ b/COFF/Writer.h
@@ -14,9 +14,7 @@
namespace lld {
namespace coff {
-
-class Chunk;
-class OutputSection;
+class SymbolTable;
void writeResult(SymbolTable *T);