diff --git a/lld/COFF/COFFLinkerContext.h b/lld/COFF/COFFLinkerContext.h --- a/lld/COFF/COFFLinkerContext.h +++ b/lld/COFF/COFFLinkerContext.h @@ -15,12 +15,13 @@ #include "InputFiles.h" #include "SymbolTable.h" #include "Writer.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Timer.h" namespace lld { namespace coff { -class COFFLinkerContext { +class COFFLinkerContext : public CommonLinkerContext { public: COFFLinkerContext(); COFFLinkerContext(const COFFLinkerContext &) = delete; diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp --- a/lld/COFF/Chunks.cpp +++ b/lld/COFF/Chunks.cpp @@ -12,7 +12,6 @@ #include "SymbolTable.h" #include "Symbols.h" #include "Writer.h" -#include "lld/Common/ErrorHandler.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFF.h" @@ -430,7 +429,7 @@ return; warn("some relocations in " + file->getName() + " are not sorted"); MutableArrayRef newRelocs( - bAlloc.Allocate(relocsSize), relocsSize); + bAlloc().Allocate(relocsSize), relocsSize); memcpy(newRelocs.data(), relocsData, relocsSize * sizeof(coff_relocation)); llvm::sort(newRelocs, cmpByVa); setRelocs(newRelocs); diff --git a/lld/COFF/DLL.cpp b/lld/COFF/DLL.cpp --- a/lld/COFF/DLL.cpp +++ b/lld/COFF/DLL.cpp @@ -659,14 +659,14 @@ // Add a syntentic symbol for this load thunk, using the "__imp_load" // prefix, in case this thunk needs to be added to the list of valid // call targets for Control Flow Guard. - StringRef symName = saver.save("__imp_load_" + extName); + StringRef symName = saver().save("__imp_load_" + extName); s->loadThunkSym = cast(ctx.symtab.addSynthetic(symName, t)); } } thunks.push_back(tm); StringRef tmName = - saver.save("__tailMerge_" + syms[0]->getDLLName().lower()); + saver().save("__tailMerge_" + syms[0]->getDLLName().lower()); ctx.symtab.addSynthetic(tmName, tm); // Terminate with null values. addresses.push_back(make(8)); diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -19,9 +19,7 @@ #include "Writer.h" #include "lld/Common/Args.h" #include "lld/Common/Driver.h" -#include "lld/Common/ErrorHandler.h" #include "lld/Common/Filesystem.h" -#include "lld/Common/Memory.h" #include "lld/Common/Timer.h" #include "lld/Common/Version.h" #include "llvm/ADT/Optional.h" @@ -63,36 +61,23 @@ std::unique_ptr config; std::unique_ptr driver; -bool link(ArrayRef args, bool canExitEarly, raw_ostream &stdoutOS, - raw_ostream &stderrOS) { - lld::stdoutOS = &stdoutOS; - lld::stderrOS = &stderrOS; +bool link(ArrayRef args, raw_ostream &stdoutOS, + raw_ostream &stderrOS, bool exitEarly, bool disableOutput) { + // This driver-specific context will be freed later by lldMain(). + // It is created on the heap to avoid destructors if 'exitEarly' is set. + auto *ctx = new COFFLinkerContext; - errorHandler().cleanupCallback = []() { - freeArena(); - }; - - errorHandler().logName = args::getFilenameWithoutExe(args[0]); - errorHandler().errorLimitExceededMsg = - "too many errors emitted, stopping now" - " (use /errorlimit:0 to see all errors)"; - errorHandler().exitEarly = canExitEarly; - stderrOS.enable_colors(stderrOS.has_colors()); + ctx->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput); + ctx->e.logName = args::getFilenameWithoutExe(args[0]); + ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now" + " (use /errorlimit:0 to see all errors)"; - COFFLinkerContext ctx; config = std::make_unique(); - driver = std::make_unique(ctx); + driver = std::make_unique(*ctx); driver->linkerMain(args); - // Call exit() if we can to avoid calling destructors. - if (canExitEarly) - exitLld(errorCount() ? 1 : 0); - - bool ret = errorCount() == 0; - if (!canExitEarly) - errorHandler().reset(); - return ret; + return errorCount() == 0; } // Parse options of the form "old;new". @@ -162,7 +147,7 @@ static StringRef mangle(StringRef sym) { assert(config->machine != IMAGE_FILE_MACHINE_UNKNOWN); if (config->machine == I386) - return saver.save("_" + sym); + return saver().save("_" + sym); return sym; } @@ -358,9 +343,9 @@ Export exp = parseExport(e); if (config->machine == I386 && config->mingw) { if (!isDecorated(exp.name)) - exp.name = saver.save("_" + exp.name); + exp.name = saver().save("_" + exp.name); if (!exp.extName.empty() && !isDecorated(exp.extName)) - exp.extName = saver.save("_" + exp.extName); + exp.extName = saver().save("_" + exp.extName); } exp.directives = true; config->exports.push_back(exp); @@ -442,11 +427,11 @@ SmallString<128> path = dir; sys::path::append(path, filename); if (sys::fs::exists(path.str())) - return saver.save(path.str()); + return saver().save(path.str()); if (!hasExt) { path.append(".obj"); if (sys::fs::exists(path.str())) - return saver.save(path.str()); + return saver().save(path.str()); } } return filename; @@ -483,7 +468,7 @@ SmallString<128> s = filename; sys::path::replace_extension(s, ".a"); - StringRef libName = saver.save("lib" + s.str()); + StringRef libName = saver().save("lib" + s.str()); return doFindFile(libName); } @@ -492,7 +477,7 @@ // Add ".lib" to Filename if that has no file extension. bool hasExt = filename.contains('.'); if (!hasExt) - filename = saver.save(filename + ".lib"); + filename = saver().save(filename + ".lib"); StringRef ret = doFindFile(filename); // For MinGW, if the find above didn't turn up anything, try // looking for a MinGW formatted library name. @@ -525,7 +510,7 @@ Optional envOpt = Process::GetEnv("LIB"); if (!envOpt.hasValue()) return; - StringRef env = saver.save(*envOpt); + StringRef env = saver().save(*envOpt); while (!env.empty()) { StringRef path; std::tie(path, env) = env.split(';'); @@ -872,8 +857,8 @@ driver->takeBuffer(std::move(mb)); if (config->outputFile.empty()) - config->outputFile = std::string(saver.save(m.OutputFile)); - config->importName = std::string(saver.save(m.ImportName)); + config->outputFile = std::string(saver().save(m.OutputFile)); + config->importName = std::string(saver().save(m.ImportName)); if (m.ImageBase) config->imageBase = m.ImageBase; if (m.StackReserve) @@ -901,13 +886,13 @@ // DLL instead. This is supported by both MS and GNU linkers. if (!e1.ExtName.empty() && e1.ExtName != e1.Name && StringRef(e1.Name).contains('.')) { - e2.name = saver.save(e1.ExtName); - e2.forwardTo = saver.save(e1.Name); + e2.name = saver().save(e1.ExtName); + e2.forwardTo = saver().save(e1.Name); config->exports.push_back(e2); continue; } - e2.name = saver.save(e1.Name); - e2.extName = saver.save(e1.ExtName); + e2.name = saver().save(e1.Name); + e2.extName = saver().save(e1.ExtName); e2.ordinal = e1.Ordinal; e2.noname = e1.Noname; e2.data = e1.Data; @@ -1904,9 +1889,9 @@ Export e = parseExport(arg->getValue()); if (config->machine == I386) { if (!isDecorated(e.name)) - e.name = saver.save("_" + e.name); + e.name = saver().save("_" + e.name); if (!e.extName.empty() && !isDecorated(e.extName)) - e.extName = saver.save("_" + e.extName); + e.extName = saver().save("_" + e.extName); } config->exports.push_back(e); } diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp --- a/lld/COFF/DriverUtils.cpp +++ b/lld/COFF/DriverUtils.cpp @@ -48,17 +48,17 @@ class Executor { public: - explicit Executor(StringRef s) : 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)); } + explicit Executor(StringRef s) : 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 run() { ErrorOr exeOrErr = sys::findProgramByName(prog); if (auto ec = exeOrErr.getError()) fatal("unable to find " + prog + " in PATH: " + ec.message()); - StringRef exe = saver.save(*exeOrErr); + StringRef exe = saver().save(*exeOrErr); args.insert(args.begin(), exe); if (sys::ExecuteAndWait(args[0], args) != 0) @@ -636,14 +636,14 @@ sym = sym.substr(0, sym.find('@', 1)); if (!sym.startswith("@")) { if (prefix && !sym.startswith("_")) - return saver.save("_" + sym); + return saver().save("_" + sym); return sym; } // For fastcall, remove the leading @ and replace it with an // underscore, if prefixes are used. sym = sym.substr(1); if (prefix) - sym = saver.save("_" + sym); + sym = saver().save("_" + sym); return sym; } @@ -854,7 +854,7 @@ argv.data() + argv.size()); if (!args.hasArg(OPT_lldignoreenv)) addLINK(expandedArgv); - cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv); + cl::ExpandResponseFiles(saver(), getQuotingStyle(args), expandedArgv); args = optTable.ParseArgs(makeArrayRef(expandedArgv).drop_front(), missingIndex, missingCount); @@ -901,7 +901,7 @@ // Handle /EXPORT and /INCLUDE in a fast path. These directives can appear for // potentially every symbol in the object, so they must be handled quickly. SmallVector tokens; - cl::TokenizeWindowsCommandLineNoCopy(s, saver, tokens); + cl::TokenizeWindowsCommandLineNoCopy(s, saver(), tokens); for (StringRef tok : tokens) { if (tok.startswith_insensitive("/export:") || tok.startswith_insensitive("-export:")) @@ -914,7 +914,7 @@ // already copied quoted arguments for us, so those do not need to be // copied again. bool HasNul = tok.end() != s.end() && tok.data()[tok.size()] == '\0'; - rest.push_back(HasNul ? tok.data() : saver.save(tok).data()); + rest.push_back(HasNul ? tok.data() : saver().save(tok).data()); } } @@ -948,7 +948,7 @@ std::vector ArgParser::tokenize(StringRef s) { SmallVector tokens; - cl::TokenizeWindowsCommandLine(s, saver, tokens); + cl::TokenizeWindowsCommandLine(s, saver(), tokens); return std::vector(tokens.begin(), tokens.end()); } diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp --- a/lld/COFF/InputFiles.cpp +++ b/lld/COFF/InputFiles.cpp @@ -15,8 +15,6 @@ #include "SymbolTable.h" #include "Symbols.h" #include "lld/Common/DWARF.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" #include "llvm-c/lto.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Triple.h" @@ -905,7 +903,7 @@ Optional> ret = dwarf->getVariableLoc(var); if (!ret) return None; - return std::make_pair(saver.save(ret->first), ret->second); + return std::make_pair(saver().save(ret->first), ret->second); } // Used only for DWARF debug info, which is not common (except in MinGW @@ -940,8 +938,8 @@ fatal("broken import library"); // Read names and create an __imp_ symbol. - StringRef name = saver.save(StringRef(buf + sizeof(*hdr))); - StringRef impName = saver.save("__imp_" + name); + StringRef name = saver().save(StringRef(buf + sizeof(*hdr))); + StringRef impName = saver().save("__imp_" + name); const char *nameStart = buf + sizeof(coff_import_header) + name.size() + 1; dllName = std::string(StringRef(nameStart)); StringRef extName; @@ -995,11 +993,12 @@ // into consideration at LTO time (which very likely causes undefined // symbols later in the link stage). So we append file offset to make // filename unique. - MemoryBufferRef mbref( - mb.getBuffer(), - saver.save(archiveName.empty() ? path - : archiveName + sys::path::filename(path) + - utostr(offsetInArchive))); + MemoryBufferRef mbref(mb.getBuffer(), + saver().save(archiveName.empty() + ? path + : archiveName + + sys::path::filename(path) + + utostr(offsetInArchive))); obj = check(lto::InputFile::create(mbref)); } @@ -1035,6 +1034,7 @@ } // namespace void BitcodeFile::parse() { + llvm::StringSaver &saver = lld::saver(); std::vector> comdat(obj->getComdatTable().size()); for (size_t i = 0; i != obj->getComdatTable().size(); ++i) // FIXME: Check nodeduplicate @@ -1156,11 +1156,11 @@ s->nameType = ImportNameType::IMPORT_NAME; if (coffObj->getMachine() == I386) { - s->symbolName = symbolName = saver.save("_" + symbolName); + s->symbolName = symbolName = saver().save("_" + symbolName); s->nameType = ImportNameType::IMPORT_NAME_NOPREFIX; } - StringRef impName = saver.save("__imp_" + symbolName); + StringRef impName = saver().save("__imp_" + symbolName); ctx.symtab.addLazyDLLSymbol(this, s, impName); if (code) ctx.symtab.addLazyDLLSymbol(this, s, symbolName); @@ -1179,7 +1179,7 @@ size_t impSize = s->dllName.size() + s->symbolName.size() + 2; // +2 for NULs size_t size = sizeof(coff_import_header) + impSize; - char *buf = bAlloc.Allocate(size); + char *buf = bAlloc().Allocate(size); memset(buf, 0, size); char *p = buf; auto *imp = reinterpret_cast(p); diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp --- a/lld/COFF/LTO.cpp +++ b/lld/COFF/LTO.cpp @@ -11,7 +11,7 @@ #include "InputFiles.h" #include "Symbols.h" #include "lld/Common/Args.h" -#include "lld/Common/ErrorHandler.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Strings.h" #include "lld/Common/TargetOptionsCommandFlags.h" #include "llvm/ADT/STLExtras.h" @@ -209,8 +209,8 @@ // - foo.exe.lto.1.obj // - ... StringRef ltoObjName = - saver.save(Twine(config->outputFile) + ".lto" + - (i == 0 ? Twine("") : Twine('.') + Twine(i)) + ".obj"); + saver().save(Twine(config->outputFile) + ".lto" + + (i == 0 ? Twine("") : Twine('.') + Twine(i)) + ".obj"); // Get the native object contents either from the cache or from memory. Do // not use the cached MemoryBuffer directly, or the PDB will not be diff --git a/lld/COFF/MinGW.cpp b/lld/COFF/MinGW.cpp --- a/lld/COFF/MinGW.cpp +++ b/lld/COFF/MinGW.cpp @@ -11,7 +11,6 @@ #include "Driver.h" #include "InputFiles.h" #include "SymbolTable.h" -#include "lld/Common/ErrorHandler.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/Object/COFF.h" @@ -184,8 +183,8 @@ static StringRef mangle(Twine sym) { assert(config->machine != IMAGE_FILE_MACHINE_UNKNOWN); if (config->machine == I386) - return saver.save("_" + sym); - return saver.save(sym); + return saver().save("_" + sym); + return saver().save(sym); } // Handles -wrap option. @@ -249,7 +248,7 @@ // referenced it or not, though.) if (imp) { DefinedLocalImport *wrapimp = make( - saver.save("__imp_" + w.wrap->getName()), d); + saver().save("__imp_" + w.wrap->getName()), d); ctx.symtab.localImportChunks.push_back(wrapimp->getChunk()); map[imp] = wrapimp; } diff --git a/lld/COFF/PDB.cpp b/lld/COFF/PDB.cpp --- a/lld/COFF/PDB.cpp +++ b/lld/COFF/PDB.cpp @@ -16,7 +16,6 @@ #include "Symbols.h" #include "TypeMerger.h" #include "Writer.h" -#include "lld/Common/ErrorHandler.h" #include "lld/Common/Timer.h" #include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" #include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" @@ -75,7 +74,7 @@ public: PDBLinker(COFFLinkerContext &ctx) - : builder(bAlloc), tMerger(ctx, bAlloc), ctx(ctx) { + : builder(bAlloc()), tMerger(ctx, bAlloc()), ctx(ctx) { // This isn't strictly necessary, but link.exe usually puts an empty string // as the first "valid" string in the string table, so we do the same in // order to maintain as much byte-for-byte compatibility as possible. @@ -501,7 +500,7 @@ case SymbolKind::S_LPROCREF: { // sym is a temporary object, so we have to copy and reallocate the record // to stabilize it. - uint8_t *mem = bAlloc.Allocate(sym.length()); + uint8_t *mem = bAlloc().Allocate(sym.length()); memcpy(mem, sym.data().data(), sym.length()); builder.addGlobalSymbol(CVSymbol(makeArrayRef(mem, sym.length()))); break; @@ -1003,7 +1002,7 @@ // Allocate memory for a .debug$S / .debug$F section and relocate it. static ArrayRef relocateDebugChunk(SectionChunk &debugChunk) { - uint8_t *buffer = bAlloc.Allocate(debugChunk.getSize()); + uint8_t *buffer = bAlloc().Allocate(debugChunk.getSize()); assert(debugChunk.getOutputSectionIdx() == 0 && "debug sections should not be in output sections"); debugChunk.writeTo(buffer); @@ -1417,6 +1416,7 @@ ebs.Fields.push_back(path); ebs.Fields.push_back("cmd"); ebs.Fields.push_back(argStr); + llvm::BumpPtrAllocator &bAlloc = lld::bAlloc(); mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( ons, bAlloc, CodeViewContainer::Pdb)); mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( @@ -1448,7 +1448,7 @@ cgs.Characteristics |= llvm::COFF::IMAGE_SCN_MEM_WRITE; mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( - cgs, bAlloc, CodeViewContainer::Pdb)); + cgs, bAlloc(), CodeViewContainer::Pdb)); } static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod, @@ -1461,7 +1461,7 @@ sym.Rva = os.getRVA(); sym.SectionNumber = os.sectionIndex; mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( - sym, bAlloc, CodeViewContainer::Pdb)); + sym, bAlloc(), CodeViewContainer::Pdb)); // Skip COFF groups in MinGW because it adds a significant footprint to the // PDB, due to each function being in its own section @@ -1536,6 +1536,7 @@ ts.Segment = thunkOS->sectionIndex; ts.Offset = thunkChunk->getRVA() - thunkOS->getRVA(); + llvm::BumpPtrAllocator &bAlloc = lld::bAlloc(); mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol( ons, bAlloc, CodeViewContainer::Pdb)); mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol( diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp --- a/lld/COFF/SymbolTable.cpp +++ b/lld/COFF/SymbolTable.cpp @@ -134,7 +134,7 @@ const DILineInfo &lineInfo = *optionalLineInfo; if (lineInfo.FileName == DILineInfo::BadString) return None; - return std::make_pair(saver.save(lineInfo.FileName), lineInfo.Line); + return std::make_pair(saver().save(lineInfo.FileName), lineInfo.Line); } static Optional> diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -485,7 +485,7 @@ MutableArrayRef newRelocs; if (originalRelocs.data() == curRelocs.data()) { newRelocs = makeMutableArrayRef( - bAlloc.Allocate(originalRelocs.size()), + bAlloc().Allocate(originalRelocs.size()), originalRelocs.size()); } else { newRelocs = makeMutableArrayRef( diff --git a/lld/Common/CMakeLists.txt b/lld/Common/CMakeLists.txt --- a/lld/Common/CMakeLists.txt +++ b/lld/Common/CMakeLists.txt @@ -28,6 +28,7 @@ add_lld_library(lldCommon Args.cpp + CommonLinkerContext.cpp DWARF.cpp ErrorHandler.cpp Filesystem.cpp diff --git a/lld/Common/CommonLinkerContext.cpp b/lld/Common/CommonLinkerContext.cpp new file mode 100644 --- /dev/null +++ b/lld/Common/CommonLinkerContext.cpp @@ -0,0 +1,41 @@ +//===- CommonLinkerContext.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/CommonLinkerContext.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.h" + +using namespace llvm; +using namespace lld; + +// Reference to the current LLD instance. +static CommonLinkerContext *lctx; + +CommonLinkerContext::CommonLinkerContext() { lctx = this; } + +CommonLinkerContext::~CommonLinkerContext() { + assert(lctx); + // Explicitly call the destructors since we created the objects with placement + // new in SpecificAlloc::create(). + for (auto &it : instances) + it.second->~SpecificAllocBase(); + lctx = nullptr; +} + +CommonLinkerContext &lld::commonContext() { + assert(lctx); + return *lctx; +} + +bool lld::hasContext() { return lctx != nullptr; } + +void CommonLinkerContext::destroy() { + if (lctx == nullptr) + return; + delete lctx; +} diff --git a/lld/Common/ErrorHandler.cpp b/lld/Common/ErrorHandler.cpp --- a/lld/Common/ErrorHandler.cpp +++ b/lld/Common/ErrorHandler.cpp @@ -10,6 +10,7 @@ #include "llvm/Support/Parallel.h" +#include "lld/Common/CommonLinkerContext.h" #include "llvm/ADT/Twine.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" @@ -18,51 +19,69 @@ #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" -#include #include using namespace llvm; using namespace lld; -// The functions defined in this file can be called from multiple threads, -// but lld::outs() or lld::errs() are not thread-safe. We protect them using a -// mutex. -static std::mutex mu; - -// We want to separate multi-line messages with a newline. `sep` is "\n" -// if the last messages was multi-line. Otherwise "". -static StringRef sep; - static StringRef getSeparator(const Twine &msg) { if (StringRef(msg.str()).contains('\n')) return "\n"; return ""; } -raw_ostream *lld::stdoutOS; -raw_ostream *lld::stderrOS; +ErrorHandler::~ErrorHandler() { + if (cleanupCallback) + cleanupCallback(); +} + +void ErrorHandler::initialize(llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, + bool disableOutput) { + this->stdoutOS = &stdoutOS; + this->stderrOS = &stderrOS; + stderrOS.enable_colors(stderrOS.has_colors()); + this->exitEarly = exitEarly; + this->disableOutput = disableOutput; +} -ErrorHandler &lld::errorHandler() { - static ErrorHandler handler; - return handler; +void ErrorHandler::flushStreams() { + std::lock_guard lock(mu); + outs().flush(); + errs().flush(); } +ErrorHandler &lld::errorHandler() { return context().e; } + raw_ostream &lld::outs() { - if (errorHandler().disableOutput) + ErrorHandler &e = errorHandler(); + return e.outs(); +} + +raw_ostream &lld::errs() { + ErrorHandler &e = errorHandler(); + return e.errs(); +} + +raw_ostream &ErrorHandler::outs() { + if (disableOutput) return llvm::nulls(); return stdoutOS ? *stdoutOS : llvm::outs(); } -raw_ostream &lld::errs() { - if (errorHandler().disableOutput) +raw_ostream &ErrorHandler::errs() { + if (disableOutput) return llvm::nulls(); return stderrOS ? *stderrOS : llvm::errs(); } void lld::exitLld(int val) { - // Delete any temporary file, while keeping the memory mapping open. - if (errorHandler().outputBuffer) - errorHandler().outputBuffer->discard(); + if (hasContext()) { + ErrorHandler &e = errorHandler(); + // Delete any temporary file, while keeping the memory mapping open. + if (e.outputBuffer) + e.outputBuffer->discard(); + } // Re-throw a possible signal or exception once/if it was catched by // safeLldMain(). @@ -75,11 +94,9 @@ if (!CrashRecoveryContext::GetCurrent()) llvm_shutdown(); - { - std::lock_guard lock(mu); - lld::outs().flush(); - lld::errs().flush(); - } + if (hasContext()) + lld::errorHandler().flushStreams(); + // When running inside safeLldMain(), restore the control flow back to the // CrashRecoveryContext. Otherwise simply use _exit(), meanning no cleanup, // since we want to avoid further crashes on shutdown. diff --git a/lld/Common/Memory.cpp b/lld/Common/Memory.cpp --- a/lld/Common/Memory.cpp +++ b/lld/Common/Memory.cpp @@ -7,16 +7,19 @@ //===----------------------------------------------------------------------===// #include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" using namespace llvm; using namespace lld; -BumpPtrAllocator lld::bAlloc; -StringSaver lld::saver{bAlloc}; -std::vector lld::SpecificAllocBase::instances; - -void lld::freeArena() { - for (SpecificAllocBase *alloc : SpecificAllocBase::instances) - alloc->reset(); - bAlloc.Reset(); +SpecificAllocBase * +lld::SpecificAllocBase::getOrCreate(void *tag, size_t size, size_t align, + SpecificAllocBase *(&creator)(void *)) { + auto &instances = context().instances; + auto &instance = instances[tag]; + if (instance == nullptr) { + void *storage = context().bAlloc.Allocate(size, align); + instance = creator(storage); + } + return instance; } diff --git a/lld/Common/TargetOptionsCommandFlags.cpp b/lld/Common/TargetOptionsCommandFlags.cpp --- a/lld/Common/TargetOptionsCommandFlags.cpp +++ b/lld/Common/TargetOptionsCommandFlags.cpp @@ -7,12 +7,9 @@ //===----------------------------------------------------------------------===// #include "lld/Common/TargetOptionsCommandFlags.h" - #include "llvm/CodeGen/CommandFlags.h" #include "llvm/Target/TargetOptions.h" -static llvm::codegen::RegisterCodeGenFlags CGF; - llvm::TargetOptions lld::initTargetOptionsFromCodeGenFlags() { return llvm::codegen::InitTargetOptionsFromCodeGenFlags(llvm::Triple()); } diff --git a/lld/ELF/AArch64ErrataFix.cpp b/lld/ELF/AArch64ErrataFix.cpp --- a/lld/ELF/AArch64ErrataFix.cpp +++ b/lld/ELF/AArch64ErrataFix.cpp @@ -33,7 +33,7 @@ #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Strings.h" #include "llvm/Support/Endian.h" #include "llvm/Support/raw_ostream.h" @@ -398,9 +398,9 @@ patchee(p), patcheeOffset(off) { this->parent = p->getParent(); patchSym = addSyntheticLocal( - saver.save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC, 0, - getSize(), *this); - addSyntheticLocal(saver.save("$x"), STT_NOTYPE, 0, 0, *this); + saver().save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC, + 0, getSize(), *this); + addSyntheticLocal(saver().save("$x"), STT_NOTYPE, 0, 0, *this); } uint64_t Patch843419Section::getLDSTAddr() const { diff --git a/lld/ELF/ARMErrataFix.cpp b/lld/ELF/ARMErrataFix.cpp --- a/lld/ELF/ARMErrataFix.cpp +++ b/lld/ELF/ARMErrataFix.cpp @@ -22,7 +22,7 @@ #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Strings.h" #include "llvm/Support/Endian.h" #include "llvm/Support/raw_ostream.h" @@ -142,9 +142,9 @@ patchee(p), patcheeOffset(off), instr(instr), isARM(isARM) { parent = p->getParent(); patchSym = addSyntheticLocal( - saver.save("__CortexA8657417_" + utohexstr(getBranchAddr())), STT_FUNC, + saver().save("__CortexA8657417_" + utohexstr(getBranchAddr())), STT_FUNC, isARM ? 0 : 1, getSize(), *this); - addSyntheticLocal(saver.save(isARM ? "$a" : "$t"), STT_NOTYPE, 0, 0, *this); + addSyntheticLocal(saver().save(isARM ? "$a" : "$t"), STT_NOTYPE, 0, 0, *this); } uint64_t Patch657417Section::getBranchAddr() const { diff --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp --- a/lld/ELF/Arch/PPC64.cpp +++ b/lld/ELF/Arch/PPC64.cpp @@ -11,8 +11,7 @@ #include "SyntheticSections.h" #include "Target.h" #include "Thunks.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "llvm/Support/Endian.h" using namespace llvm; @@ -197,7 +196,7 @@ Symbol *sym = symtab->find(name); if (!sym || sym->isDefined()) return false; - sym->resolve(Defined{/*file=*/nullptr, saver.save(name), STB_GLOBAL, + sym->resolve(Defined{/*file=*/nullptr, saver().save(name), STB_GLOBAL, STV_HIDDEN, STT_FUNC, value, /*size=*/0, /*section=*/nullptr}); defined.push_back(cast(sym)); diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -77,14 +77,13 @@ static void setConfigs(opt::InputArgList &args); static void readConfigs(opt::InputArgList &args); -bool elf::link(ArrayRef args, bool canExitEarly, - raw_ostream &stdoutOS, raw_ostream &stderrOS) { - lld::stdoutOS = &stdoutOS; - lld::stderrOS = &stderrOS; - - errorHandler().cleanupCallback = []() { - freeArena(); +bool elf::link(ArrayRef args, raw_ostream &stdoutOS, + raw_ostream &stderrOS, bool exitEarly, bool disableOutput) { + // This driver-specific context will be freed later by lldMain(). + auto *ctx = new CommonLinkerContext; + ctx->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput); + ctx->e.cleanupCallback = []() { inputSections.clear(); outputSections.clear(); memoryBuffers.clear(); @@ -106,13 +105,9 @@ SharedFile::vernauxNum = 0; }; - - errorHandler().logName = args::getFilenameWithoutExe(args[0]); - errorHandler().errorLimitExceededMsg = - "too many errors emitted, stopping now (use " - "-error-limit=0 to see all errors)"; - errorHandler().exitEarly = canExitEarly; - stderrOS.enable_colors(stderrOS.has_colors()); + ctx->e.logName = args::getFilenameWithoutExe(args[0]); + ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now (use " + "-error-limit=0 to see all errors)"; config = std::make_unique(); driver = std::make_unique(); @@ -126,15 +121,7 @@ driver->linkerMain(args); - // Exit immediately if we don't need to return to the caller. - // This saves time because the overhead of calling destructors - // for all globally-allocated objects is not negligible. - int hasError = errorCount() ? 1 : 0; - if (canExitEarly) - exitLld(hasError); - else - errorHandler().reset(); - return !hasError; + return errorCount() == 0; } // Parses a linker -m option. @@ -1259,7 +1246,7 @@ // Parse LTO options. if (auto *arg = args.getLastArg(OPT_plugin_opt_mcpu_eq)) - parseClangOption(saver.save("-mcpu=" + StringRef(arg->getValue())), + parseClangOption(saver().save("-mcpu=" + StringRef(arg->getValue())), arg->getSpelling()); for (opt::Arg *arg : args.filtered(OPT_plugin_opt_eq_minus)) @@ -2072,9 +2059,9 @@ if (!sym) continue; - Symbol *real = addUnusedUndefined(saver.save("__real_" + name)); + Symbol *real = addUnusedUndefined(saver().save("__real_" + name)); Symbol *wrap = - addUnusedUndefined(saver.save("__wrap_" + name), sym->binding); + addUnusedUndefined(saver().save("__wrap_" + name), sym->binding); v.push_back({sym, real, wrap}); // We want to tell LTO not to inline symbols to be overwritten diff --git a/lld/ELF/DriverUtils.cpp b/lld/ELF/DriverUtils.cpp --- a/lld/ELF/DriverUtils.cpp +++ b/lld/ELF/DriverUtils.cpp @@ -13,8 +13,7 @@ //===----------------------------------------------------------------------===// #include "Driver.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Reproduce.h" #include "lld/Common/Version.h" #include "llvm/ADT/Optional.h" @@ -102,7 +101,7 @@ for (size_t i = 0, e = args.size(); i != e; ++i) { StringRef s = args[i]; if ((s == "-plugin-opt" || s == "--plugin-opt") && i + 1 != e) { - v.push_back(saver.save(s + "=" + args[i + 1]).data()); + v.push_back(saver().save(s + "=" + args[i + 1]).data()); ++i; } else { v.push_back(args[i]); @@ -125,7 +124,7 @@ // Expand response files (arguments in the form of @) // and then parse the argument again. - cl::ExpandResponseFiles(saver, getQuotingStyle(args), vec); + cl::ExpandResponseFiles(saver(), getQuotingStyle(args), vec); concatLTOPluginOptions(vec); args = this->ParseArgs(vec, missingIndex, missingCount); diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -13,9 +13,8 @@ #include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/DWARF.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" #include "llvm/ADT/STLExtras.h" #include "llvm/CodeGen/Analysis.h" #include "llvm/IR/LLVMContext.h" @@ -111,7 +110,7 @@ // The --chroot option changes our virtual root directory. // This is useful when you are dealing with files created by --reproduce. if (!config->chroot.empty() && path.startswith("/")) - path = saver.save(config->chroot + path); + path = saver().save(config->chroot + path); log(path); config->dependencyFiles.insert(llvm::CachedHashString(path)); @@ -1526,8 +1525,8 @@ } StringRef verName = stringTable.data() + verneeds[idx]; versionedNameBuffer.clear(); - name = - saver.save((name + "@" + verName).toStringRef(versionedNameBuffer)); + name = saver().save( + (name + "@" + verName).toStringRef(versionedNameBuffer)); } Symbol *s = symtab.addSymbol( Undefined{this, name, sym.getBinding(), sym.st_other, sym.getType()}); @@ -1569,7 +1568,7 @@ reinterpret_cast(verdefs[idx])->getAux()->vda_name; versionedNameBuffer.clear(); name = (name + "@" + verName).toStringRef(versionedNameBuffer); - symtab.addSymbol(SharedSymbol{*this, saver.save(name), sym.getBinding(), + symtab.addSymbol(SharedSymbol{*this, saver().save(name), sym.getBinding(), sym.st_other, sym.getType(), sym.st_value, sym.st_size, alignment, idx}); } @@ -1652,11 +1651,10 @@ // into consideration at LTO time (which very likely causes undefined // symbols later in the link stage). So we append file offset to make // filename unique. - StringRef name = - archiveName.empty() - ? saver.save(path) - : saver.save(archiveName + "(" + path::filename(path) + " at " + - utostr(offsetInArchive) + ")"); + StringRef name = archiveName.empty() + ? saver().save(path) + : saver().save(archiveName + "(" + path::filename(path) + + " at " + utostr(offsetInArchive) + ")"); MemoryBufferRef mbref(mb.getBuffer(), name); obj = CHECK(lto::InputFile::create(mbref), this); @@ -1692,7 +1690,7 @@ if (sym) { name = sym->getName(); } else { - name = saver.save(objSym.getName()); + name = saver().save(objSym.getName()); sym = symtab->insert(name); } @@ -1742,8 +1740,8 @@ symbols.resize(obj->symbols().size()); for (auto it : llvm::enumerate(obj->symbols())) if (!it.value().isUndefined()) - symbols[it.index()] = - symtab.addSymbol(LazyObject{*this, saver.save(it.value().getName())}); + symbols[it.index()] = symtab.addSymbol( + LazyObject{*this, saver().save(it.value().getName())}); } void BinaryFile::parse() { @@ -1761,6 +1759,8 @@ if (!isAlnum(s[i])) s[i] = '_'; + llvm::StringSaver &saver = lld::saver(); + symtab->addSymbol(Defined{nullptr, saver.save(s + "_start"), STB_GLOBAL, STV_DEFAULT, STT_OBJECT, 0, 0, section}); symtab->addSymbol(Defined{nullptr, saver.save(s + "_end"), STB_GLOBAL, diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -18,8 +18,7 @@ #include "SyntheticSections.h" #include "Target.h" #include "Thunks.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Compression.h" #include "llvm/Support/Endian.h" @@ -143,7 +142,7 @@ { static std::mutex mu; std::lock_guard lock(mu); - uncompressedBuf = bAlloc.Allocate(size); + uncompressedBuf = bAlloc().Allocate(size); } if (Error e = zlib::uncompress(toStringRef(rawData), uncompressedBuf, size)) @@ -236,7 +235,7 @@ // Restore the original section name. // (e.g. ".zdebug_info" -> ".debug_info") - name = saver.save("." + name.substr(2)); + name = saver().save("." + name.substr(2)); return; } diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -19,7 +19,7 @@ #include "SyntheticSections.h" #include "Target.h" #include "Writer.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Strings.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" @@ -64,8 +64,8 @@ if (InputSectionBase *rel = isec->getRelocatedSection()) { OutputSection *out = rel->getOutputSection(); if (s->type == SHT_RELA) - return saver.save(".rela" + out->name); - return saver.save(".rel" + out->name); + return saver().save(".rela" + out->name); + return saver().save(".rel" + out->name); } } diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp --- a/lld/ELF/MarkLive.cpp +++ b/lld/ELF/MarkLive.cpp @@ -27,7 +27,7 @@ #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Strings.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Object/ELF.h" @@ -308,8 +308,8 @@ // As a workaround for glibc libc.a before 2.34 // (https://sourceware.org/PR27492), retain __libc_atexit and similar // sections regardless of zStartStopGC. - cNamedSections[saver.save("__start_" + sec->name)].push_back(sec); - cNamedSections[saver.save("__stop_" + sec->name)].push_back(sec); + cNamedSections[saver().save("__start_" + sec->name)].push_back(sec); + cNamedSections[saver().save("__stop_" + sec->name)].push_back(sec); } } diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp --- a/lld/ELF/ScriptParser.cpp +++ b/lld/ELF/ScriptParser.cpp @@ -20,7 +20,7 @@ #include "ScriptLexer.h" #include "Symbols.h" #include "Target.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" @@ -290,7 +290,7 @@ SmallString<128> pathData; StringRef path = (config->sysroot + s).toStringRef(pathData); if (sys::fs::exists(path)) - driver->addFile(saver.save(path), /*withLOption=*/false); + driver->addFile(saver().save(path), /*withLOption=*/false); else setError("cannot find " + s + " inside " + config->sysroot); return; @@ -304,7 +304,7 @@ if (config->sysroot.empty()) driver->addFile(s.substr(1), /*withLOption=*/false); else - driver->addFile(saver.save(config->sysroot + "/" + s.substr(1)), + driver->addFile(saver().save(config->sysroot + "/" + s.substr(1)), /*withLOption=*/false); } else if (s.startswith("-l")) { // Case 3: search in the list of library paths. @@ -327,7 +327,7 @@ } else { // Finally, search in the list of library paths. if (Optional path = findFromSearchPaths(s)) - driver->addFile(saver.save(*path), /*withLOption=*/true); + driver->addFile(saver().save(*path), /*withLOption=*/true); else setError("unable to find " + s); } diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -22,9 +22,8 @@ #include "Symbols.h" #include "Target.h" #include "Writer.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/DWARF.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" #include "lld/Common/Strings.h" #include "lld/Common/Version.h" #include "llvm/ADT/SetOperations.h" @@ -73,7 +72,7 @@ // This is only for testing. StringRef s = getenv("LLD_VERSION"); if (s.empty()) - s = saver.save(Twine("Linker: ") + getLLDVersion()); + s = saver().save(Twine("Linker: ") + getLLDVersion()); // +1 to include the terminating '\0'. return {(const uint8_t *)s.data(), s.size() + 1}; @@ -255,7 +254,7 @@ InputSection *elf::createInterpSection() { // StringSaver guarantees that the returned string ends with '\0'. - StringRef s = saver.save(config->dynamicLinker); + StringRef s = saver().save(config->dynamicLinker); ArrayRef contents = {(const uint8_t *)s.data(), s.size() + 1}; return make(nullptr, SHF_ALLOC, SHT_PROGBITS, 1, contents, diff --git a/lld/ELF/Thunks.cpp b/lld/ELF/Thunks.cpp --- a/lld/ELF/Thunks.cpp +++ b/lld/ELF/Thunks.cpp @@ -27,8 +27,7 @@ #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Endian.h" @@ -434,7 +433,7 @@ } void AArch64ABSLongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__AArch64AbsLongThunk_" + destination.getName()), + addSymbol(saver().save("__AArch64AbsLongThunk_" + destination.getName()), STT_FUNC, 0, isec); addSymbol("$x", STT_NOTYPE, 0, isec); addSymbol("$d", STT_NOTYPE, 8, isec); @@ -460,8 +459,8 @@ } void AArch64ADRPThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__AArch64ADRPThunk_" + destination.getName()), STT_FUNC, - 0, isec); + addSymbol(saver().save("__AArch64ADRPThunk_" + destination.getName()), + STT_FUNC, 0, isec); addSymbol("$x", STT_NOTYPE, 0, isec); } @@ -560,7 +559,7 @@ } void ARMV7ABSLongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__ARMv7ABSLongThunk_" + destination.getName()), + addSymbol(saver().save("__ARMv7ABSLongThunk_" + destination.getName()), STT_FUNC, 0, isec); addSymbol("$a", STT_NOTYPE, 0, isec); } @@ -578,7 +577,7 @@ } void ThumbV7ABSLongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__Thumbv7ABSLongThunk_" + destination.getName()), + addSymbol(saver().save("__Thumbv7ABSLongThunk_" + destination.getName()), STT_FUNC, 1, isec); addSymbol("$t", STT_NOTYPE, 0, isec); } @@ -599,8 +598,8 @@ } void ARMV7PILongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__ARMV7PILongThunk_" + destination.getName()), STT_FUNC, - 0, isec); + addSymbol(saver().save("__ARMV7PILongThunk_" + destination.getName()), + STT_FUNC, 0, isec); addSymbol("$a", STT_NOTYPE, 0, isec); } @@ -620,7 +619,7 @@ } void ThumbV7PILongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__ThumbV7PILongThunk_" + destination.getName()), + addSymbol(saver().save("__ThumbV7PILongThunk_" + destination.getName()), STT_FUNC, 1, isec); addSymbol("$t", STT_NOTYPE, 0, isec); } @@ -635,7 +634,7 @@ } void ARMV5ABSLongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__ARMv5ABSLongThunk_" + destination.getName()), + addSymbol(saver().save("__ARMv5ABSLongThunk_" + destination.getName()), STT_FUNC, 0, isec); addSymbol("$a", STT_NOTYPE, 0, isec); addSymbol("$d", STT_NOTYPE, 4, isec); @@ -661,8 +660,8 @@ } void ARMV5PILongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__ARMV5PILongThunk_" + destination.getName()), STT_FUNC, - 0, isec); + addSymbol(saver().save("__ARMV5PILongThunk_" + destination.getName()), + STT_FUNC, 0, isec); addSymbol("$a", STT_NOTYPE, 0, isec); addSymbol("$d", STT_NOTYPE, 12, isec); } @@ -691,7 +690,7 @@ } void ThumbV6MABSLongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__Thumbv6MABSLongThunk_" + destination.getName()), + addSymbol(saver().save("__Thumbv6MABSLongThunk_" + destination.getName()), STT_FUNC, 1, isec); addSymbol("$t", STT_NOTYPE, 0, isec); addSymbol("$d", STT_NOTYPE, 8, isec); @@ -717,7 +716,7 @@ } void ThumbV6MPILongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__Thumbv6MPILongThunk_" + destination.getName()), + addSymbol(saver().save("__Thumbv6MPILongThunk_" + destination.getName()), STT_FUNC, 1, isec); addSymbol("$t", STT_NOTYPE, 0, isec); addSymbol("$d", STT_NOTYPE, 12, isec); @@ -735,7 +734,7 @@ } void MipsThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__LA25Thunk_" + destination.getName()), STT_FUNC, 0, + addSymbol(saver().save("__LA25Thunk_" + destination.getName()), STT_FUNC, 0, isec); } @@ -758,8 +757,9 @@ } void MicroMipsThunk::addSymbols(ThunkSection &isec) { - Defined *d = addSymbol( - saver.save("__microLA25Thunk_" + destination.getName()), STT_FUNC, 0, isec); + Defined *d = + addSymbol(saver().save("__microLA25Thunk_" + destination.getName()), + STT_FUNC, 0, isec); d->stOther |= STO_MIPS_MICROMIPS; } @@ -782,8 +782,9 @@ } void MicroMipsR6Thunk::addSymbols(ThunkSection &isec) { - Defined *d = addSymbol( - saver.save("__microLA25Thunk_" + destination.getName()), STT_FUNC, 0, isec); + Defined *d = + addSymbol(saver().save("__microLA25Thunk_" + destination.getName()), + STT_FUNC, 0, isec); d->stOther |= STO_MIPS_MICROMIPS; } @@ -843,7 +844,7 @@ else os << ".plt_pic32."; os << destination.getName(); - addSymbol(saver.save(os.str()), STT_FUNC, 0, isec); + addSymbol(saver().save(os.str()), STT_FUNC, 0, isec); } bool PPC32PltCallStub::isCompatibleWith(const InputSection &isec, @@ -852,7 +853,7 @@ } void PPC32LongThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__LongThunk_" + destination.getName()), STT_FUNC, 0, + addSymbol(saver().save("__LongThunk_" + destination.getName()), STT_FUNC, 0, isec); } @@ -896,8 +897,8 @@ } void PPC64PltCallStub::addSymbols(ThunkSection &isec) { - Defined *s = addSymbol(saver.save("__plt_" + destination.getName()), STT_FUNC, - 0, isec); + Defined *s = addSymbol(saver().save("__plt_" + destination.getName()), + STT_FUNC, 0, isec); s->needsTocRestore = true; s->file = destination.file; } @@ -947,7 +948,7 @@ } void PPC64R2SaveStub::addSymbols(ThunkSection &isec) { - Defined *s = addSymbol(saver.save("__toc_save_" + destination.getName()), + Defined *s = addSymbol(saver().save("__toc_save_" + destination.getName()), STT_FUNC, 0, isec); s->needsTocRestore = true; } @@ -983,7 +984,7 @@ } void PPC64R12SetupStub::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__gep_setup_" + destination.getName()), STT_FUNC, 0, + addSymbol(saver().save("__gep_setup_" + destination.getName()), STT_FUNC, 0, isec); } @@ -1019,7 +1020,7 @@ } void PPC64PCRelPLTStub::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__plt_pcrel_" + destination.getName()), STT_FUNC, 0, + addSymbol(saver().save("__plt_pcrel_" + destination.getName()), STT_FUNC, 0, isec); } @@ -1035,7 +1036,7 @@ } void PPC64LongBranchThunk::addSymbols(ThunkSection &isec) { - addSymbol(saver.save("__long_branch_" + destination.getName()), STT_FUNC, 0, + addSymbol(saver().save("__long_branch_" + destination.getName()), STT_FUNC, 0, isec); } diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -20,8 +20,8 @@ #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/Arrays.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Filesystem.h" -#include "lld/Common/Memory.h" #include "lld/Common/Strings.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSwitch.h" @@ -2216,9 +2216,9 @@ StringRef s = sec->name; if (!isValidCIdentifier(s)) return; - addOptionalRegular(saver.save("__start_" + s), sec, 0, + addOptionalRegular(saver().save("__start_" + s), sec, 0, config->zStartStopVisibility); - addOptionalRegular(saver.save("__stop_" + s), sec, -1, + addOptionalRegular(saver().save("__stop_" + s), sec, -1, config->zStartStopVisibility); } diff --git a/lld/MachO/ConcatOutputSection.cpp b/lld/MachO/ConcatOutputSection.cpp --- a/lld/MachO/ConcatOutputSection.cpp +++ b/lld/MachO/ConcatOutputSection.cpp @@ -13,8 +13,7 @@ #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/TimeProfiler.h" @@ -322,8 +321,8 @@ // get written are happy. thunkInfo.isec->live = true; - StringRef thunkName = saver.save(funcSym->getName() + ".thunk." + - std::to_string(thunkInfo.sequence++)); + StringRef thunkName = saver().save(funcSym->getName() + ".thunk." + + std::to_string(thunkInfo.sequence++)); r.referent = thunkInfo.sym = symtab->addDefined( thunkName, /*file=*/nullptr, thunkInfo.isec, /*value=*/0, /*size=*/thunkSize, /*isWeakDef=*/false, /*isPrivateExtern=*/true, diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -128,7 +128,7 @@ // only append suffix if realpath() succeeds Twine suffixed = location + suffix; if (fs::exists(suffixed)) - return resolvedFrameworks[key] = saver.save(suffixed.str()); + return resolvedFrameworks[key] = saver().save(suffixed.str()); } // Suffix lookup failed, fall through to the no-suffix case. } @@ -165,7 +165,7 @@ path::append(buffer, path); // Do not warn about paths that are computed via the syslib roots if (fs::is_directory(buffer)) { - paths.push_back(saver.save(buffer.str())); + paths.push_back(saver().save(buffer.str())); found = true; } } @@ -183,7 +183,7 @@ SmallString<261> buffer(root); path::append(buffer, path); if (fs::is_directory(buffer)) - paths.push_back(saver.save(buffer.str())); + paths.push_back(saver().save(buffer.str())); } } return paths; @@ -1102,14 +1102,13 @@ symtab->addUndefined("dyld_stub_binder", /*file=*/nullptr, /*isWeak=*/false); } -bool macho::link(ArrayRef argsArr, bool canExitEarly, - raw_ostream &stdoutOS, raw_ostream &stderrOS) { - lld::stdoutOS = &stdoutOS; - lld::stderrOS = &stderrOS; - - errorHandler().cleanupCallback = []() { - freeArena(); +bool macho::link(ArrayRef argsArr, raw_ostream &stdoutOS, + raw_ostream &stderrOS, bool exitEarly, bool disableOutput) { + // This driver-specific context will be freed later by lldMain(). + auto *ctx = new CommonLinkerContext; + ctx->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput); + ctx->e.cleanupCallback = []() { resolvedFrameworks.clear(); resolvedLibraries.clear(); cachedReads.clear(); @@ -1130,17 +1129,15 @@ InputFile::resetIdCount(); }; - errorHandler().logName = args::getFilenameWithoutExe(argsArr[0]); - stderrOS.enable_colors(stderrOS.has_colors()); + ctx->e.logName = args::getFilenameWithoutExe(argsArr[0]); MachOOptTable parser; InputArgList args = parser.parse(argsArr.slice(1)); - errorHandler().errorLimitExceededMsg = - "too many errors emitted, stopping now " - "(use --error-limit=0 to see all errors)"; - errorHandler().errorLimit = args::getInteger(args, OPT_error_limit_eq, 20); - errorHandler().verbose = args.hasArg(OPT_verbose); + ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now " + "(use --error-limit=0 to see all errors)"; + ctx->e.errorLimit = args::getInteger(args, OPT_error_limit_eq, 20); + ctx->e.verbose = args.hasArg(OPT_verbose); if (args.hasArg(OPT_help_hidden)) { parser.printHelp(argsArr[0], /*showHidden=*/true); @@ -1184,7 +1181,7 @@ // these are meaningful for our text based stripping if (config->osoPrefix.equals(".") || config->osoPrefix.endswith(sep)) expanded += sep; - config->osoPrefix = saver.save(expanded.str()); + config->osoPrefix = saver().save(expanded.str()); } } @@ -1470,7 +1467,7 @@ // Parse LTO options. if (const Arg *arg = args.getLastArg(OPT_mcpu)) - parseClangOption(saver.save("-mcpu=" + StringRef(arg->getValue())), + parseClangOption(saver().save("-mcpu=" + StringRef(arg->getValue())), arg->getSpelling()); for (const Arg *arg : args.filtered(OPT_mllvm)) @@ -1561,11 +1558,5 @@ timeTraceProfilerCleanup(); } - - if (canExitEarly) - exitLld(errorCount() ? 1 : 0); - - bool ret = errorCount() == 0; - errorHandler().reset(); - return ret; + return errorCount() == 0; } diff --git a/lld/MachO/DriverUtils.cpp b/lld/MachO/DriverUtils.cpp --- a/lld/MachO/DriverUtils.cpp +++ b/lld/MachO/DriverUtils.cpp @@ -13,8 +13,7 @@ #include "Target.h" #include "lld/Common/Args.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Reproduce.h" #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseMap.h" @@ -82,7 +81,7 @@ // Expand response files (arguments in the form of @) // and then parse the argument again. - cl::ExpandResponseFiles(saver, cl::TokenizeGNUCommandLine, vec); + cl::ExpandResponseFiles(saver(), cl::TokenizeGNUCommandLine, vec); InputArgList args = ParseArgs(vec, missingIndex, missingCount); // Handle -fatal_warnings early since it converts missing argument warnings @@ -191,12 +190,12 @@ bool tbdExists = fs::exists(tbdPath); searchedDylib(tbdPath, tbdExists); if (tbdExists) - return saver.save(tbdPath.str()); + return saver().save(tbdPath.str()); bool dylibExists = fs::exists(dylibPath); searchedDylib(dylibPath, dylibExists); if (dylibExists) - return saver.save(dylibPath); + return saver().save(dylibPath); return {}; } @@ -261,7 +260,7 @@ bool exists = fs::exists(location); searchedDylib(location, exists); if (exists) - return saver.save(location.str()); + return saver().save(location.str()); } } return {}; diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -56,9 +56,8 @@ #include "SyntheticSections.h" #include "Target.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/DWARF.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" #include "lld/Common/Reproduce.h" #include "llvm/ADT/iterator.h" #include "llvm/BinaryFormat/MachO.h" @@ -210,6 +209,8 @@ return cachedReads[key] = mbref; } + llvm::BumpPtrAllocator &bAlloc = lld::bAlloc(); + // Object files and archive files may be fat files, which contain multiple // real files for different CPU ISAs. Here, we search for a file that matches // with the current link target and returns it as a MemoryBufferRef. @@ -241,7 +242,7 @@ } InputFile::InputFile(Kind kind, const InterfaceFile &interface) - : id(idCount++), fileKind(kind), name(saver.save(interface.getPath())) {} + : id(idCount++), fileKind(kind), name(saver().save(interface.getPath())) {} // Some sections comprise of fixed-size records, so instead of splitting them at // symbol boundaries, we split them based on size. Records are distinct from @@ -1177,7 +1178,7 @@ // Find all the $ld$* symbols to process first. parseTrie(buf + c->export_off, c->export_size, [&](const Twine &name, uint64_t flags) { - StringRef savedName = saver.save(name); + StringRef savedName = saver().save(name); if (handleLDSymbol(savedName)) return; entries.push_back({savedName, flags}); @@ -1251,7 +1252,7 @@ umbrella = this; this->umbrella = umbrella; - installName = saver.save(interface.getInstallName()); + installName = saver().save(interface.getInstallName()); compatibilityVersion = interface.getCompatibilityVersion().rawValue(); currentVersion = interface.getCurrentVersion().rawValue(); @@ -1270,7 +1271,7 @@ exportingFile = isImplicitlyLinked(installName) ? this : umbrella; auto addSymbol = [&](const Twine &name) -> void { - StringRef savedName = saver.save(name); + StringRef savedName = saver().save(name); if (exportingFile->hiddenSymbols.contains(CachedHashStringRef(savedName))) return; @@ -1389,7 +1390,7 @@ config->platformInfo.minimum >= end) return; - this->installName = saver.save(installName); + this->installName = saver().save(installName); if (!compatVersion.empty()) { VersionTuple cVersion; @@ -1411,7 +1412,7 @@ if (!condition.consume_front("os") || version.tryParse(condition)) warn("failed to parse os version, symbol '" + originalName + "' ignored"); else if (version == config->platformInfo.minimum) - this->installName = saver.save(installName); + this->installName = saver().save(installName); } void DylibFile::handleLDHideSymbol(StringRef name, StringRef originalName) { @@ -1516,7 +1517,7 @@ static macho::Symbol *createBitcodeSymbol(const lto::InputFile::Symbol &objSym, BitcodeFile &file) { - StringRef name = saver.save(objSym.getName()); + StringRef name = saver().save(objSym.getName()); if (objSym.isUndefined()) return symtab->addUndefined(name, &file, /*isWeakRef=*/objSym.isWeak()); @@ -1558,11 +1559,12 @@ // So, we append the archive name to disambiguate two members with the same // name from multiple different archives, and offset within the archive to // disambiguate two members of the same name from a single archive. - MemoryBufferRef mbref( - mb.getBuffer(), - saver.save(archiveName.empty() ? path - : archiveName + sys::path::filename(path) + - utostr(offsetInArchive))); + MemoryBufferRef mbref(mb.getBuffer(), + saver().save(archiveName.empty() + ? path + : archiveName + + sys::path::filename(path) + + utostr(offsetInArchive))); obj = check(lto::InputFile::create(mbref)); diff --git a/lld/MachO/LTO.cpp b/lld/MachO/LTO.cpp --- a/lld/MachO/LTO.cpp +++ b/lld/MachO/LTO.cpp @@ -14,7 +14,7 @@ #include "Target.h" #include "lld/Common/Args.h" -#include "lld/Common/ErrorHandler.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Strings.h" #include "lld/Common/TargetOptionsCommandFlags.h" #include "llvm/LTO/Config.h" @@ -148,7 +148,7 @@ modTime = getModTime(filePath); } ret.push_back(make( - MemoryBufferRef(buf[i], saver.save(filePath.str())), modTime, "")); + MemoryBufferRef(buf[i], saver().save(filePath.str())), modTime, "")); } for (std::unique_ptr &file : files) if (file) diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp --- a/lld/MachO/SyntheticSections.cpp +++ b/lld/MachO/SyntheticSections.cpp @@ -16,8 +16,7 @@ #include "SymbolTable.h" #include "Symbols.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Config/llvm-config.h" #include "llvm/Support/EndianStream.h" @@ -834,7 +833,7 @@ if (!dir.endswith(sep)) dir += sep; stab.strx = stringTableSection.addString( - saver.save(dir + compileUnit->getUnitDIE().getShortName())); + saver().save(dir + compileUnit->getUnitDIE().getShortName())); stabs.emplace_back(std::move(stab)); } @@ -856,7 +855,7 @@ if (!file->archiveName.empty()) path.append({"(", file->getName(), ")"}); - StringRef adjustedPath = saver.save(path.str()); + StringRef adjustedPath = saver().save(path.str()); adjustedPath.consume_front(config->osoPrefix); stab.strx = stringTableSection.addString(adjustedPath); diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -22,8 +22,7 @@ #include "UnwindInfoSection.h" #include "lld/Common/Arrays.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/Config/llvm-config.h" #include "llvm/Support/LEB128.h" @@ -611,7 +610,7 @@ } static void prepareSymbolRelocation(Symbol *sym, const InputSection *isec, - const Reloc &r) { + const lld::macho::Reloc &r) { assert(sym->isLive()); const RelocAttrs &relocAttrs = target->getRelocAttrs(r.type); @@ -644,7 +643,7 @@ continue; for (auto it = isec->relocs.begin(); it != isec->relocs.end(); ++it) { - Reloc &r = *it; + lld::macho::Reloc &r = *it; if (target->hasAttr(r.type, RelocAttrBits::SUBTRAHEND)) { // Skip over the following UNSIGNED relocation -- it's just there as the // minuend, and doesn't have the usual UNSIGNED semantics. We don't want @@ -858,8 +857,8 @@ if (f->archiveName.empty()) filename = path::filename(f->getName()); else - filename = saver.save(path::filename(f->archiveName) + "(" + - path::filename(f->getName()) + ")"); + filename = saver().save(path::filename(f->archiveName) + "(" + + path::filename(f->getName()) + ")"); return std::max(entry.objectFiles.lookup(filename), entry.anyObjectFile); } @@ -1216,7 +1215,7 @@ // This section contains space for just a single word, and will be used by // dyld to cache an address to the image loader it uses. - uint8_t *arr = bAlloc.Allocate(target->wordSize); + uint8_t *arr = bAlloc().Allocate(target->wordSize); memset(arr, 0, target->wordSize); in.imageLoaderCache = make( segment_names::data, section_names::data, /*file=*/nullptr, diff --git a/lld/MinGW/Driver.cpp b/lld/MinGW/Driver.cpp --- a/lld/MinGW/Driver.cpp +++ b/lld/MinGW/Driver.cpp @@ -100,7 +100,7 @@ unsigned missingCount; SmallVector vec(argv.data(), argv.data() + argv.size()); - cl::ExpandResponseFiles(saver, getQuotingStyle(), vec); + cl::ExpandResponseFiles(saver(), getQuotingStyle(), vec); opt::InputArgList args = this->ParseArgs(vec, missingIndex, missingCount); if (missingCount) @@ -154,12 +154,10 @@ // Convert Unix-ish command line arguments to Windows-ish ones and // then call coff::link. -bool mingw::link(ArrayRef argsArr, bool canExitEarly, - raw_ostream &stdoutOS, raw_ostream &stderrOS) { - lld::stdoutOS = &stdoutOS; - lld::stderrOS = &stderrOS; - - stderrOS.enable_colors(stderrOS.has_colors()); +bool mingw::link(ArrayRef argsArr, raw_ostream &stdoutOS, + raw_ostream &stderrOS, bool exitEarly, bool disableOutput) { + auto *ctx = new CommonLinkerContext; + ctx->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput); MinGWOptTable parser; opt::InputArgList args = parser.parse(argsArr.slice(1)); @@ -445,5 +443,9 @@ // Pass the actual binary name, to make error messages be printed with // the right prefix. vec[0] = argsArr[0]; - return coff::link(vec, canExitEarly, stdoutOS, stderrOS); + + // The context will be re-created in the COFF driver. + lld::CommonLinkerContext::destroy(); + + return coff::link(vec, stdoutOS, stderrOS, exitEarly, disableOutput); } diff --git a/lld/include/lld/Common/CommonLinkerContext.h b/lld/include/lld/Common/CommonLinkerContext.h new file mode 100644 --- /dev/null +++ b/lld/include/lld/Common/CommonLinkerContext.h @@ -0,0 +1,63 @@ +//===- CommonLinkerContext.h ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Entry point for all global state in lldCommon. The objective is for LLD to be +// used "as a library" in a thread-safe manner. +// +// Instead of program-wide globals or function-local statics, we prefer +// aggregating all "global" states into a heap-based structure +// (CommonLinkerContext). This also achieves deterministic initialization & +// shutdown for all "global" states. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_COMMON_COMMONLINKINGCONTEXT_H +#define LLD_COMMON_COMMONLINKINGCONTEXT_H + +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.h" +#include "llvm/CodeGen/CommandFlags.h" +#include "llvm/Support/StringSaver.h" + +namespace llvm { +class raw_ostream; +} // namespace llvm + +namespace lld { +struct SpecificAllocBase; +class CommonLinkerContext { +public: + CommonLinkerContext(); + virtual ~CommonLinkerContext(); + + static void destroy(); + + llvm::BumpPtrAllocator bAlloc; + llvm::StringSaver saver{bAlloc}; + llvm::DenseMap instances; + + ErrorHandler e; + +private: + llvm::codegen::RegisterCodeGenFlags cgf; +}; + +// Retrieve the global state. Only one state can exist per application. +CommonLinkerContext &commonContext(); + +template T &context() { + return static_cast(commonContext()); +} + +bool hasContext(); + +inline llvm::StringSaver &saver() { return context().saver; } +inline llvm::BumpPtrAllocator &bAlloc() { return context().bAlloc; } +} // namespace lld + +#endif diff --git a/lld/include/lld/Common/Driver.h b/lld/include/lld/Common/Driver.h --- a/lld/include/lld/Common/Driver.h +++ b/lld/include/lld/Common/Driver.h @@ -9,6 +9,7 @@ #ifndef LLD_COMMON_DRIVER_H #define LLD_COMMON_DRIVER_H +#include "lld/Common/CommonLinkerContext.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/raw_ostream.h" @@ -28,28 +29,28 @@ llvm::raw_ostream &stderrOS); namespace coff { -bool link(llvm::ArrayRef args, bool canExitEarly, - llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); +bool link(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput); } namespace mingw { -bool link(llvm::ArrayRef args, bool canExitEarly, - llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); +bool link(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput); } namespace elf { -bool link(llvm::ArrayRef args, bool canExitEarly, - llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); +bool link(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput); } namespace macho { -bool link(llvm::ArrayRef args, bool canExitEarly, - llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); +bool link(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput); } namespace wasm { -bool link(llvm::ArrayRef args, bool canExitEarly, - llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); +bool link(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput); } } diff --git a/lld/include/lld/Common/ErrorHandler.h b/lld/include/lld/Common/ErrorHandler.h --- a/lld/include/lld/Common/ErrorHandler.h +++ b/lld/include/lld/Common/ErrorHandler.h @@ -73,6 +73,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileOutputBuffer.h" +#include namespace llvm { class DiagnosticInfo; @@ -81,11 +82,6 @@ namespace lld { -// We wrap stdout and stderr so that you can pass alternative stdout/stderr as -// arguments to lld::*::link() functions. -extern llvm::raw_ostream *stdoutOS; -extern llvm::raw_ostream *stderrOS; - llvm::raw_ostream &outs(); llvm::raw_ostream &errs(); @@ -93,6 +89,11 @@ class ErrorHandler { public: + ~ErrorHandler(); + + void initialize(llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, + bool exitEarly, bool disableOutput); + uint64_t errorCount = 0; uint64_t errorLimit = 20; StringRef errorLimitExceededMsg = "too many errors emitted, stopping now"; @@ -112,11 +113,9 @@ void message(const Twine &msg, llvm::raw_ostream &s); void warn(const Twine &msg); - void reset() { - if (cleanupCallback) - cleanupCallback(); - *this = ErrorHandler(); - } + raw_ostream &outs(); + raw_ostream &errs(); + void flushStreams(); std::unique_ptr outputBuffer; @@ -126,6 +125,19 @@ std::string getLocation(const Twine &msg); void reportDiagnostic(StringRef location, Colors c, StringRef diagKind, const Twine &msg); + + // We want to separate multi-line messages with a newline. `sep` is "\n" + // if the last messages was multi-line. Otherwise "". + llvm::StringRef sep; + + // We wrap stdout and stderr so that you can pass alternative stdout/stderr as + // arguments to lld::*::link() functions. Since lld::outs() or lld::errs() can + // be indirectly called from multiple threads, we protect them using a mutex. + // In the future, we plan on supporting several concurent linker contexts, + // which explains why the mutex is not a global but part of this context. + std::mutex mu; + llvm::raw_ostream *stdoutOS{}; + llvm::raw_ostream *stderrOS{}; }; /// Returns the default error handler. diff --git a/lld/include/lld/Common/Memory.h b/lld/include/lld/Common/Memory.h --- a/lld/include/lld/Common/Memory.h +++ b/lld/include/lld/Common/Memory.h @@ -22,42 +22,41 @@ #define LLD_COMMON_MEMORY_H #include "llvm/Support/Allocator.h" -#include "llvm/Support/StringSaver.h" -#include namespace lld { - -// Use this arena if your object doesn't have a destructor. -extern llvm::BumpPtrAllocator bAlloc; -extern llvm::StringSaver saver; - -void freeArena(); - -// These two classes are hack to keep track of all -// SpecificBumpPtrAllocator instances. +// A base class only used by the CommonLinkerContext to keep track of the +// SpecificAlloc<> instances. struct SpecificAllocBase { - SpecificAllocBase() { instances.push_back(this); } virtual ~SpecificAllocBase() = default; - virtual void reset() = 0; - static std::vector instances; + static SpecificAllocBase *getOrCreate(void *tag, size_t size, size_t align, + SpecificAllocBase *(&creator)(void *)); }; +// An arena of specific types T, created on-demand. template struct SpecificAlloc : public SpecificAllocBase { - void reset() override { alloc.DestroyAll(); } + static SpecificAllocBase *create(void *storage) { + return new (storage) SpecificAlloc(); + } llvm::SpecificBumpPtrAllocator alloc; + static int tag; }; -// Use a static local for these singletons so they are only registered if an -// object of this instance is ever constructed. Otherwise we will create and -// register ELF allocators for COFF and the reverse. +// The address of this static member is only used as a key in +// CommonLinkerContext::instances. Its value does not matter. +template int SpecificAlloc::tag = 0; + +// Creates the arena on-demand on the first call; or returns it, if it was +// already created. template inline llvm::SpecificBumpPtrAllocator &getSpecificAllocSingleton() { - static SpecificAlloc instance; - return instance.alloc; + SpecificAllocBase *instance = SpecificAllocBase::getOrCreate( + &SpecificAlloc::tag, sizeof(SpecificAlloc), + alignof(SpecificAlloc), SpecificAlloc::create); + return ((SpecificAlloc *)instance)->alloc; } -// Use this arena if your object has a destructor. -// Your destructor will be invoked from freeArena(). +// Creates new instances of T off a (almost) contiguous arena/object pool. The +// instances are destroyed whenever lldMain() goes out of scope. template T *make(U &&... args) { return new (getSpecificAllocSingleton().Allocate()) T(std::forward(args)...); diff --git a/lld/include/lld/Core/LinkingContext.h b/lld/include/lld/Core/LinkingContext.h --- a/lld/include/lld/Core/LinkingContext.h +++ b/lld/include/lld/Core/LinkingContext.h @@ -9,6 +9,7 @@ #ifndef LLD_CORE_LINKING_CONTEXT_H #define LLD_CORE_LINKING_CONTEXT_H +#include "lld/Common/CommonLinkerContext.h" #include "lld/Core/Node.h" #include "lld/Core/Reader.h" #include "llvm/ADT/ArrayRef.h" @@ -34,7 +35,7 @@ /// The base class LinkingContext contains the options needed by core linking. /// Subclasses of LinkingContext have additional options needed by specific /// Writers. -class LinkingContext { +class LinkingContext : public CommonLinkerContext { public: virtual ~LinkingContext(); diff --git a/lld/tools/lld/lld.cpp b/lld/tools/lld/lld.cpp --- a/lld/tools/lld/lld.cpp +++ b/lld/tools/lld/lld.cpp @@ -87,6 +87,8 @@ // Expand response files (arguments in the form of @) // to allow detecting the -m argument from arguments in them. SmallVector expandedArgs(v.data(), v.data() + v.size()); + BumpPtrAllocator a; + StringSaver saver(a); cl::ExpandResponseFiles(saver, getDefaultQuotingStyle(), expandedArgs); for (auto it = expandedArgs.begin(); it + 1 != expandedArgs.end(); ++it) { if (StringRef(*it) != "-m") @@ -134,27 +136,43 @@ return parseProgname(arg0); } +static bool inTestOutputDisabled = false; + /// Universal linker main(). This linker emulates the gnu, darwin, or /// windows linker based on the argv[0] or -flavor option. static int lldMain(int argc, const char **argv, llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, bool exitEarly = true) { std::vector args(argv, argv + argc); - switch (parseFlavor(args)) { - case Gnu: - if (isPETarget(args)) - return !mingw::link(args, exitEarly, stdoutOS, stderrOS); - return !elf::link(args, exitEarly, stdoutOS, stderrOS); - case WinLink: - return !coff::link(args, exitEarly, stdoutOS, stderrOS); - case Darwin: - return !macho::link(args, exitEarly, stdoutOS, stderrOS); - case Wasm: - return !lld::wasm::link(args, exitEarly, stdoutOS, stderrOS); - default: + auto link = [&args]() { + Flavor f = parseFlavor(args); + if (f == Gnu && isPETarget(args)) + return mingw::link; + if (f == Gnu) + return elf::link; + if (f == WinLink) + return coff::link; + if (f == Darwin) + return macho::link; + if (f == Wasm) + return lld::wasm::link; die("lld is a generic driver.\n" "Invoke ld.lld (Unix), ld64.lld (macOS), lld-link (Windows), wasm-ld" " (WebAssembly) instead"); - } + }(); + // Run the driver. If an error occurs, false will be returned. + int r = !link(args, stdoutOS, stderrOS, exitEarly, inTestOutputDisabled); + + // Exit immediately if we don't need to return to the caller. + // This saves time because the overhead of calling destructors + // for all globally-allocated objects is not negligible. + if (exitEarly) + exitLld(r); + + // Delete the global context and clear the global context pointer, so that it + // cannot be accessed anymore. + CommonLinkerContext::destroy(); + + return r; } // Similar to lldMain except that exceptions are caught. @@ -176,7 +194,7 @@ // Cleanup memory and reset everything back in pristine condition. This path // is only taken when LLD is in test, or when it is used as a library. llvm::CrashRecoveryContext crc; - if (!crc.RunSafely([&]() { errorHandler().reset(); })) { + if (!crc.RunSafely([&]() { CommonLinkerContext::destroy(); })) { // The memory is corrupted beyond any possible recovery. return {r, /*canRunAgain=*/false}; } @@ -207,8 +225,7 @@ for (unsigned i = inTestVerbosity(); i > 0; --i) { // Disable stdout/stderr for all iterations but the last one. - if (i != 1) - errorHandler().disableOutput = true; + inTestOutputDisabled = (i != 1); // Execute one iteration. auto r = safeLldMain(argc, argv, llvm::outs(), llvm::errs()); diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -81,18 +81,15 @@ }; } // anonymous namespace -bool link(ArrayRef args, bool canExitEarly, raw_ostream &stdoutOS, - raw_ostream &stderrOS) { - lld::stdoutOS = &stdoutOS; - lld::stderrOS = &stderrOS; +bool link(ArrayRef args, raw_ostream &stdoutOS, + raw_ostream &stderrOS, bool exitEarly, bool disableOutput) { + // This driver-specific context will be freed later by lldMain(). + auto *ctx = new CommonLinkerContext; - errorHandler().cleanupCallback = []() { freeArena(); }; - - errorHandler().logName = args::getFilenameWithoutExe(args[0]); - errorHandler().errorLimitExceededMsg = - "too many errors emitted, stopping now (use " - "-error-limit=0 to see all errors)"; - stderrOS.enable_colors(stderrOS.has_colors()); + ctx->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput); + ctx->e.logName = args::getFilenameWithoutExe(args[0]); + ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now (use " + "-error-limit=0 to see all errors)"; config = make(); symtab = make(); @@ -100,13 +97,7 @@ initLLVM(); LinkerDriver().linkerMain(args); - // Exit immediately if we don't need to return to the caller. - // This saves time because the overhead of calling destructors - // for all globally-allocated objects is not negligible. - if (canExitEarly) - exitLld(errorCount() ? 1 : 0); - - return !errorCount(); + return errorCount() == 0; } // Create prefix string literals used in Options.td @@ -189,7 +180,7 @@ // Expand response files (arguments in the form of @) // and then parse the argument again. - cl::ExpandResponseFiles(saver, getQuotingStyle(args), vec); + cl::ExpandResponseFiles(saver(), getQuotingStyle(args), vec); args = this->ParseArgs(vec, missingIndex, missingCount); handleColorDiagnostics(args); @@ -760,8 +751,8 @@ if (!sym) continue; - Symbol *real = addUndefined(saver.save("__real_" + name)); - Symbol *wrap = addUndefined(saver.save("__wrap_" + name)); + Symbol *real = addUndefined(saver().save("__real_" + name)); + Symbol *wrap = addUndefined(saver().save("__wrap_" + name)); v.push_back({sym, real, wrap}); // We want to tell LTO not to inline symbols to be overwritten diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -12,8 +12,7 @@ #include "InputElement.h" #include "OutputSegment.h" #include "SymbolTable.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Reproduce.h" #include "llvm/Object/Binary.h" #include "llvm/Object/Wasm.h" @@ -721,7 +720,7 @@ static Symbol *createBitcodeSymbol(const std::vector &keptComdats, const lto::InputFile::Symbol &objSym, BitcodeFile &f) { - StringRef name = saver.save(objSym.getName()); + StringRef name = saver().save(objSym.getName()); uint32_t flags = objSym.isWeak() ? WASM_SYMBOL_BINDING_WEAK : 0; flags |= mapVisibility(objSym.getVisibility()); @@ -756,9 +755,9 @@ // symbols later in the link stage). So we append file offset to make // filename unique. StringRef name = archiveName.empty() - ? saver.save(path) - : saver.save(archiveName + "(" + path::filename(path) + - " at " + utostr(offsetInArchive) + ")"); + ? saver().save(path) + : saver().save(archiveName + "(" + path::filename(path) + + " at " + utostr(offsetInArchive) + ")"); MemoryBufferRef mbref(mb.getBuffer(), name); obj = check(lto::InputFile::create(mbref)); diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp --- a/lld/wasm/SymbolTable.cpp +++ b/lld/wasm/SymbolTable.cpp @@ -11,8 +11,7 @@ #include "InputChunks.h" #include "InputElement.h" #include "WriterUtils.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "llvm/ADT/SetVector.h" #define DEBUG_TYPE "lld" @@ -843,7 +842,7 @@ void SymbolTable::replaceWithUndefined(Symbol *sym) { // Add a synthetic dummy for weak undefined functions. These dummies will // be GC'd if not used as the target of any "call" instructions. - StringRef debugName = saver.save("undefined_weak:" + toString(*sym)); + StringRef debugName = saver().save("undefined_weak:" + toString(*sym)); replaceWithUnreachable(sym, *sym->getSignature(), debugName); // Hide our dummy to prevent export. sym->setHidden(true); @@ -941,7 +940,8 @@ if (symbol != defined) { auto *f = cast(symbol); reportFunctionSignatureMismatch(symName, f, defined, false); - StringRef debugName = saver.save("signature_mismatch:" + toString(*f)); + StringRef debugName = + saver().save("signature_mismatch:" + toString(*f)); replaceWithUnreachable(f, *f->signature, debugName); } } diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -17,8 +17,7 @@ #include "SymbolTable.h" #include "SyntheticSections.h" #include "WriterUtils.h" -#include "lld/Common/ErrorHandler.h" -#include "lld/Common/Memory.h" +#include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Strings.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallSet.h" @@ -190,7 +189,7 @@ else if (sec->type == WASM_SEC_CODE) name = "reloc.CODE"; else if (sec->type == WASM_SEC_CUSTOM) - name = saver.save("reloc." + sec->name); + name = saver().save("reloc." + sec->name); else llvm_unreachable( "relocations only supported for code, data, or custom sections"); @@ -389,8 +388,8 @@ LLVM_DEBUG(dbgs() << "addStartStopSymbols: " << name << "\n"); uint64_t start = seg->startVA; uint64_t stop = start + seg->size; - symtab->addOptionalDataSymbol(saver.save("__start_" + name), start); - symtab->addOptionalDataSymbol(saver.save("__stop_" + name), stop); + symtab->addOptionalDataSymbol(saver().save("__start_" + name), start); + symtab->addOptionalDataSymbol(saver().save("__stop_" + name), stop); } void Writer::addSections() { @@ -958,7 +957,7 @@ writeUleb128(os, bodyContent.size(), "function size"); os << bodyContent; } - ArrayRef body = arrayRefFromStringRef(saver.save(functionBody)); + ArrayRef body = arrayRefFromStringRef(saver().save(functionBody)); cast(func->function)->setBody(body); }