diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2017-04-16 16:03:39 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2017-04-16 16:03:39 +0000 |
| commit | d2d3ebb81992e107edf95c1c136d7a342d9b1418 (patch) | |
| tree | bb1af8fff2b1400cf240e3b2532a1e5d22a121da /COFF | |
| parent | 16787c9ce0b96aaa669d7fab3a495916b35ce758 (diff) | |
Notes
Diffstat (limited to 'COFF')
| -rw-r--r-- | COFF/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | COFF/Chunks.cpp | 9 | ||||
| -rw-r--r-- | COFF/Chunks.h | 4 | ||||
| -rw-r--r-- | COFF/Config.h | 14 | ||||
| -rw-r--r-- | COFF/Driver.cpp | 287 | ||||
| -rw-r--r-- | COFF/Driver.h | 4 | ||||
| -rw-r--r-- | COFF/DriverUtils.cpp | 71 | ||||
| -rw-r--r-- | COFF/Error.cpp | 74 | ||||
| -rw-r--r-- | COFF/Error.h | 10 | ||||
| -rw-r--r-- | COFF/ICF.cpp | 9 | ||||
| -rw-r--r-- | COFF/InputFiles.cpp | 94 | ||||
| -rw-r--r-- | COFF/InputFiles.h | 11 | ||||
| -rw-r--r-- | COFF/LTO.cpp | 140 | ||||
| -rw-r--r-- | COFF/LTO.h | 56 | ||||
| -rw-r--r-- | COFF/Librarian.cpp | 13 | ||||
| -rw-r--r-- | COFF/MapFile.cpp | 114 | ||||
| -rw-r--r-- | COFF/MapFile.h | 22 | ||||
| -rw-r--r-- | COFF/ModuleDef.cpp | 22 | ||||
| -rw-r--r-- | COFF/Options.td | 8 | ||||
| -rw-r--r-- | COFF/PDB.cpp | 109 | ||||
| -rw-r--r-- | COFF/SymbolTable.cpp | 173 | ||||
| -rw-r--r-- | COFF/SymbolTable.h | 17 | ||||
| -rw-r--r-- | COFF/Symbols.cpp | 9 | ||||
| -rw-r--r-- | COFF/Symbols.h | 78 | ||||
| -rw-r--r-- | COFF/Writer.cpp | 61 | ||||
| -rw-r--r-- | COFF/Writer.h | 51 |
26 files changed, 1034 insertions, 431 deletions
diff --git a/COFF/CMakeLists.txt b/COFF/CMakeLists.txt index 70a33b9fdd81..8f24e36c0eca 100644 --- a/COFF/CMakeLists.txt +++ b/COFF/CMakeLists.txt @@ -15,6 +15,8 @@ add_lld_library(lldCOFF ICF.cpp InputFiles.cpp Librarian.cpp + LTO.cpp + MapFile.cpp MarkLive.cpp ModuleDef.cpp PDB.cpp @@ -25,6 +27,7 @@ add_lld_library(lldCOFF LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} + BitReader Core DebugInfoCodeView DebugInfoMSF @@ -40,7 +43,7 @@ add_lld_library(lldCOFF LINK_LIBS lldCore - ${PTHREAD_LIB} + ${LLVM_PTHREAD_LIB} DEPENDS COFFOptionsTableGen diff --git a/COFF/Chunks.cpp b/COFF/Chunks.cpp index 7f0dfa92ec10..10eeedd88e55 100644 --- a/COFF/Chunks.cpp +++ b/COFF/Chunks.cpp @@ -11,6 +11,7 @@ #include "Error.h" #include "InputFiles.h" #include "Symbols.h" +#include "llvm/ADT/Twine.h" #include "llvm/Object/COFF.h" #include "llvm/Support/COFF.h" #include "llvm/Support/Debug.h" @@ -61,7 +62,7 @@ void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, Defined *Sym, case IMAGE_REL_AMD64_SECTION: add16(Off, Sym->getSectionIndex()); break; case IMAGE_REL_AMD64_SECREL: add32(Off, Sym->getSecrel()); break; default: - fatal("unsupported relocation type"); + fatal("unsupported relocation type 0x" + Twine::utohexstr(Type)); } } @@ -76,7 +77,7 @@ void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, Defined *Sym, case IMAGE_REL_I386_SECTION: add16(Off, Sym->getSectionIndex()); break; case IMAGE_REL_I386_SECREL: add32(Off, Sym->getSecrel()); break; default: - fatal("unsupported relocation type"); + fatal("unsupported relocation type 0x" + Twine::utohexstr(Type)); } } @@ -136,7 +137,7 @@ void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, Defined *Sym, 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"); + fatal("unsupported relocation type 0x" + Twine::utohexstr(Type)); } } @@ -226,7 +227,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) - outs() << "Discarded " << Sym->getName() << "\n"; + message("Discarded " + Sym->getName()); } StringRef SectionChunk::getDebugName() { diff --git a/COFF/Chunks.h b/COFF/Chunks.h index 59e36b84c9b0..44d7f31afc67 100644 --- a/COFF/Chunks.h +++ b/COFF/Chunks.h @@ -187,10 +187,10 @@ public: const coff_section *Header; -private: - // A file this chunk was created from. + // The file that this chunk was created from. ObjectFile *File; +private: StringRef SectionName; std::vector<SectionChunk *> AssocChildren; llvm::iterator_range<const coff_relocation *> Relocs; diff --git a/COFF/Config.h b/COFF/Config.h index 0fa3338aa28c..31534aeb3971 100644 --- a/COFF/Config.h +++ b/COFF/Config.h @@ -80,14 +80,16 @@ struct Configuration { SymbolBody *Entry = nullptr; bool NoEntry = false; std::string OutputFile; + bool ColorDiagnostics; bool DoGC = true; bool DoICF = true; + uint64_t ErrorLimit = 20; bool Relocatable = true; bool Force = false; bool Debug = false; bool WriteSymtab = true; unsigned DebugTypes = static_cast<unsigned>(DebugType::None); - StringRef PDBPath; + llvm::SmallString<128> PDBPath; // Symbols in this set are considered as live by the garbage collector. std::set<SymbolBody *> GCRoot; @@ -103,6 +105,8 @@ struct Configuration { std::map<std::string, int> DLLOrder; SymbolBody *DelayLoadHelper = nullptr; + bool SaveTemps = false; + // Used for SafeSEH. Symbol *SEHTable = nullptr; Symbol *SEHCount = nullptr; @@ -111,7 +115,9 @@ struct Configuration { unsigned LTOOptLevel = 2; // Used for /opt:lldltojobs=N - unsigned LTOJobs = 1; + unsigned LTOJobs = 0; + // Used for /opt:lldltopartitions=N + unsigned LTOPartitions = 1; // Used for /merge:from=to (e.g. /merge:.rdata=.text) std::map<StringRef, StringRef> Merge; @@ -135,6 +141,9 @@ struct Configuration { // Used for /alternatename. std::map<StringRef, StringRef> AlternateNames; + // Used for /lldmap. + std::string MapFile; + uint64_t ImageBase = -1; uint64_t StackReserve = 1024 * 1024; uint64_t StackCommit = 4096; @@ -151,6 +160,7 @@ struct Configuration { bool TerminalServerAware = true; bool LargeAddressAware = false; bool HighEntropyVA = false; + bool AppContainer = false; // This is for debugging. bool DebugPdb = false; diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp index 4dabd9ebcc6d..3e7f10bf8d11 100644 --- a/COFF/Driver.cpp +++ b/COFF/Driver.cpp @@ -19,6 +19,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/LibDriver/LibDriver.h" +#include "llvm/Object/ArchiveWriter.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" @@ -31,17 +32,11 @@ #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; -using llvm::sys::fs::OpenFlags; using llvm::sys::fs::file_magic; using llvm::sys::fs::identify_magic; @@ -55,11 +50,16 @@ BumpPtrAllocator BAlloc; StringSaver Saver{BAlloc}; std::vector<SpecificAllocBase *> SpecificAllocBase::Instances; -bool link(ArrayRef<const char *> Args) { +bool link(ArrayRef<const char *> Args, raw_ostream &Diag) { + ErrorCount = 0; + ErrorOS = &Diag; + Argv0 = Args[0]; Config = make<Configuration>(); + Config->ColorDiagnostics = + (ErrorOS == &llvm::errs() && Process::StandardErrHasColors()); Driver = make<LinkerDriver>(); Driver->link(Args); - return true; + return !ErrorCount; } // Drop directory components and replace extension with ".exe" or ".dll". @@ -121,10 +121,12 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> MB) { return Symtab.addFile(make<ArchiveFile>(MBRef)); if (Magic == file_magic::bitcode) return Symtab.addFile(make<BitcodeFile>(MBRef)); + if (Magic == file_magic::coff_cl_gl_object) - fatal(MBRef.getBufferIdentifier() + ": is not a native COFF file. " + error(MBRef.getBufferIdentifier() + ": is not a native COFF file. " "Recompile without /GL"); - Symtab.addFile(make<ObjectFile>(MBRef)); + else + Symtab.addFile(make<ObjectFile>(MBRef)); } void LinkerDriver::enqueuePath(StringRef Path) { @@ -134,12 +136,10 @@ void LinkerDriver::enqueuePath(StringRef Path) { enqueueTask([=]() { auto MBOrErr = Future->get(); if (MBOrErr.second) - fatal(MBOrErr.second, "could not open " + PathStr); - Driver->addBuffer(std::move(MBOrErr.first)); + error("could not open " + PathStr + ": " + MBOrErr.second.message()); + else + Driver->addBuffer(std::move(MBOrErr.first)); }); - - if (Config->OutputFile == "") - Config->OutputFile = getOutputPath(Path); } void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName, @@ -151,17 +151,18 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName, } InputFile *Obj; - if (Magic == file_magic::coff_object) + if (Magic == file_magic::coff_object) { Obj = make<ObjectFile>(MB); - else if (Magic == file_magic::bitcode) + } else if (Magic == file_magic::bitcode) { Obj = make<BitcodeFile>(MB); - else - fatal("unknown file type: " + MB.getBufferIdentifier()); + } else { + error("unknown file type: " + MB.getBufferIdentifier()); + return; + } Obj->ParentName = ParentName; Symtab.addFile(Obj); - if (Config->Verbose) - outs() << "Loaded " << toString(Obj) << " for " << SymName << "\n"; + log("Loaded " + toString(Obj) + " for " + SymName); } void LinkerDriver::enqueueArchiveMember(const Archive::Child &C, @@ -234,7 +235,7 @@ void LinkerDriver::parseDirectives(StringRef S) { case OPT_throwingnew: break; default: - fatal(Arg->getSpelling() + " is not allowed in .drectve"); + error(Arg->getSpelling() + " is not allowed in .drectve"); } } } @@ -402,7 +403,8 @@ static unsigned parseDebugType(StringRef Arg) { 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)); + .Case("fixup", static_cast<unsigned>(DebugType::Fixup)) + .Default(0); return DebugTypes; } @@ -418,6 +420,132 @@ static std::string getMapFile(const opt::InputArgList &Args) { return (OutFile.substr(0, OutFile.rfind('.')) + ".map").str(); } +std::vector<MemoryBufferRef> getArchiveMembers(Archive *File) { + std::vector<MemoryBufferRef> V; + Error Err = Error::success(); + for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) { + Archive::Child C = + check(COrErr, + File->getFileName() + ": could not get the child of the archive"); + MemoryBufferRef MBRef = + check(C.getMemoryBufferRef(), + File->getFileName() + + ": could not get the buffer for a child of the archive"); + V.push_back(MBRef); + } + if (Err) + fatal(File->getFileName() + + ": Archive::children failed: " + toString(std::move(Err))); + return V; +} + +// A helper function for filterBitcodeFiles. +static bool needsRebuilding(MemoryBufferRef MB) { + // The MSVC linker doesn't support thin archives, so if it's a thin + // archive, we always need to rebuild it. + std::unique_ptr<Archive> File = + check(Archive::create(MB), "Failed to read " + MB.getBufferIdentifier()); + if (File->isThin()) + return true; + + // Returns true if the archive contains at least one bitcode file. + for (MemoryBufferRef Member : getArchiveMembers(File.get())) + if (identify_magic(Member.getBuffer()) == file_magic::bitcode) + return true; + return false; +} + +// Opens a given path as an archive file and removes bitcode files +// from them if exists. This function is to appease the MSVC linker as +// their linker doesn't like archive files containing non-native +// object files. +// +// If a given archive doesn't contain bitcode files, the archive path +// is returned as-is. Otherwise, a new temporary file is created and +// its path is returned. +static Optional<std::string> +filterBitcodeFiles(StringRef Path, std::vector<std::string> &TemporaryFiles) { + std::unique_ptr<MemoryBuffer> MB = check( + MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path); + MemoryBufferRef MBRef = MB->getMemBufferRef(); + file_magic Magic = identify_magic(MBRef.getBuffer()); + + if (Magic == file_magic::bitcode) + return None; + if (Magic != file_magic::archive) + return Path.str(); + if (!needsRebuilding(MBRef)) + return Path.str(); + + std::unique_ptr<Archive> File = + check(Archive::create(MBRef), + MBRef.getBufferIdentifier() + ": failed to parse archive"); + + std::vector<NewArchiveMember> New; + for (MemoryBufferRef Member : getArchiveMembers(File.get())) + if (identify_magic(Member.getBuffer()) != file_magic::bitcode) + New.emplace_back(Member); + + if (New.empty()) + return None; + + log("Creating a temporary archive for " + Path + " to remove bitcode files"); + + SmallString<128> S; + if (auto EC = sys::fs::createTemporaryFile("lld-" + sys::path::stem(Path), + ".lib", S)) + fatal(EC, "cannot create a temporary file"); + std::string Temp = S.str(); + TemporaryFiles.push_back(Temp); + + std::pair<StringRef, std::error_code> Ret = + llvm::writeArchive(Temp, New, /*WriteSymtab=*/true, Archive::Kind::K_GNU, + /*Deterministics=*/true, + /*Thin=*/false); + if (Ret.second) + error("failed to create a new archive " + S.str() + ": " + Ret.first); + return Temp; +} + +// Create response file contents and invoke the MSVC linker. +void LinkerDriver::invokeMSVC(opt::InputArgList &Args) { + std::string Rsp = "/nologo "; + std::vector<std::string> Temps; + + for (auto *Arg : Args) { + switch (Arg->getOption().getID()) { + case OPT_linkrepro: + case OPT_lldmap: + case OPT_lldmap_file: + case OPT_lldsavetemps: + case OPT_msvclto: + // LLD-specific options are stripped. + break; + case OPT_opt: + if (!StringRef(Arg->getValue()).startswith("lld")) + Rsp += toString(Arg) + " "; + break; + case OPT_INPUT: { + if (Optional<StringRef> Path = doFindFile(Arg->getValue())) { + if (Optional<std::string> S = filterBitcodeFiles(*Path, Temps)) + Rsp += quote(*S) + " "; + continue; + } + Rsp += quote(Arg->getValue()) + " "; + break; + } + default: + Rsp += toString(Arg) + " "; + } + } + + std::vector<StringRef> ObjectFiles = Symtab.compileBitcodeFiles(); + runMSVCLinker(Rsp, ObjectFiles); + + for (StringRef Path : Temps) + sys::fs::remove(Path); +} + void LinkerDriver::enqueueTask(std::function<void()> Task) { TaskQueue.push_back(std::move(Task)); } @@ -451,6 +579,22 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // Parse command line options. opt::InputArgList Args = Parser.parseLINK(ArgsArr.slice(1)); + // Parse and evaluate -mllvm options. + std::vector<const char *> V; + V.push_back("lld-link (LLVM option parsing)"); + for (auto *Arg : Args.filtered(OPT_mllvm)) + V.push_back(Arg->getValue()); + cl::ParseCommandLineOptions(V.size(), V.data()); + + // Handle /errorlimit early, because error() depends on it. + if (auto *Arg = Args.getLastArg(OPT_errorlimit)) { + int N = 20; + StringRef S = Arg->getValue(); + if (S.getAsInteger(10, N)) + error(Arg->getSpelling() + " number expected, but got " + S); + Config->ErrorLimit = N; + } + // Handle /help if (Args.hasArg(OPT_help)) { printHelp(ArgsArr[0]); @@ -467,12 +611,12 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { if (ErrOrWriter) { Tar = std::move(*ErrOrWriter); } else { - errs() << "/linkrepro: failed to open " << Path << ": " - << toString(ErrOrWriter.takeError()) << '\n'; + error("/linkrepro: failed to open " + Path + ": " + + toString(ErrOrWriter.takeError())); } } - if (Args.filtered_begin(OPT_INPUT) == Args.filtered_end()) + if (!Args.hasArgNoClaim(OPT_INPUT)) fatal("no input files"); // Construct search path list. @@ -508,9 +652,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // Handle /noentry if (Args.hasArg(OPT_noentry)) { - if (!Args.hasArg(OPT_dll)) - fatal("/noentry must be specified with /dll"); - Config->NoEntry = true; + if (Args.hasArg(OPT_dll)) + Config->NoEntry = true; + else + error("/noentry must be specified with /dll"); } // Handle /dll @@ -521,12 +666,17 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // Handle /fixed if (Args.hasArg(OPT_fixed)) { - if (Args.hasArg(OPT_dynamicbase)) - fatal("/fixed must not be specified with /dynamicbase"); - Config->Relocatable = false; - Config->DynamicBase = false; + if (Args.hasArg(OPT_dynamicbase)) { + error("/fixed must not be specified with /dynamicbase"); + } else { + Config->Relocatable = false; + Config->DynamicBase = false; + } } + if (Args.hasArg(OPT_appcontainer)) + Config->AppContainer = true; + // Handle /machine if (auto *Arg = Args.getLastArg(OPT_machine)) Config->Machine = getMachineType(Arg->getValue()); @@ -596,20 +746,31 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { StringRef OptLevel = StringRef(S).substr(7); if (OptLevel.getAsInteger(10, Config->LTOOptLevel) || Config->LTOOptLevel > 3) - fatal("/opt:lldlto: invalid optimization level: " + OptLevel); + error("/opt:lldlto: invalid optimization level: " + OptLevel); continue; } if (StringRef(S).startswith("lldltojobs=")) { StringRef Jobs = StringRef(S).substr(11); if (Jobs.getAsInteger(10, Config->LTOJobs) || Config->LTOJobs == 0) - fatal("/opt:lldltojobs: invalid job count: " + Jobs); + error("/opt:lldltojobs: invalid job count: " + Jobs); + continue; + } + if (StringRef(S).startswith("lldltopartitions=")) { + StringRef N = StringRef(S).substr(17); + if (N.getAsInteger(10, Config->LTOPartitions) || + Config->LTOPartitions == 0) + error("/opt:lldltopartitions: invalid partition count: " + N); continue; } if (S != "ref" && S != "lbr" && S != "nolbr") - fatal("/opt: unknown option: " + S); + error("/opt: unknown option: " + S); } } + // Handle /lldsavetemps + if (Args.hasArg(OPT_lldsavetemps)) + Config->SaveTemps = true; + // Handle /failifmismatch for (auto *Arg : Args.filtered(OPT_failifmismatch)) checkFailIfMismatch(Arg->getValue()); @@ -658,6 +819,11 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { Config->DumpPdb = Args.hasArg(OPT_dumppdb); Config->DebugPdb = Args.hasArg(OPT_debugpdb); + Config->MapFile = getMapFile(Args); + + if (ErrorCount) + return; + // Create a list of input files. Files can be given as arguments // for /defaultlib option. std::vector<MemoryBufferRef> MBs; @@ -678,7 +844,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // 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) { - errs() << "warning: /machine is not specified. x64 is assumed.\n"; + warn("/machine is not specified. x64 is assumed"); Config->Machine = AMD64; } @@ -715,8 +881,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { if (S.empty()) fatal("entry point must be defined"); Config->Entry = addUndefined(S); - if (Config->Verbose) - outs() << "Entry name inferred: " << S << "\n"; + log("Entry name inferred: " + S); } // Handle /export @@ -749,6 +914,22 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { } } + // Set default image name if neither /out or /def set it. + if (Config->OutputFile.empty()) { + Config->OutputFile = + getOutputPath((*Args.filtered(OPT_INPUT).begin())->getValue()); + } + + // Put the PDB next to the image if no /pdb flag was passed. + if (Config->Debug && Config->PDBPath.empty()) { + Config->PDBPath = Config->OutputFile; + sys::path::replace_extension(Config->PDBPath, ".pdb"); + } + + // Disable PDB generation if the user requested it. + if (Args.hasArg(OPT_nopdb)) + Config->PDBPath = ""; + // Set default image base if /base is not given. if (Config->ImageBase == uint64_t(-1)) Config->ImageBase = getDefaultImageBase(); @@ -801,6 +982,16 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { addUndefined(mangle("_load_config_used")); } while (run()); + if (ErrorCount) + return; + + // If /msvclto is given, we use the MSVC linker to link LTO output files. + // This is useful because MSVC link.exe can generate complete PDBs. + if (Args.hasArg(OPT_msvclto)) { + invokeMSVC(Args); + exit(0); + } + // Do LTO by compiling bitcode input files to a set of native COFF files then // link those files. Symtab.addCombinedLTOObjects(); @@ -818,10 +1009,13 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { } // Handle /safeseh. - if (Args.hasArg(OPT_safeseh)) + if (Args.hasArg(OPT_safeseh)) { for (ObjectFile *File : Symtab.ObjectFiles) if (!File->SEHCompat) - fatal("/safeseh: " + File->getName() + " is not compatible with SEH"); + error("/safeseh: " + File->getName() + " is not compatible with SEH"); + if (ErrorCount) + return; + } // Windows specific -- when we are creating a .dll file, we also // need to create a .lib file. @@ -846,17 +1040,6 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // Write the result. writeResult(&Symtab); - // Create a symbol map file containing symbol VAs and their names - // to help debugging. - std::string MapFile = getMapFile(Args); - if (!MapFile.empty()) { - std::error_code EC; - raw_fd_ostream Out(MapFile, EC, OpenFlags::F_Text); - if (EC) - 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 44894269fcbe..4566f73eef31 100644 --- a/COFF/Driver.h +++ b/COFF/Driver.h @@ -107,6 +107,8 @@ private: StringRef findDefaultEntry(); WindowsSubsystem inferSubsystem(); + void invokeMSVC(llvm::opt::InputArgList &Args); + MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> MB); void addBuffer(std::unique_ptr<MemoryBuffer> MB); void addArchiveBuffer(MemoryBufferRef MBRef, StringRef SymName, @@ -178,6 +180,8 @@ void checkFailIfMismatch(StringRef Arg); std::unique_ptr<MemoryBuffer> convertResToCOFF(const std::vector<MemoryBufferRef> &MBs); +void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects); + // 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 14dd004f1c04..a9c1c9d5593e 100644 --- a/COFF/DriverUtils.cpp +++ b/COFF/DriverUtils.cpp @@ -44,31 +44,33 @@ namespace { class Executor { public: explicit Executor(StringRef S) : Saver(Alloc), Prog(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 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 run() { 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).data(); + StringRef Exe = Saver.save(*ExeOrErr); Args.insert(Args.begin(), Exe); - Args.push_back(nullptr); - if (sys::ExecuteAndWait(Args[0], Args.data()) != 0) { - for (const char *S : Args) - if (S) - errs() << S << " "; - fatal("ExecuteAndWait failed"); - } + + std::vector<const char *> Vec; + for (StringRef S : Args) + Vec.push_back(S.data()); + Vec.push_back(nullptr); + + if (sys::ExecuteAndWait(Args[0], Vec.data()) != 0) + fatal("ExecuteAndWait failed: " + + llvm::join(Args.begin(), Args.end(), " ")); } private: BumpPtrAllocator Alloc; StringSaver Saver; StringRef Prog; - std::vector<const char *> Args; + std::vector<StringRef> Args; }; } // anonymous namespace @@ -167,8 +169,7 @@ void parseMerge(StringRef S) { if (!Inserted) { StringRef Existing = Pair.first->second; if (Existing != To) - errs() << "warning: " << S << ": already merged into " << Existing - << "\n"; + warn(S + ": already merged into " + Existing); } } @@ -282,11 +283,19 @@ static void quoteAndPrint(raw_ostream &Out, StringRef S) { namespace { class TemporaryFile { public: - TemporaryFile(StringRef Prefix, StringRef Extn) { + TemporaryFile(StringRef Prefix, StringRef Extn, StringRef Contents = "") { SmallString<128> S; if (auto EC = sys::fs::createTemporaryFile("lld-" + Prefix, Extn, S)) fatal(EC, "cannot create a temporary file"); Path = S.str(); + + if (!Contents.empty()) { + std::error_code EC; + raw_fd_ostream OS(Path, EC, sys::fs::F_None); + if (EC) + fatal(EC, "failed to open " + Path); + OS << Contents; + } } TemporaryFile(TemporaryFile &&Obj) { @@ -542,7 +551,7 @@ void fixupExports() { Export *Existing = Pair.first->second; if (E == *Existing || E.Name != Existing->Name) continue; - errs() << "warning: duplicate /export option: " << E.Name << "\n"; + warn("duplicate /export option: " + E.Name); } Config->Exports = std::move(V); @@ -617,6 +626,26 @@ convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) { return File.getMemoryBuffer(); } +// Run MSVC link.exe for given in-memory object files. +// Command line options are copied from those given to LLD. +// This is for the /msvclto option. +void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects) { + // Write the in-memory object files to disk. + std::vector<TemporaryFile> Temps; + for (StringRef S : Objects) { + Temps.emplace_back("lto", "obj", S); + Rsp += quote(Temps.back().Path) + " "; + } + + log("link.exe " + Rsp); + + // Run MSVC link.exe. + Temps.emplace_back("lto", "rsp", Rsp); + Executor E("link.exe"); + E.add(Twine("@" + Temps.back().Path)); + E.run(); +} + // Create OptTable // Create prefix string literals used in Options.td @@ -653,16 +682,16 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) { // Print the real command line if response files are expanded. if (Args.hasArg(OPT_verbose) && ArgsArr.size() != Argv.size()) { - outs() << "Command line:"; + std::string Msg = "Command line:"; for (const char *S : Argv) - outs() << " " << S; - outs() << "\n"; + Msg += " " + std::string(S); + message(Msg); } if (MissingCount) fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument"); for (auto *Arg : Args.filtered(OPT_UNKNOWN)) - errs() << "ignoring unknown argument: " << Arg->getSpelling() << "\n"; + warn("ignoring unknown argument: " + Arg->getSpelling()); return Args; } diff --git a/COFF/Error.cpp b/COFF/Error.cpp index b2bd557413df..b2c7c89bd36c 100644 --- a/COFF/Error.cpp +++ b/COFF/Error.cpp @@ -8,11 +8,14 @@ //===----------------------------------------------------------------------===// #include "Error.h" +#include "Config.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/Error.h" +#include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Process.h" #include "llvm/Support/raw_ostream.h" +#include <mutex> #if !defined(_MSC_VER) && !defined(__MINGW32__) #include <unistd.h> @@ -21,10 +24,68 @@ using namespace llvm; namespace lld { +// The functions defined in this file can be called from multiple threads, +// but outs() or errs() are not thread-safe. We protect them using a mutex. +static std::mutex Mu; + namespace coff { +StringRef Argv0; +uint64_t ErrorCount; +raw_ostream *ErrorOS; + +static LLVM_ATTRIBUTE_NORETURN void exitLld(int Val) { + // Dealloc/destroy ManagedStatic variables before calling + // _exit(). In a non-LTO build, this is a nop. In an LTO + // build allows us to get the output of -time-passes. + llvm_shutdown(); + + outs().flush(); + errs().flush(); + _exit(Val); +} + +static void print(StringRef S, raw_ostream::Colors C) { + *ErrorOS << Argv0 + ": "; + if (Config->ColorDiagnostics) { + ErrorOS->changeColor(C, true); + *ErrorOS << S; + ErrorOS->resetColor(); + } else { + *ErrorOS << S; + } +} + +void log(const Twine &Msg) { + if (Config->Verbose) { + std::lock_guard<std::mutex> Lock(Mu); + outs() << Argv0 << ": " << Msg << "\n"; + } +} + +void message(const Twine &Msg) { + std::lock_guard<std::mutex> Lock(Mu); + outs() << Msg << "\n"; + outs().flush(); +} + +void error(const Twine &Msg) { + std::lock_guard<std::mutex> Lock(Mu); + + if (Config->ErrorLimit == 0 || ErrorCount < Config->ErrorLimit) { + print("error: ", raw_ostream::RED); + *ErrorOS << Msg << "\n"; + } else if (ErrorCount == Config->ErrorLimit) { + print("error: ", raw_ostream::RED); + *ErrorOS << "too many errors emitted, stopping now" + << " (use /ERRORLIMIT:0 to see all errors)\n"; + exitLld(1); + } + + ++ErrorCount; +} void fatal(const Twine &Msg) { - if (sys::Process::StandardErrHasColors()) { + if (Config->ColorDiagnostics) { errs().changeColor(raw_ostream::RED, /*bold=*/true); errs() << "error: "; errs().resetColor(); @@ -32,10 +93,7 @@ void fatal(const Twine &Msg) { errs() << "error: "; } errs() << Msg << "\n"; - - outs().flush(); - errs().flush(); - _exit(1); + exitLld(1); } void fatal(std::error_code EC, const Twine &Msg) { @@ -46,5 +104,11 @@ void fatal(llvm::Error &Err, const Twine &Msg) { fatal(errorToErrorCode(std::move(Err)), Msg); } +void warn(const Twine &Msg) { + std::lock_guard<std::mutex> Lock(Mu); + print("warning: ", raw_ostream::MAGENTA); + *ErrorOS << Msg << "\n"; +} + } // namespace coff } // namespace lld diff --git a/COFF/Error.h b/COFF/Error.h index 47549327db2b..a4f44fb1e36c 100644 --- a/COFF/Error.h +++ b/COFF/Error.h @@ -16,11 +16,19 @@ namespace lld { namespace coff { +extern uint64_t ErrorCount; +extern llvm::raw_ostream *ErrorOS; +extern llvm::StringRef Argv0; + +void log(const Twine &Msg); +void message(const Twine &Msg); +void warn(const Twine &Msg); +void error(const Twine &Msg); LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg); LLVM_ATTRIBUTE_NORETURN void fatal(std::error_code EC, const Twine &Prefix); LLVM_ATTRIBUTE_NORETURN void fatal(llvm::Error &Err, const Twine &Prefix); -template <class T> T check(ErrorOr<T> &&V, const Twine &Prefix) { +template <class T> T check(ErrorOr<T> V, const Twine &Prefix) { if (auto EC = V.getError()) fatal(EC, Prefix); return std::move(*V); diff --git a/COFF/ICF.cpp b/COFF/ICF.cpp index 196fbe2610ea..19468c0fac5e 100644 --- a/COFF/ICF.cpp +++ b/COFF/ICF.cpp @@ -231,19 +231,16 @@ void ICF::run(const std::vector<Chunk *> &Vec) { ++Cnt; } while (Repeat); - if (Config->Verbose) - outs() << "\nICF needed " << Cnt << " iterations\n"; + log("ICF needed " + Twine(Cnt) + " iterations"); // Merge sections in the same colors. forEachColor([&](size_t Begin, size_t End) { if (End - Begin == 1) return; - if (Config->Verbose) - outs() << "Selected " << Chunks[Begin]->getDebugName() << "\n"; + log("Selected " + Chunks[Begin]->getDebugName()); for (size_t I = Begin + 1; I < End; ++I) { - if (Config->Verbose) - outs() << " Removed " << Chunks[I]->getDebugName() << "\n"; + log(" Removed " + Chunks[I]->getDebugName()); Chunks[Begin]->replace(Chunks[I]); } }); diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp index cde355cd3f34..cb56e13014db 100644 --- a/COFF/InputFiles.cpp +++ b/COFF/InputFiles.cpp @@ -19,8 +19,6 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/Twine.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/LTO/legacy/LTOModule.h" #include "llvm/Object/Binary.h" #include "llvm/Object/COFF.h" #include "llvm/Support/COFF.h" @@ -41,13 +39,23 @@ 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 { -LLVMContext BitcodeFile::Context; +/// Checks that Source is compatible with being a weak alias to Target. +/// If Source is Undefined and has no weak alias set, makes it a weak +/// alias to Target. +static void checkAndSetWeakAlias(SymbolTable *Symtab, InputFile *F, + SymbolBody *Source, SymbolBody *Target) { + auto *U = dyn_cast<Undefined>(Source); + if (!U) + return; + else if (!U->WeakAlias) + U->WeakAlias = Target; + else if (U->WeakAlias != Target) + Symtab->reportDuplicate(Source->symbol(), F); +} ArchiveFile::ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {} @@ -177,15 +185,9 @@ void ObjectFile::initializeSymbols() { I += Sym.getNumberOfAuxSymbols(); LastSectionNumber = Sym.getSectionNumber(); } - 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]; - } + for (auto WeakAlias : WeakAliases) + checkAndSetWeakAlias(Symtab, this, WeakAlias.first, + SparseSymbolBodies[WeakAlias.second]); } SymbolBody *ObjectFile::createUndefined(COFFSymbolRef Sym) { @@ -200,7 +202,10 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP, if (Sym.isCommon()) { auto *C = new (Alloc) CommonChunk(Sym); Chunks.push_back(C); - return Symtab->addCommon(this, Sym, C)->body(); + COFFObj->getSymbolName(Sym, Name); + Symbol *S = + Symtab->addCommon(this, Name, Sym.getValue(), Sym.getGeneric(), C); + return S->body(); } if (Sym.isAbsolute()) { COFFObj->getSymbolName(Sym, Name); @@ -247,10 +252,14 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP, } DefinedRegular *B; - if (Sym.isExternal()) - B = cast<DefinedRegular>(Symtab->addRegular(this, Sym, SC)->body()); - else - B = new (Alloc) DefinedRegular(this, Sym, SC); + if (Sym.isExternal()) { + COFFObj->getSymbolName(Sym, Name); + Symbol *S = + Symtab->addRegular(this, Name, SC->isCOMDAT(), Sym.getGeneric(), SC); + B = cast<DefinedRegular>(S->body()); + } else + B = new (Alloc) DefinedRegular(this, /*Name*/ "", SC->isCOMDAT(), + /*IsExternal*/ false, Sym.getGeneric(), SC); if (SC->isCOMDAT() && Sym.getValue() == 0 && !AuxP) SC->setSymbol(B); @@ -329,39 +338,32 @@ void ImportFile::parse() { } void BitcodeFile::parse() { - 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"); - - 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) - continue; - - StringRef SymName = Saver.save(M->getSymbolName(I)); - int SymbolDef = Attrs & LTO_SYMBOL_DEFINITION_MASK; - if (SymbolDef == LTO_SYMBOL_DEFINITION_UNDEFINED) { - SymbolBodies.push_back(Symtab->addUndefined(SymName, this, false)->body()); + Obj = check(lto::InputFile::create(MemoryBufferRef( + MB.getBuffer(), Saver.save(ParentName + MB.getBufferIdentifier())))); + for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) { + StringRef SymName = Saver.save(ObjSym.getName()); + Symbol *Sym; + if (ObjSym.isUndefined()) { + Sym = Symtab->addUndefined(SymName, this, false); + } else if (ObjSym.isCommon()) { + Sym = Symtab->addCommon(this, SymName, ObjSym.getCommonSize()); + } else if (ObjSym.isWeak() && ObjSym.isIndirect()) { + // Weak external. + Sym = Symtab->addUndefined(SymName, this, true); + std::string Fallback = ObjSym.getCOFFWeakExternalFallback(); + SymbolBody *Alias = Symtab->addUndefined(Saver.save(Fallback)); + checkAndSetWeakAlias(Symtab, this, Sym->body(), Alias); } 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( - Symtab->addBitcode(this, SymName, Replaceable)->body()); + bool IsCOMDAT = ObjSym.getComdatIndex() != -1; + Sym = Symtab->addRegular(this, SymName, IsCOMDAT); } + SymbolBodies.push_back(Sym->body()); } - - Directives = M->getLinkerOpts(); + Directives = Obj->getCOFFLinkerOpts(); } MachineTypes BitcodeFile::getMachineType() { - if (!M) - return IMAGE_FILE_MACHINE_UNKNOWN; - switch (Triple(M->getTargetTriple()).getArch()) { + switch (Triple(Obj->getTargetTriple()).getArch()) { case Triple::x86_64: return AMD64; case Triple::x86: diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h index 1b5d42939cca..9e02b2fc68bb 100644 --- a/COFF/InputFiles.h +++ b/COFF/InputFiles.h @@ -13,8 +13,7 @@ #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/LTO/LTO.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" #include "llvm/Support/StringSaver.h" @@ -25,7 +24,6 @@ namespace lld { namespace coff { -using llvm::LTOModule; using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN; using llvm::COFF::MachineTypes; using llvm::object::Archive; @@ -174,7 +172,6 @@ public: private: void parse() override; - llvm::BumpPtrAllocator Alloc; llvm::BumpPtrAllocator StringAllocAux; llvm::StringSaver StringAlloc; @@ -191,16 +188,12 @@ public: static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; } std::vector<SymbolBody *> &getSymbols() { return SymbolBodies; } MachineTypes getMachineType() override; - std::unique_ptr<LTOModule> takeModule() { return std::move(M); } - - static llvm::LLVMContext Context; + std::unique_ptr<llvm::lto::InputFile> Obj; private: void parse() override; std::vector<SymbolBody *> SymbolBodies; - llvm::BumpPtrAllocator Alloc; - std::unique_ptr<LTOModule> M; }; } // namespace coff diff --git a/COFF/LTO.cpp b/COFF/LTO.cpp new file mode 100644 index 000000000000..6883b3b4c2c8 --- /dev/null +++ b/COFF/LTO.cpp @@ -0,0 +1,140 @@ +//===- LTO.cpp ------------------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LTO.h" +#include "Config.h" +#include "Error.h" +#include "InputFiles.h" +#include "Symbols.h" +#include "lld/Core/TargetOptionsCommandFlags.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/LTO/Config.h" +#include "llvm/LTO/LTO.h" +#include "llvm/Object/SymbolicFile.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cstddef> +#include <memory> +#include <string> +#include <system_error> +#include <vector> + +using namespace llvm; +using namespace llvm::object; + +using namespace lld; +using namespace lld::coff; + +static void diagnosticHandler(const DiagnosticInfo &DI) { + SmallString<128> ErrStorage; + raw_svector_ostream OS(ErrStorage); + DiagnosticPrinterRawOStream DP(OS); + DI.print(DP); + warn(ErrStorage); +} + +static void checkError(Error E) { + handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) -> Error { + error(EIB.message()); + return Error::success(); + }); +} + +static void saveBuffer(StringRef Buffer, const Twine &Path) { + std::error_code EC; + raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None); + if (EC) + error("cannot create " + Path + ": " + EC.message()); + OS << Buffer; +} + +static std::unique_ptr<lto::LTO> createLTO() { + lto::Config Conf; + Conf.Options = InitTargetOptionsFromCodeGenFlags(); + Conf.RelocModel = Reloc::PIC_; + Conf.DisableVerify = true; + Conf.DiagHandler = diagnosticHandler; + Conf.OptLevel = Config->LTOOptLevel; + if (Config->SaveTemps) + checkError(Conf.addSaveTemps(std::string(Config->OutputFile) + ".", + /*UseInputModulePath*/ true)); + lto::ThinBackend Backend; + if (Config->LTOJobs != 0) + Backend = lto::createInProcessThinBackend(Config->LTOJobs); + return llvm::make_unique<lto::LTO>(std::move(Conf), Backend, + Config->LTOPartitions); +} + +BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {} + +BitcodeCompiler::~BitcodeCompiler() = default; + +static void undefine(Symbol *S) { + replaceBody<Undefined>(S, S->body()->getName()); +} + +void BitcodeCompiler::add(BitcodeFile &F) { + lto::InputFile &Obj = *F.Obj; + unsigned SymNum = 0; + std::vector<SymbolBody *> SymBodies = F.getSymbols(); + std::vector<lto::SymbolResolution> Resols(SymBodies.size()); + + // Provide a resolution to the LTO API for each symbol. + for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) { + SymbolBody *B = SymBodies[SymNum]; + Symbol *Sym = B->symbol(); + lto::SymbolResolution &R = Resols[SymNum]; + ++SymNum; + + // Ideally we shouldn't check for SF_Undefined but currently IRObjectFile + // reports two symbols for module ASM defined. Without this check, lld + // flags an undefined in IR with a definition in ASM as prevailing. + // Once IRObjectFile is fixed to report only one symbol this hack can + // be removed. + R.Prevailing = !ObjSym.isUndefined() && B->getFile() == &F; + R.VisibleToRegularObj = Sym->IsUsedInRegularObj; + if (R.Prevailing) + undefine(Sym); + } + checkError(LTOObj->add(std::move(F.Obj), Resols)); +} + +// Merge all the bitcode files we have seen, codegen the result +// and return the resulting objects. +std::vector<StringRef> BitcodeCompiler::compile() { + unsigned MaxTasks = LTOObj->getMaxTasks(); + Buff.resize(MaxTasks); + + checkError(LTOObj->run([&](size_t Task) { + return llvm::make_unique<lto::NativeObjectStream>( + llvm::make_unique<raw_svector_ostream>(Buff[Task])); + })); + + std::vector<StringRef> Ret; + for (unsigned I = 0; I != MaxTasks; ++I) { + if (Buff[I].empty()) + continue; + if (Config->SaveTemps) { + if (I == 0) + saveBuffer(Buff[I], Config->OutputFile + ".lto.obj"); + else + saveBuffer(Buff[I], Config->OutputFile + Twine(I) + ".lto.obj"); + } + Ret.emplace_back(Buff[I].data(), Buff[I].size()); + } + return Ret; +} diff --git a/COFF/LTO.h b/COFF/LTO.h new file mode 100644 index 000000000000..194a4cce8ada --- /dev/null +++ b/COFF/LTO.h @@ -0,0 +1,56 @@ +//===- LTO.h ----------------------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides a way to combine bitcode files into one COFF +// file by compiling them using LLVM. +// +// If LTO is in use, your input files are not in regular COFF files +// but instead LLVM bitcode files. In that case, the linker has to +// convert bitcode files into the native format so that we can create +// a COFF file that contains native code. This file provides that +// functionality. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_COFF_LTO_H +#define LLD_COFF_LTO_H + +#include "lld/Core/LLVM.h" +#include "llvm/ADT/SmallString.h" +#include <memory> +#include <vector> + +namespace llvm { +namespace lto { +class LTO; +} +} + +namespace lld { +namespace coff { + +class BitcodeFile; +class InputFile; + +class BitcodeCompiler { +public: + BitcodeCompiler(); + ~BitcodeCompiler(); + + void add(BitcodeFile &F); + std::vector<StringRef> compile(); + +private: + std::unique_ptr<llvm::lto::LTO> LTOObj; + std::vector<SmallString<0>> Buff; +}; +} +} + +#endif diff --git a/COFF/Librarian.cpp b/COFF/Librarian.cpp index 4c597fad7345..3ce72822180b 100644 --- a/COFF/Librarian.cpp +++ b/COFF/Librarian.cpp @@ -104,7 +104,18 @@ static ImportNameType getNameType(StringRef Sym, StringRef ExtName) { static std::string replace(StringRef S, StringRef From, StringRef To) { size_t Pos = S.find(From); - assert(Pos != StringRef::npos); + + // From and To may be mangled, but substrings in S may not. + if (Pos == StringRef::npos && From.startswith("_") && To.startswith("_")) { + From = From.substr(1); + To = To.substr(1); + Pos = S.find(From); + } + + if (Pos == StringRef::npos) { + error(S + ": replacing '" + From + "' with '" + To + "' failed"); + return ""; + } return (Twine(S.substr(0, Pos)) + To + S.substr(Pos + From.size())).str(); } diff --git a/COFF/MapFile.cpp b/COFF/MapFile.cpp new file mode 100644 index 000000000000..43dd8dc35810 --- /dev/null +++ b/COFF/MapFile.cpp @@ -0,0 +1,114 @@ +//===- MapFile.cpp --------------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the /lldmap option. It shows lists in order and +// hierarchically the output sections, input sections, input files and +// symbol: +// +// Address Size Align Out In File Symbol +// ================================================================= +// 00201000 00000015 4 .text +// 00201000 0000000e 4 .text +// 00201000 0000000e 4 test.o +// 0020100e 00000000 0 local +// 00201005 00000000 0 f(int) +// +//===----------------------------------------------------------------------===// + +#include "MapFile.h" +#include "Error.h" +#include "Symbols.h" +#include "Writer.h" + +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::object; + +using namespace lld; +using namespace lld::coff; + +static void writeOutSecLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size, + uint64_t Align, StringRef Name) { + OS << format("%08llx %08llx %5lld ", Address, Size, Align) + << left_justify(Name, 7); +} + +static void writeInSecLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size, + uint64_t Align, StringRef Name) { + // Pass an empty name to align the text to the correct column. + writeOutSecLine(OS, Address, Size, Align, ""); + OS << ' ' << left_justify(Name, 7); +} + +static void writeFileLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size, + uint64_t Align, StringRef Name) { + // Pass an empty name to align the text to the correct column. + writeInSecLine(OS, Address, Size, Align, ""); + OS << ' ' << left_justify(Name, 7); +} + +static void writeSymbolLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size, + StringRef Name) { + // Pass an empty name to align the text to the correct column. + writeFileLine(OS, Address, Size, 0, ""); + OS << ' ' << left_justify(Name, 7); +} + +static void writeSectionChunk(raw_fd_ostream &OS, const SectionChunk *SC, + StringRef &PrevName) { + StringRef Name = SC->getSectionName(); + if (Name != PrevName) { + writeInSecLine(OS, SC->getRVA(), SC->getSize(), SC->getAlign(), Name); + OS << '\n'; + PrevName = Name; + } + coff::ObjectFile *File = SC->File; + if (!File) + return; + writeFileLine(OS, SC->getRVA(), SC->getSize(), SC->getAlign(), + toString(File)); + OS << '\n'; + ArrayRef<SymbolBody *> Syms = File->getSymbols(); + for (SymbolBody *Sym : Syms) { + auto *DR = dyn_cast<DefinedRegular>(Sym); + if (!DR || DR->getChunk() != SC || + DR->getCOFFSymbol().isSectionDefinition()) + continue; + writeSymbolLine(OS, DR->getRVA(), SC->getSize(), toString(*Sym)); + OS << '\n'; + } +} + +static void writeMapFile2(raw_fd_ostream &OS, + ArrayRef<OutputSection *> OutputSections) { + OS << "Address Size Align Out In File Symbol\n"; + + for (OutputSection *Sec : OutputSections) { + uint32_t VA = Sec->getRVA(); + writeOutSecLine(OS, VA, Sec->getVirtualSize(), /*Align=*/PageSize, + Sec->getName()); + OS << '\n'; + StringRef PrevName = ""; + for (Chunk *C : Sec->getChunks()) + if (const auto *SC = dyn_cast<SectionChunk>(C)) + writeSectionChunk(OS, SC, PrevName); + } +} + +void coff::writeMapFile(ArrayRef<OutputSection *> OutputSections) { + if (Config->MapFile.empty()) + return; + + std::error_code EC; + raw_fd_ostream OS(Config->MapFile, EC, sys::fs::F_None); + if (EC) + fatal("cannot open " + Config->MapFile + ": " + EC.message()); + writeMapFile2(OS, OutputSections); +} diff --git a/COFF/MapFile.h b/COFF/MapFile.h new file mode 100644 index 000000000000..0d0d68ce3ead --- /dev/null +++ b/COFF/MapFile.h @@ -0,0 +1,22 @@ +//===- MapFile.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_MAPFILE_H +#define LLD_COFF_MAPFILE_H + +#include "llvm/ADT/ArrayRef.h" + +namespace lld { +namespace coff { +class OutputSection; +void writeMapFile(llvm::ArrayRef<OutputSection *> OutputSections); +} +} + +#endif diff --git a/COFF/ModuleDef.cpp b/COFF/ModuleDef.cpp index a273b6f535db..c9a40ac5ab8c 100644 --- a/COFF/ModuleDef.cpp +++ b/COFF/ModuleDef.cpp @@ -163,17 +163,25 @@ private: case KwHeapsize: parseNumbers(&Config->HeapReserve, &Config->HeapCommit); return; - case KwLibrary: - parseName(&Config->OutputFile, &Config->ImageBase); - if (!StringRef(Config->OutputFile).endswith_lower(".dll")) - Config->OutputFile += ".dll"; - return; case KwStacksize: parseNumbers(&Config->StackReserve, &Config->StackCommit); return; - case KwName: - parseName(&Config->OutputFile, &Config->ImageBase); + case KwLibrary: + case KwName: { + bool IsDll = Tok.K == KwLibrary; // Check before parseName. + std::string Name; + parseName(&Name, &Config->ImageBase); + + // Append the appropriate file extension if not already present. + StringRef Ext = IsDll ? ".dll" : ".exe"; + if (!StringRef(Name).endswith_lower(Ext)) + Name += Ext; + + // Set the output file, but don't override /out if it was already passed. + if (Config->OutputFile.empty()) + Config->OutputFile = Name; return; + } case KwVersion: parseVersion(&Config->MajorImageVersion, &Config->MinorImageVersion); return; diff --git a/COFF/Options.td b/COFF/Options.td index 9dfbcc8e188c..7b5573b31cd3 100644 --- a/COFF/Options.td +++ b/COFF/Options.td @@ -21,6 +21,8 @@ def base : P<"base", "Base address of the program">; def defaultlib : P<"defaultlib", "Add the library to the list of input files">; def delayload : P<"delayload", "Delay loaded DLL name">; def entry : P<"entry", "Name of entry point symbol">; +def errorlimit : P<"errorlimit", + "Maximum number of errors to emit before stopping (0 = no limit)">; def export : P<"export", "Export a function">; // No help text because /failifmismatch is not intended to be used by the user. def failifmismatch : P<"failifmismatch", "">; @@ -28,6 +30,8 @@ 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 lldsavetemps : F<"lldsavetemps">, + HelpText<"Save temporary files instead of deleting them">; def machine : P<"machine", "Specify target platform">; def merge : P<"merge", "Combine sections">; def mllvm : P<"mllvm", "Options to pass to LLVM">; @@ -78,6 +82,8 @@ def force_unresolved : F<"force:unresolved">; defm allowbind: B<"allowbind", "Disable DLL binding">; defm allowisolation : B<"allowisolation", "Set NO_ISOLATION bit">; +defm appcontainer : B<"appcontainer", + "Image can only be run in an app container">; defm dynamicbase : B<"dynamicbase", "Disable address space layout randomization">; defm fixed : B<"fixed", "Enable base relocations">; @@ -91,7 +97,9 @@ def help : F<"help">; def help_q : Flag<["/?", "-?"], "">, Alias<help>; // LLD extensions +def nopdb : F<"nopdb">, HelpText<"Disable PDB generation for DWARF users">; def nosymtab : F<"nosymtab">; +def msvclto : F<"msvclto">; // Flags for debugging def debugpdb : F<"debugpdb">; diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp index 923fc64c7734..e32bcd20a541 100644 --- a/COFF/PDB.cpp +++ b/COFF/PDB.cpp @@ -20,20 +20,23 @@ #include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h" #include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" #include "llvm/DebugInfo/CodeView/TypeTableBuilder.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/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h" +#include "llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.h" +#include "llvm/DebugInfo/PDB/Native/StringTableBuilder.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h" #include "llvm/Object/COFF.h" +#include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/Endian.h" #include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" #include <memory> @@ -79,32 +82,49 @@ static ArrayRef<uint8_t> getDebugSection(ObjectFile *File, StringRef SecName) { return Data.slice(4); } -// Merge .debug$T sections and returns it. -static std::vector<uint8_t> mergeDebugT(SymbolTable *Symtab) { - ScopedPrinter W(outs()); +static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder, + codeview::TypeTableBuilder &TypeTable) { + // Start the TPI or IPI stream header. + TpiBuilder.setVersionHeader(pdb::PdbTpiV80); + + // Flatten the in memory type table. + TypeTable.ForEachRecord([&](TypeIndex TI, ArrayRef<uint8_t> Rec) { + // FIXME: Hash types. + TpiBuilder.addTypeRecord(Rec, None); + }); +} +// Merge .debug$T sections into IpiData and TpiData. +static void mergeDebugT(SymbolTable *Symtab, pdb::PDBFileBuilder &Builder, + codeview::TypeTableBuilder &TypeTable, + codeview::TypeTableBuilder &IDTable) { // Visit all .debug$T sections to add them to Builder. - codeview::TypeTableBuilder Builder(BAlloc); for (ObjectFile *File : Symtab->ObjectFiles) { ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$T"); if (Data.empty()) continue; - msf::ByteStream Stream(Data); + BinaryByteStream Stream(Data, support::little); codeview::CVTypeArray Types; - msf::StreamReader Reader(Stream); + BinaryStreamReader Reader(Stream); + // Follow type servers. If the same type server is encountered more than + // once for this instance of `PDBTypeServerHandler` (for example if many + // object files reference the same TypeServer), the types from the + // TypeServer will only be visited once. + pdb::PDBTypeServerHandler Handler; + Handler.addSearchPath(llvm::sys::path::parent_path(File->getName())); if (auto EC = Reader.readArray(Types, Reader.getLength())) fatal(EC, "Reader::readArray failed"); - if (!codeview::mergeTypeStreams(Builder, Types)) - fatal("codeview::mergeTypeStreams failed"); + if (auto Err = + codeview::mergeTypeStreams(IDTable, TypeTable, &Handler, Types)) + fatal(Err, "codeview::mergeTypeStreams failed"); } - // Construct section contents. - std::vector<uint8_t> V; - Builder.ForEachRecord([&](TypeIndex TI, ArrayRef<uint8_t> Rec) { - V.insert(V.end(), Rec.begin(), Rec.end()); - }); - return V; + // Construct TPI stream contents. + addTypeInfo(Builder.getTpiBuilder(), TypeTable); + + // Construct IPI stream contents. + addTypeInfo(Builder.getIpiBuilder(), IDTable); } static void dumpDebugT(ScopedPrinter &W, ObjectFile *File) { @@ -115,6 +135,8 @@ static void dumpDebugT(ScopedPrinter &W, ObjectFile *File) { TypeDatabase TDB; TypeDumpVisitor TDV(TDB, &W, false); + // Use a default implementation that does not follow type servers and instead + // just dumps the contents of the TypeServer2 record. CVTypeDumper TypeDumper(TDB); if (auto EC = TypeDumper.dump(Data, TDV)) fatal(EC, "CVTypeDumper::dump failed"); @@ -126,9 +148,9 @@ static void dumpDebugS(ScopedPrinter &W, ObjectFile *File) { if (Data.empty()) return; - msf::ByteStream Stream(Data); + BinaryByteStream Stream(Data, llvm::support::little); CVSymbolArray Symbols; - msf::StreamReader Reader(Stream); + BinaryStreamReader Reader(Stream); if (auto EC = Reader.readArray(Symbols, Reader.getLength())) fatal(EC, "StreamReader.readArray<CVSymbolArray> failed"); @@ -148,17 +170,6 @@ static void dumpCodeView(SymbolTable *Symtab) { } } -static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder, - ArrayRef<uint8_t> Data) { - 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, @@ -177,9 +188,12 @@ void coff::createPDB(StringRef Path, SymbolTable *Symtab, // Add an Info stream. auto &InfoBuilder = Builder.getInfoBuilder(); - InfoBuilder.setAge(DI->PDB70.Age); - InfoBuilder.setGuid( - *reinterpret_cast<const pdb::PDB_UniqueId *>(&DI->PDB70.Signature)); + InfoBuilder.setAge(DI ? DI->PDB70.Age : 0); + + pdb::PDB_UniqueId uuid{}; + if (DI) + memcpy(&uuid, &DI->PDB70.Signature, sizeof(uuid)); + InfoBuilder.setGuid(uuid); // Should be the current time, but set 0 for reproducibilty. InfoBuilder.setSignature(0); InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70); @@ -188,18 +202,9 @@ void coff::createPDB(StringRef Path, SymbolTable *Symtab, auto &DbiBuilder = Builder.getDbiBuilder(); DbiBuilder.setVersionHeader(pdb::PdbDbiV110); - // Add an empty TPI stream. - auto &TpiBuilder = Builder.getTpiBuilder(); - TpiBuilder.setVersionHeader(pdb::PdbTpiV80); - std::vector<uint8_t> TpiData; - if (Config->DebugPdb) { - TpiData = mergeDebugT(Symtab); - addTypeInfo(TpiBuilder, TpiData); - } - - // Add an empty IPI stream. - auto &IpiBuilder = Builder.getIpiBuilder(); - IpiBuilder.setVersionHeader(pdb::PdbTpiV80); + codeview::TypeTableBuilder TypeTable(BAlloc); + codeview::TypeTableBuilder IDTable(BAlloc); + mergeDebugT(Symtab, Builder, TypeTable, IDTable); // Add Section Contributions. std::vector<pdb::SectionContrib> Contribs = @@ -214,7 +219,7 @@ void coff::createPDB(StringRef Path, SymbolTable *Symtab, pdb::DbiStreamBuilder::createSectionMap(Sections); DbiBuilder.setSectionMap(SectionMap); - ExitOnErr(DbiBuilder.addModuleInfo("", "* Linker *")); + ExitOnErr(DbiBuilder.addModuleInfo("* Linker *")); // Add COFF section header stream. ExitOnErr( diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp index 9cc0b75c1510..310eab274526 100644 --- a/COFF/SymbolTable.cpp +++ b/COFF/SymbolTable.cpp @@ -11,10 +11,10 @@ #include "Config.h" #include "Driver.h" #include "Error.h" +#include "LTO.h" #include "Memory.h" #include "Symbols.h" #include "llvm/IR/LLVMContext.h" -#include "llvm/LTO/legacy/LTOCodeGenerator.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include <utility> @@ -24,11 +24,40 @@ using namespace llvm; namespace lld { namespace coff { +enum SymbolPreference { + SP_EXISTING = -1, + SP_CONFLICT = 0, + SP_NEW = 1, +}; + +/// Checks if an existing symbol S should be kept or replaced by a new symbol. +/// Returns SP_EXISTING when S should be kept, SP_NEW when the new symbol +/// should be kept, and SP_CONFLICT if no valid resolution exists. +static SymbolPreference compareDefined(Symbol *S, bool WasInserted, + bool NewIsCOMDAT) { + // If the symbol wasn't previously known, the new symbol wins by default. + if (WasInserted || !isa<Defined>(S->body())) + return SP_NEW; + + // If the existing symbol is a DefinedRegular, both it and the new symbol + // must be comdats. In that case, we have no reason to prefer one symbol + // over the other, and we keep the existing one. If one of the symbols + // is not a comdat, we report a conflict. + if (auto *R = dyn_cast<DefinedRegular>(S->body())) { + if (NewIsCOMDAT && R->isCOMDAT()) + return SP_EXISTING; + else + return SP_CONFLICT; + } + + // Existing symbol is not a DefinedRegular; new symbol wins. + return SP_NEW; +} + SymbolTable *Symtab; void SymbolTable::addFile(InputFile *File) { - if (Config->Verbose) - outs() << "Reading " << toString(File) << "\n"; + log("Reading " + toString(File)); File->parse(); MachineTypes MT = File->getMachineType(); @@ -51,8 +80,7 @@ void SymbolTable::addFile(InputFile *File) { if (S.empty()) return; - if (Config->Verbose) - outs() << "Directives: " << toString(File) << ": " << S << "\n"; + log("Directives: " + toString(File) + ": " + S); Driver->parseDirectives(S); } @@ -106,12 +134,11 @@ void SymbolTable::reportRemainingUndefines() { return; for (SymbolBody *B : Config->GCRoot) if (Undefs.count(B)) - errs() << "<root>: undefined symbol: " << B->getName() << "\n"; + warn("<root>: undefined symbol: " + B->getName()); for (ObjectFile *File : ObjectFiles) for (SymbolBody *Sym : File->getSymbols()) if (Undefs.count(Sym)) - errs() << toString(File) << ": undefined symbol: " << Sym->getName() - << "\n"; + warn(toString(File) + ": undefined symbol: " + Sym->getName()); if (!Config->Force) fatal("link failed"); } @@ -163,7 +190,7 @@ void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) { } void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) { - fatal("duplicate symbol: " + toString(*Existing->body()) + " in " + + error("duplicate symbol: " + toString(*Existing->body()) + " in " + toString(Existing->body()->getFile()) + " and in " + (NewFile ? toString(NewFile) : "(internal)")); } @@ -204,59 +231,35 @@ Symbol *SymbolTable::addRelative(StringRef N, uint64_t VA) { return S; } -Symbol *SymbolTable::addRegular(ObjectFile *F, COFFSymbolRef Sym, +Symbol *SymbolTable::addRegular(InputFile *F, StringRef N, bool IsCOMDAT, + const coff_symbol_generic *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; + if (!isa<BitcodeFile>(F)) + S->IsUsedInRegularObj = true; + SymbolPreference SP = compareDefined(S, WasInserted, IsCOMDAT); + if (SP == SP_CONFLICT) { + reportDuplicate(S, F); + } else if (SP == SP_NEW) { + replaceBody<DefinedRegular>(S, F, N, IsCOMDAT, /*IsExternal*/ true, Sym, C); } - 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; } -Symbol *SymbolTable::addCommon(ObjectFile *F, COFFSymbolRef Sym, - CommonChunk *C) { - StringRef Name; - F->getCOFFObj()->getSymbolName(Sym, Name); +Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size, + const coff_symbol_generic *Sym, CommonChunk *C) { Symbol *S; bool WasInserted; - std::tie(S, WasInserted) = insert(Name); - S->IsUsedInRegularObj = true; + std::tie(S, WasInserted) = insert(N); + if (!isa<BitcodeFile>(F)) + S->IsUsedInRegularObj = true; if (WasInserted || !isa<DefinedCOFF>(S->body())) - replaceBody<DefinedCommon>(S, F, Sym, C); + replaceBody<DefinedCommon>(S, F, N, Size, Sym, C); else if (auto *DC = dyn_cast<DefinedCommon>(S->body())) - if (Sym.getValue() > DC->getSize()) - replaceBody<DefinedCommon>(S, F, Sym, C); + if (Size > DC->getSize()) + replaceBody<DefinedCommon>(S, F, N, Size, Sym, C); return S; } @@ -345,75 +348,21 @@ SymbolBody *SymbolTable::addUndefined(StringRef Name) { return addUndefined(Name, nullptr, false)->body(); } -void SymbolTable::printMap(llvm::raw_ostream &OS) { - for (ObjectFile *File : ObjectFiles) { - OS << toString(File) << ":\n"; - for (SymbolBody *Body : File->getSymbols()) - if (auto *R = dyn_cast<DefinedRegular>(Body)) - if (R->getChunk()->isLive()) - OS << Twine::utohexstr(Config->ImageBase + R->getRVA()) - << " " << R->getName() << "\n"; - } +std::vector<StringRef> SymbolTable::compileBitcodeFiles() { + LTO.reset(new BitcodeCompiler); + for (BitcodeFile *F : BitcodeFiles) + LTO->add(*F); + return LTO->compile(); } void SymbolTable::addCombinedLTOObjects() { if (BitcodeFiles.empty()) return; - - // 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); - for (ObjectFile *Obj : createLTOObjects(&CG)) + for (StringRef Object : compileBitcodeFiles()) { + auto *Obj = make<ObjectFile>(MemoryBufferRef(Object, "lto.tmp")); 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, 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)) - continue; - if (Body->symbol()->IsUsedInRegularObj) - CG->addMustPreserveSymbol(Body->getName()); - replaceBody<Undefined>(Body->symbol(), Body->getName()); - } - - CG->setModule(BitcodeFiles[0]->takeModule()); - for (unsigned I = 1, E = BitcodeFiles.size(); I != E; ++I) - CG->addModule(BitcodeFiles[I]->takeModule().get()); - - bool DisableVerify = true; -#ifdef NDEBUG - DisableVerify = false; -#endif - if (!CG->optimize(DisableVerify, false, false, false)) - fatal(""); // optimize() should have emitted any error message. - - Objs.resize(Config->LTOJobs); - // Use std::list to avoid invalidation of pointers in OSPtrs. - std::list<raw_svector_ostream> OSs; - std::vector<raw_pwrite_stream *> OSPtrs; - for (SmallString<0> &Obj : Objs) { - OSs.emplace_back(Obj); - OSPtrs.push_back(&OSs.back()); + ObjectFiles.push_back(Obj); } - - if (!CG->compileOptimized(OSPtrs)) - fatal(""); // compileOptimized() should have emitted any error message. - - std::vector<ObjectFile *> ObjFiles; - for (SmallString<0> &Obj : Objs) { - auto *ObjFile = make<ObjectFile>(MemoryBufferRef(Obj, "<LTO object>")); - ObjectFiles.push_back(ObjFile); - ObjFiles.push_back(ObjFile); - } - - return ObjFiles; } } // namespace coff diff --git a/COFF/SymbolTable.h b/COFF/SymbolTable.h index 703821f2e124..764dd5318775 100644 --- a/COFF/SymbolTable.h +++ b/COFF/SymbolTable.h @@ -11,6 +11,7 @@ #define LLD_COFF_SYMBOL_TABLE_H #include "InputFiles.h" +#include "LTO.h" #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMapInfo.h" @@ -69,13 +70,11 @@ public: void mangleMaybe(SymbolBody *B); StringRef findMangle(StringRef Name); - // Print a layout map to OS. - void printMap(llvm::raw_ostream &OS); - // Build a set of COFF objects representing the combined contents of // BitcodeFiles and add them to the symbol table. Called after all files are // added and before the writer writes results to a file. void addCombinedLTOObjects(); + std::vector<StringRef> compileBitcodeFiles(); // The writer needs to handle DLL import libraries specially in // order to create the import descriptor table. @@ -93,9 +92,12 @@ public: 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 *addRegular(InputFile *F, StringRef N, bool IsCOMDAT, + const llvm::object::coff_symbol_generic *S = nullptr, + SectionChunk *C = nullptr); + Symbol *addCommon(InputFile *F, StringRef N, uint64_t Size, + const llvm::object::coff_symbol_generic *S = nullptr, + CommonChunk *C = nullptr); Symbol *addImportData(StringRef N, ImportFile *F); Symbol *addImportThunk(StringRef Name, DefinedImportData *S, uint16_t Machine); @@ -113,12 +115,11 @@ private: StringRef findByPrefix(StringRef Prefix); void addCombinedLTOObject(ObjectFile *Obj); - std::vector<ObjectFile *> createLTOObjects(llvm::LTOCodeGenerator *CG); llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> Symtab; std::vector<BitcodeFile *> BitcodeFiles; - std::vector<SmallString<0>> Objs; + std::unique_ptr<BitcodeCompiler> LTO; }; extern SymbolTable *Symtab; diff --git a/COFF/Symbols.cpp b/COFF/Symbols.cpp index c44537d37135..993e920ce7f7 100644 --- a/COFF/Symbols.cpp +++ b/COFF/Symbols.cpp @@ -30,7 +30,7 @@ namespace lld { namespace coff { StringRef SymbolBody::getName() { - // DefinedCOFF names are read lazily for a performance reason. + // COFF symbol names are read lazily for a performance reason. // Non-external symbol names are never used by the linker except for logging // or debugging. Their internal references are resolved not by name but by // symbol index. And because they are not external, no one can refer them by @@ -39,7 +39,7 @@ StringRef SymbolBody::getName() { // is a waste of time. if (Name.empty()) { auto *D = cast<DefinedCOFF>(this); - D->File->getCOFFObj()->getSymbolName(D->Sym, Name); + cast<ObjectFile>(D->File)->getCOFFObj()->getSymbolName(D->Sym, Name); } return Name; } @@ -47,15 +47,14 @@ StringRef SymbolBody::getName() { 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() { - size_t SymSize = File->getCOFFObj()->getSymbolTableEntrySize(); + size_t SymSize = + cast<ObjectFile>(File)->getCOFFObj()->getSymbolTableEntrySize(); if (SymSize == sizeof(coff_symbol16)) return COFFSymbolRef(reinterpret_cast<const coff_symbol16 *>(Sym)); assert(SymSize == sizeof(coff_symbol32)); diff --git a/COFF/Symbols.h b/COFF/Symbols.h index 1ca7366364d7..1b83f73ff20c 100644 --- a/COFF/Symbols.h +++ b/COFF/Symbols.h @@ -30,7 +30,6 @@ using llvm::object::coff_import_header; using llvm::object::coff_symbol_generic; class ArchiveFile; -class BitcodeFile; class InputFile; class ObjectFile; struct Symbol; @@ -52,13 +51,12 @@ public: DefinedImportDataKind, DefinedAbsoluteKind, DefinedRelativeKind, - DefinedBitcodeKind, UndefinedKind, LazyKind, LastDefinedCOFFKind = DefinedCommonKind, - LastDefinedKind = DefinedBitcodeKind, + LastDefinedKind = DefinedRelativeKind, }; Kind kind() const { return static_cast<Kind>(SymbolKind); } @@ -81,7 +79,7 @@ protected: friend SymbolTable; explicit SymbolBody(Kind K, StringRef N = "") : SymbolKind(K), IsExternal(true), IsCOMDAT(false), - IsReplaceable(false), WrittenToSymtab(false), Name(N) {} + WrittenToSymtab(false), Name(N) {} const unsigned SymbolKind : 8; unsigned IsExternal : 1; @@ -89,11 +87,9 @@ protected: // This bit is used by the \c DefinedRegular subclass. unsigned IsCOMDAT : 1; - // This bit is used by the \c DefinedBitcode subclass. - unsigned IsReplaceable : 1; - public: - // This bit is used by Writer::createSymbolAndStringTable(). + // This bit is used by Writer::createSymbolAndStringTable() to prevent + // symbols from being written to the symbol table more than once. unsigned WrittenToSymtab : 1; protected: @@ -104,7 +100,7 @@ protected: // etc. class Defined : public SymbolBody { public: - Defined(Kind K, StringRef N = "") : SymbolBody(K, N) {} + Defined(Kind K, StringRef N) : SymbolBody(K, N) {} static bool classof(const SymbolBody *S) { return S->kind() <= LastDefinedKind; @@ -127,22 +123,25 @@ public: bool isExecutable(); }; -// Symbols defined via a COFF object file. +// Symbols defined via a COFF object file or bitcode file. For COFF files, this +// stores a coff_symbol_generic*, and names of internal symbols are lazily +// loaded through that. For bitcode files, Sym is nullptr and the name is stored +// as a StringRef. class DefinedCOFF : public Defined { friend SymbolBody; public: - DefinedCOFF(Kind K, ObjectFile *F, COFFSymbolRef S) - : Defined(K), File(F), Sym(S.getGeneric()) {} + DefinedCOFF(Kind K, InputFile *F, StringRef N, const coff_symbol_generic *S) + : Defined(K, N), File(F), Sym(S) {} static bool classof(const SymbolBody *S) { return S->kind() <= LastDefinedCOFFKind; } - ObjectFile *getFile() { return File; } + InputFile *getFile() { return File; } COFFSymbolRef getCOFFSymbol(); - ObjectFile *File; + InputFile *File; protected: const coff_symbol_generic *Sym; @@ -151,10 +150,13 @@ protected: // Regular defined symbols read from object file symbol tables. class DefinedRegular : public DefinedCOFF { public: - DefinedRegular(ObjectFile *F, COFFSymbolRef S, SectionChunk *C) - : DefinedCOFF(DefinedRegularKind, F, S), Data(&C->Repl) { - IsExternal = S.isExternal(); - IsCOMDAT = C->isCOMDAT(); + DefinedRegular(InputFile *F, StringRef N, bool IsCOMDAT, + bool IsExternal = false, + const coff_symbol_generic *S = nullptr, + SectionChunk *C = nullptr) + : DefinedCOFF(DefinedRegularKind, F, N, S), Data(C ? &C->Repl : nullptr) { + this->IsExternal = IsExternal; + this->IsCOMDAT = IsCOMDAT; } static bool classof(const SymbolBody *S) { @@ -172,9 +174,11 @@ private: class DefinedCommon : public DefinedCOFF { public: - DefinedCommon(ObjectFile *F, COFFSymbolRef S, CommonChunk *C) - : DefinedCOFF(DefinedCommonKind, F, S), Data(C) { - IsExternal = S.isExternal(); + DefinedCommon(InputFile *F, StringRef N, uint64_t Size, + const coff_symbol_generic *S = nullptr, + CommonChunk *C = nullptr) + : DefinedCOFF(DefinedCommonKind, F, N, S), Data(C), Size(Size) { + this->IsExternal = true; } static bool classof(const SymbolBody *S) { @@ -185,8 +189,9 @@ public: private: friend SymbolTable; - uint64_t getSize() { return Sym->Value; } + uint64_t getSize() const { return Size; } CommonChunk *Data; + uint64_t Size; }; // Absolute symbols. @@ -340,26 +345,6 @@ private: LocalImportChunk *Data; }; -class DefinedBitcode : public Defined { - friend SymbolBody; -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; - } - - static bool classof(const SymbolBody *S) { - return S->kind() == DefinedBitcodeKind; - } - - BitcodeFile *File; -}; - inline uint64_t Defined::getRVA() { switch (kind()) { case DefinedAbsoluteKind: @@ -376,8 +361,6 @@ inline uint64_t Defined::getRVA() { return cast<DefinedCommon>(this)->getRVA(); case DefinedRegularKind: return cast<DefinedRegular>(this)->getRVA(); - case DefinedBitcodeKind: - llvm_unreachable("There is no address for a bitcode symbol."); case LazyKind: case UndefinedKind: llvm_unreachable("Cannot get the address for an undefined symbol."); @@ -401,10 +384,9 @@ struct Symbol { // 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> + llvm::AlignedCharArrayUnion< + DefinedRegular, DefinedCommon, DefinedAbsolute, DefinedRelative, Lazy, + Undefined, DefinedImportData, DefinedImportThunk, DefinedLocalImport> Body; SymbolBody *body() { diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp index 71217ebeb60a..8762b88c4d6b 100644 --- a/COFF/Writer.cpp +++ b/COFF/Writer.cpp @@ -12,6 +12,7 @@ #include "DLL.h" #include "Error.h" #include "InputFiles.h" +#include "MapFile.h" #include "Memory.h" #include "PDB.h" #include "SymbolTable.h" @@ -39,7 +40,6 @@ using namespace llvm::support::endian; using namespace lld; using namespace lld::coff; -static const int PageSize = 4096; static const int SectorSize = 512; static const int DOSStubSize = 64; static const int NumberfOfDataDirectory = 16; @@ -163,51 +163,6 @@ namespace coff { void writeResult(SymbolTable *T) { Writer(T).run(); } -// OutputSection represents a section in an output file. It's a -// container of chunks. OutputSection and Chunk are 1:N relationship. -// Chunks cannot belong to more than one OutputSections. The writer -// creates multiple OutputSections and assign them unique, -// non-overlapping file offsets and RVAs. -class OutputSection { -public: - OutputSection(StringRef N) : Name(N), Header({}) {} - void setRVA(uint64_t); - void setFileOffset(uint64_t); - void addChunk(Chunk *C); - StringRef getName() { return Name; } - std::vector<Chunk *> &getChunks() { return Chunks; } - void addPermissions(uint32_t C); - void setPermissions(uint32_t C); - uint32_t getPermissions() { return Header.Characteristics & PermMask; } - uint32_t getCharacteristics() { return Header.Characteristics; } - uint64_t getRVA() { return Header.VirtualAddress; } - uint64_t getFileOff() { return Header.PointerToRawData; } - void writeHeaderTo(uint8_t *Buf); - - // Returns the size of this section in an executable memory image. - // This may be smaller than the raw size (the raw size is multiple - // of disk sector size, so there may be padding at end), or may be - // larger (if that's the case, the loader reserves spaces after end - // of raw data). - uint64_t getVirtualSize() { return Header.VirtualSize; } - - // Returns the size of the section in the output file. - uint64_t getRawSize() { return Header.SizeOfRawData; } - - // Set offset into the string table storing this section name. - // Used only when the name is longer than 8 bytes. - void setStringTableOff(uint32_t V) { StringTableOff = V; } - - // N.B. The section index is one based. - uint32_t SectionIndex = 0; - -private: - StringRef Name; - coff_section Header; - uint32_t StringTableOff = 0; - std::vector<Chunk *> Chunks; -}; - void OutputSection::setRVA(uint64_t RVA) { Header.VirtualAddress = RVA; for (Chunk *C : Chunks) @@ -303,8 +258,14 @@ void Writer::run() { sortExceptionTable(); writeBuildId(); - if (!Config->PDBPath.empty()) - createPDB(Config->PDBPath, Symtab, SectionTable, BuildId->DI); + if (!Config->PDBPath.empty()) { + const llvm::codeview::DebugInfo *DI = nullptr; + if (Config->DebugTypes & static_cast<unsigned>(coff::DebugType::CV)) + DI = BuildId->DI; + createPDB(Config->PDBPath, Symtab, SectionTable, DI); + } + + writeMapFile(OutputSections); if (auto EC = Buffer->commit()) fatal(EC, "failed to write the output file"); @@ -641,6 +602,8 @@ template <typename PEHeaderTy> void Writer::writeHeader() { PE->SizeOfStackCommit = Config->StackCommit; PE->SizeOfHeapReserve = Config->HeapReserve; PE->SizeOfHeapCommit = Config->HeapCommit; + if (Config->AppContainer) + PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_APPCONTAINER; if (Config->DynamicBase) PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE; if (Config->HighEntropyVA) @@ -830,7 +793,7 @@ void Writer::writeBuildId() { "only PDB 7.0 is supported"); assert(sizeof(Res) == sizeof(BuildId->DI->PDB70.Signature) && "signature size mismatch"); - memcpy(BuildId->DI->PDB70.Signature, Res, + memcpy(BuildId->DI->PDB70.Signature, Res.Bytes.data(), sizeof(codeview::PDB70DebugInfo::Signature)); // TODO(compnerd) track the Age BuildId->DI->PDB70.Age = 1; diff --git a/COFF/Writer.h b/COFF/Writer.h index 0d26090177d8..fef575423878 100644 --- a/COFF/Writer.h +++ b/COFF/Writer.h @@ -10,14 +10,65 @@ #ifndef LLD_COFF_WRITER_H #define LLD_COFF_WRITER_H +#include "Chunks.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/COFF.h" +#include <cstdint> #include <vector> namespace lld { namespace coff { class SymbolTable; +static const int PageSize = 4096; + void writeResult(SymbolTable *T); +// OutputSection represents a section in an output file. It's a +// container of chunks. OutputSection and Chunk are 1:N relationship. +// Chunks cannot belong to more than one OutputSections. The writer +// creates multiple OutputSections and assign them unique, +// non-overlapping file offsets and RVAs. +class OutputSection { +public: + OutputSection(llvm::StringRef N) : Name(N), Header({}) {} + void setRVA(uint64_t); + void setFileOffset(uint64_t); + void addChunk(Chunk *C); + llvm::StringRef getName() { return Name; } + std::vector<Chunk *> &getChunks() { return Chunks; } + void addPermissions(uint32_t C); + void setPermissions(uint32_t C); + uint32_t getPermissions() { return Header.Characteristics & PermMask; } + uint32_t getCharacteristics() { return Header.Characteristics; } + uint64_t getRVA() { return Header.VirtualAddress; } + uint64_t getFileOff() { return Header.PointerToRawData; } + void writeHeaderTo(uint8_t *Buf); + + // Returns the size of this section in an executable memory image. + // This may be smaller than the raw size (the raw size is multiple + // of disk sector size, so there may be padding at end), or may be + // larger (if that's the case, the loader reserves spaces after end + // of raw data). + uint64_t getVirtualSize() { return Header.VirtualSize; } + + // Returns the size of the section in the output file. + uint64_t getRawSize() { return Header.SizeOfRawData; } + + // Set offset into the string table storing this section name. + // Used only when the name is longer than 8 bytes. + void setStringTableOff(uint32_t V) { StringTableOff = V; } + + // N.B. The section index is one based. + uint32_t SectionIndex = 0; + +private: + llvm::StringRef Name; + llvm::object::coff_section Header; + uint32_t StringTableOff = 0; + std::vector<Chunk *> Chunks; +}; + } } |
