diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 19:19:15 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 19:19:15 +0000 |
commit | d93e1dfac8711cfed1a9d9cd1876a788b83945cd (patch) | |
tree | 5896fa6c02a262a6148b215487e545d937de58b7 /COFF | |
parent | 8d43286d630f9224de07809ea253e83ebb9cdee6 (diff) | |
download | src-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.txt | 18 | ||||
-rw-r--r-- | COFF/Chunks.cpp | 32 | ||||
-rw-r--r-- | COFF/Chunks.h | 7 | ||||
-rw-r--r-- | COFF/Config.h | 28 | ||||
-rw-r--r-- | COFF/DLL.cpp | 2 | ||||
-rw-r--r-- | COFF/Driver.cpp | 428 | ||||
-rw-r--r-- | COFF/Driver.h | 38 | ||||
-rw-r--r-- | COFF/DriverUtils.cpp | 194 | ||||
-rw-r--r-- | COFF/Error.cpp | 21 | ||||
-rw-r--r-- | COFF/Error.h | 17 | ||||
-rw-r--r-- | COFF/ICF.cpp | 278 | ||||
-rw-r--r-- | COFF/InputFiles.cpp | 179 | ||||
-rw-r--r-- | COFF/InputFiles.h | 68 | ||||
-rw-r--r-- | COFF/Librarian.cpp | 39 | ||||
-rw-r--r-- | COFF/MarkLive.cpp | 6 | ||||
-rw-r--r-- | COFF/Memory.h | 52 | ||||
-rw-r--r-- | COFF/ModuleDef.cpp | 12 | ||||
-rw-r--r-- | COFF/Options.td | 8 | ||||
-rw-r--r-- | COFF/PDB.cpp | 214 | ||||
-rw-r--r-- | COFF/PDB.h | 25 | ||||
-rw-r--r-- | COFF/Strings.cpp | 30 | ||||
-rw-r--r-- | COFF/Strings.h | 23 | ||||
-rw-r--r-- | COFF/SymbolTable.cpp | 494 | ||||
-rw-r--r-- | COFF/SymbolTable.h | 66 | ||||
-rw-r--r-- | COFF/Symbols.cpp | 178 | ||||
-rw-r--r-- | COFF/Symbols.h | 142 | ||||
-rw-r--r-- | COFF/Writer.cpp | 198 | ||||
-rw-r--r-- | COFF/Writer.h | 4 |
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); |