diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2019-10-23 17:52:45 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2019-10-23 17:52:45 +0000 |
commit | d2bd9e70b16db88a7808ee2280b0a107afbfdd3b (patch) | |
tree | 12612d2c593445b297ac656911c9db7cf9065bdd /COFF | |
parent | f1e1c239e31b467e17f1648b1f524fc9ab5b431a (diff) |
Vendor import of stripped lld trunk r375505, the last commit before thevendor/lld/lld-trunk-r375505vendor/lld
upstream Subversion repository was made read-only, and the LLVM project
migrated to GitHub:
https://llvm.org/svn/llvm-project/lld/trunk@375505
Notes
Notes:
svn path=/vendor/lld/dist/; revision=353950
svn path=/vendor/lld/lld-r375505/; revision=353951; tag=vendor/lld/lld-trunk-r375505
Diffstat (limited to 'COFF')
-rw-r--r-- | COFF/CMakeLists.txt | 2 | ||||
-rw-r--r-- | COFF/Config.h | 5 | ||||
-rw-r--r-- | COFF/DLL.cpp | 4 | ||||
-rw-r--r-- | COFF/DebugTypes.cpp | 27 | ||||
-rw-r--r-- | COFF/Driver.cpp | 255 | ||||
-rw-r--r-- | COFF/Driver.h | 22 | ||||
-rw-r--r-- | COFF/DriverUtils.cpp | 59 | ||||
-rw-r--r-- | COFF/ICF.cpp | 8 | ||||
-rw-r--r-- | COFF/InputFiles.cpp | 156 | ||||
-rw-r--r-- | COFF/InputFiles.h | 59 | ||||
-rw-r--r-- | COFF/LTO.cpp | 17 | ||||
-rw-r--r-- | COFF/MapFile.cpp | 15 | ||||
-rw-r--r-- | COFF/MinGW.cpp | 14 | ||||
-rw-r--r-- | COFF/Options.td | 39 | ||||
-rw-r--r-- | COFF/PDB.cpp | 59 | ||||
-rw-r--r-- | COFF/PDB.h | 9 | ||||
-rw-r--r-- | COFF/SymbolTable.cpp | 366 | ||||
-rw-r--r-- | COFF/SymbolTable.h | 22 | ||||
-rw-r--r-- | COFF/Symbols.cpp | 43 | ||||
-rw-r--r-- | COFF/Symbols.h | 52 | ||||
-rw-r--r-- | COFF/Writer.cpp | 94 |
21 files changed, 950 insertions, 377 deletions
diff --git a/COFF/CMakeLists.txt b/COFF/CMakeLists.txt index c7ef7c47fea1..7c5e8b79b7fe 100644 --- a/COFF/CMakeLists.txt +++ b/COFF/CMakeLists.txt @@ -28,8 +28,10 @@ add_lld_library(lldCOFF BinaryFormat Core DebugInfoCodeView + DebugInfoDWARF DebugInfoMSF DebugInfoPDB + Demangle LibDriver LTO MC diff --git a/COFF/Config.h b/COFF/Config.h index 1b0e24042710..309e1fbf99e3 100644 --- a/COFF/Config.h +++ b/COFF/Config.h @@ -122,6 +122,7 @@ struct Configuration { bool dll = false; StringRef implib; std::vector<Export> exports; + bool hadExplicitExports; std::set<std::string> delayLoads; std::map<std::string, int> dllOrder; Symbol *delayLoadHelper = nullptr; @@ -189,6 +190,10 @@ struct Configuration { // Used for /thinlto-object-suffix-replace: std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace; + // Used for /lto-obj-path: + llvm::StringRef ltoObjPath; + + uint64_t align = 4096; uint64_t imageBase = -1; uint64_t fileAlign = 512; uint64_t stackReserve = 1024 * 1024; diff --git a/COFF/DLL.cpp b/COFF/DLL.cpp index 40d1f463aa3f..39d9fbab63d5 100644 --- a/COFF/DLL.cpp +++ b/COFF/DLL.cpp @@ -135,7 +135,7 @@ private: static std::vector<std::vector<DefinedImportData *>> binImports(const std::vector<DefinedImportData *> &imports) { // Group DLL-imported symbols by DLL name because that's how - // symbols are layed out in the import descriptor table. + // symbols are laid out in the import descriptor table. auto less = [](const std::string &a, const std::string &b) { return config->dllOrder[a] < config->dllOrder[b]; }; @@ -188,7 +188,7 @@ public: // Initial contents for delay-loaded functions. // This code calls __delayLoadHelper2 function to resolve a symbol -// and then overwrites its jump table slot with the result +// which then overwrites its jump table slot with the result // for subsequent function calls. static const uint8_t thunkX64[] = { 0x48, 0x8D, 0x05, 0, 0, 0, 0, // lea rax, [__imp_<FUNCNAME>] diff --git a/COFF/DebugTypes.cpp b/COFF/DebugTypes.cpp index 78c1c78b408d..6c7d70ee8dcb 100644 --- a/COFF/DebugTypes.cpp +++ b/COFF/DebugTypes.cpp @@ -17,11 +17,12 @@ #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/Support/Path.h" -using namespace lld; -using namespace lld::coff; using namespace llvm; using namespace llvm::codeview; +namespace lld { +namespace coff { + namespace { // The TypeServerSource class represents a PDB type server, a file referenced by // OBJ files compiled with MSVC /Zi. A single PDB can be shared by several OBJ @@ -96,27 +97,25 @@ TpiSource::TpiSource(TpiKind k, const ObjFile *f) : kind(k), file(f) { GC.push_back(std::unique_ptr<TpiSource>(this)); } -TpiSource *lld::coff::makeTpiSource(const ObjFile *f) { +TpiSource *makeTpiSource(const ObjFile *f) { return new TpiSource(TpiSource::Regular, f); } -TpiSource *lld::coff::makeUseTypeServerSource(const ObjFile *f, +TpiSource *makeUseTypeServerSource(const ObjFile *f, const TypeServer2Record *ts) { TypeServerSource::enqueue(f, *ts); return new UseTypeServerSource(f, ts); } -TpiSource *lld::coff::makePrecompSource(const ObjFile *f) { +TpiSource *makePrecompSource(const ObjFile *f) { return new PrecompSource(f); } -TpiSource *lld::coff::makeUsePrecompSource(const ObjFile *f, +TpiSource *makeUsePrecompSource(const ObjFile *f, const PrecompRecord *precomp) { return new UsePrecompSource(f, precomp); } -namespace lld { -namespace coff { template <> const PrecompRecord &retrieveDependencyInfo(const TpiSource *source) { assert(source->kind == TpiSource::UsingPCH); @@ -128,8 +127,6 @@ const TypeServer2Record &retrieveDependencyInfo(const TpiSource *source) { assert(source->kind == TpiSource::UsingPDB); return ((const UseTypeServerSource *)source)->typeServerDependency; } -} // namespace coff -} // namespace lld std::map<std::string, std::pair<std::string, TypeServerSource *>> TypeServerSource::instances; @@ -210,8 +207,7 @@ TypeServerSource::findFromFile(const ObjFile *dependentFile) { // FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is // moved here. -Expected<llvm::pdb::NativeSession *> -lld::coff::findTypeServerSource(const ObjFile *f) { +Expected<llvm::pdb::NativeSession *> findTypeServerSource(const ObjFile *f) { Expected<TypeServerSource *> ts = TypeServerSource::findFromFile(f); if (!ts) return ts.takeError(); @@ -231,7 +227,7 @@ void TypeServerSource::enqueue(const ObjFile *dependentFile, if (!it.second) return; // another OBJ already scheduled this PDB for load - driver->enqueuePath(*p, false); + driver->enqueuePath(*p, false, false); } // Create an instance of TypeServerSource or an error string if the PDB couldn't @@ -239,7 +235,7 @@ void TypeServerSource::enqueue(const ObjFile *dependentFile, // will be merged in. NOTE - a PDB load failure is not a link error: some // debug info will simply be missing from the final PDB - that is the default // accepted behavior. -void lld::coff::loadTypeServerSource(llvm::MemoryBufferRef m) { +void loadTypeServerSource(llvm::MemoryBufferRef m) { std::string path = normalizePdbPath(m.getBufferIdentifier()); Expected<TypeServerSource *> ts = TypeServerSource::getInstance(m); @@ -266,3 +262,6 @@ Expected<TypeServerSource *> TypeServerSource::getInstance(MemoryBufferRef m) { return info.takeError(); return new TypeServerSource(m, session.release()); } + +} // namespace coff +} // namespace lld diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp index d7af50b9318f..30967a39b4ca 100644 --- a/COFF/Driver.cpp +++ b/COFF/Driver.cpp @@ -27,6 +27,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/Magic.h" +#include "llvm/LTO/LTO.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/COFFImportFile.h" #include "llvm/Object/COFFModuleDefinition.h" @@ -36,6 +37,7 @@ #include "llvm/Option/Option.h" #include "llvm/Support/Debug.h" #include "llvm/Support/LEB128.h" +#include "llvm/Support/MathExtras.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/TarWriter.h" @@ -62,16 +64,16 @@ LinkerDriver *driver; bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &diag) { errorHandler().logName = args::getFilenameWithoutExe(args[0]); errorHandler().errorOS = &diag; - errorHandler().colorDiagnostics = diag.has_colors(); errorHandler().errorLimitExceededMsg = "too many errors emitted, stopping now" " (use /errorlimit:0 to see all errors)"; errorHandler().exitEarly = canExitEarly; - config = make<Configuration>(); + enableColors(diag.has_colors()); + config = make<Configuration>(); symtab = make<SymbolTable>(); - driver = make<LinkerDriver>(); + driver->link(args); // Call exit() if we can to avoid calling destructors. @@ -169,7 +171,7 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> mb) { } void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb, - bool wholeArchive) { + bool wholeArchive, bool lazy) { StringRef filename = mb->getBufferIdentifier(); MemoryBufferRef mbref = takeBuffer(std::move(mb)); @@ -184,19 +186,28 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb, if (wholeArchive) { std::unique_ptr<Archive> file = CHECK(Archive::create(mbref), filename + ": failed to parse archive"); + Archive *archive = file.get(); + make<std::unique_ptr<Archive>>(std::move(file)); // take ownership - for (MemoryBufferRef m : getArchiveMembers(file.get())) - addArchiveBuffer(m, "<whole-archive>", filename, 0); + int memberIndex = 0; + for (MemoryBufferRef m : getArchiveMembers(archive)) + addArchiveBuffer(m, "<whole-archive>", filename, memberIndex++); return; } symtab->addFile(make<ArchiveFile>(mbref)); break; case file_magic::bitcode: - symtab->addFile(make<BitcodeFile>(mbref, "", 0)); + if (lazy) + symtab->addFile(make<LazyObjFile>(mbref)); + else + symtab->addFile(make<BitcodeFile>(mbref, "", 0)); break; case file_magic::coff_object: case file_magic::coff_import_library: - symtab->addFile(make<ObjFile>(mbref)); + if (lazy) + symtab->addFile(make<LazyObjFile>(mbref)); + else + symtab->addFile(make<ObjFile>(mbref)); break; case file_magic::pdb: loadTypeServerSource(mbref); @@ -217,7 +228,7 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb, } } -void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) { +void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) { auto future = std::make_shared<std::future<MBErrPair>>(createFutureForFile(path)); std::string pathStr = path; @@ -237,7 +248,7 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) { else error(msg + "; did you mean '" + nearest + "'"); } else - driver->addBuffer(std::move(mbOrErr.first), wholeArchive); + driver->addBuffer(std::move(mbOrErr.first), wholeArchive, lazy); }); } @@ -268,13 +279,12 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName, } void LinkerDriver::enqueueArchiveMember(const Archive::Child &c, - StringRef symName, + const Archive::Symbol &sym, StringRef parentName) { - auto reportBufferError = [=](Error &&e, - StringRef childName) { + auto reportBufferError = [=](Error &&e, StringRef childName) { fatal("could not get the buffer for the member defining symbol " + - symName + ": " + parentName + "(" + childName + "): " + + toCOFFString(sym) + ": " + parentName + "(" + childName + "): " + toString(std::move(e))); }; @@ -285,7 +295,8 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c, reportBufferError(mbOrErr.takeError(), check(c.getFullName())); MemoryBufferRef mb = mbOrErr.get(); enqueueTask([=]() { - driver->addArchiveBuffer(mb, symName, parentName, offsetInArchive); + driver->addArchiveBuffer(mb, toCOFFString(sym), parentName, + offsetInArchive); }); return; } @@ -293,15 +304,17 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c, std::string childName = CHECK( c.getFullName(), "could not get the filename for the member defining symbol " + - symName); + toCOFFString(sym)); auto future = std::make_shared<std::future<MBErrPair>>( createFutureForFile(childName)); enqueueTask([=]() { auto mbOrErr = future->get(); if (mbOrErr.second) reportBufferError(errorCodeToError(mbOrErr.second), childName); - driver->addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)), symName, - parentName, /* OffsetInArchive */ 0); + // Pass empty string as archive name so that the original filename is + // used as the buffer identifier. + driver->addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)), + toCOFFString(sym), "", /*OffsetInArchive=*/0); }); } @@ -355,7 +368,7 @@ void LinkerDriver::parseDirectives(InputFile *file) { break; case OPT_defaultlib: if (Optional<StringRef> path = findLib(arg->getValue())) - enqueuePath(*path, false); + enqueuePath(*path, false, false); break; case OPT_entry: config->entry = addUndefined(mangle(arg->getValue())); @@ -590,6 +603,7 @@ static std::string createResponseFile(const opt::InputArgList &args, for (auto *arg : args) { switch (arg->getOption().getID()) { case OPT_linkrepro: + case OPT_reproduce: case OPT_INPUT: case OPT_defaultlib: case OPT_libpath: @@ -704,8 +718,7 @@ static std::string getImplibPath() { return out.str(); } -// -// The import name is caculated as the following: +// The import name is calculated as follows: // // | LIBRARY w/ ext | LIBRARY w/o ext | no LIBRARY // -----+----------------+---------------------+------------------ @@ -987,30 +1000,37 @@ static void parsePDBAltPath(StringRef altPath) { config->pdbAltPath = buf; } -/// Check that at most one resource obj file was used. +/// Convert resource files and potentially merge input resource object +/// trees into one resource tree. /// Call after ObjFile::Instances is complete. -static void diagnoseMultipleResourceObjFiles() { - // The .rsrc$01 section in a resource obj file contains a tree description - // of resources. Merging multiple resource obj files would require merging - // the trees instead of using usual linker section merging semantics. - // Since link.exe disallows linking more than one resource obj file with - // LNK4078, mirror that. The normal use of resource files is to give the - // linker many .res files, which are then converted to a single resource obj - // file internally, so this is not a big restriction in practice. - ObjFile *resourceObjFile = nullptr; - for (ObjFile *f : ObjFile::instances) { - if (!f->isResourceObjFile) - continue; +void LinkerDriver::convertResources() { + std::vector<ObjFile *> resourceObjFiles; - if (!resourceObjFile) { - resourceObjFile = f; - continue; - } + for (ObjFile *f : ObjFile::instances) { + if (f->isResourceObjFile()) + resourceObjFiles.push_back(f); + } - error(toString(f) + + if (!config->mingw && + (resourceObjFiles.size() > 1 || + (resourceObjFiles.size() == 1 && !resources.empty()))) { + error((!resources.empty() ? "internal .obj file created from .res files" + : toString(resourceObjFiles[1])) + ": more than one resource obj file not allowed, already got " + - toString(resourceObjFile)); + toString(resourceObjFiles.front())); + return; + } + + if (resources.empty() && resourceObjFiles.size() <= 1) { + // No resources to convert, and max one resource object file in + // the input. Keep that preconverted resource section as is. + for (ObjFile *f : resourceObjFiles) + f->includeResourceChunks(); + return; } + ObjFile *f = make<ObjFile>(convertResToCOFF(resources, resourceObjFiles)); + symtab->addFile(f); + f->includeResourceChunks(); } // In MinGW, if no symbols are chosen to be exported, then all symbols are @@ -1051,6 +1071,26 @@ void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) { }); } +// lld has a feature to create a tar file containing all input files as well as +// all command line options, so that other people can run lld again with exactly +// the same inputs. This feature is accessible via /linkrepro and /reproduce. +// +// /linkrepro and /reproduce are very similar, but /linkrepro takes a directory +// name while /reproduce takes a full path. We have /linkrepro for compatibility +// with Microsoft link.exe. +Optional<std::string> getReproduceFile(const opt::InputArgList &args) { + if (auto *arg = args.getLastArg(OPT_reproduce)) + return std::string(arg->getValue()); + + if (auto *arg = args.getLastArg(OPT_linkrepro)) { + SmallString<64> path = StringRef(arg->getValue()); + sys::path::append(path, "repro.tar"); + return path.str().str(); + } + + return None; +} + void LinkerDriver::link(ArrayRef<const char *> argsArr) { // Needed for LTO. InitializeAllTargetInfos(); @@ -1069,7 +1109,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) { // Parse command line options. ArgParser parser; - opt::InputArgList args = parser.parseLINK(argsArr); + opt::InputArgList args = parser.parse(argsArr); // Parse and evaluate -mllvm options. std::vector<const char *> v; @@ -1113,17 +1153,15 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) { // options are handled. config->mingw = args.hasArg(OPT_lldmingw); - if (auto *arg = args.getLastArg(OPT_linkrepro)) { - SmallString<64> path = StringRef(arg->getValue()); - sys::path::append(path, "repro.tar"); - + // Handle /linkrepro and /reproduce. + if (Optional<std::string> path = getReproduceFile(args)) { Expected<std::unique_ptr<TarWriter>> errOrWriter = - TarWriter::create(path, "repro"); + TarWriter::create(*path, sys::path::stem(*path)); if (errOrWriter) { tar = std::move(*errOrWriter); } else { - error("/linkrepro: failed to open " + path + ": " + + error("/linkrepro: failed to open " + *path + ": " + toString(errOrWriter.takeError())); } } @@ -1139,7 +1177,8 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) { searchPaths.push_back(""); for (auto *arg : args.filtered(OPT_libpath)) searchPaths.push_back(arg->getValue()); - addLibSearchPaths(); + if (!args.hasArg(OPT_lldignoreenv)) + addLibSearchPaths(); // Handle /ignore for (auto *arg : args.filtered(OPT_ignore)) { @@ -1419,6 +1458,13 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) { for (auto *arg : args.filtered(OPT_section)) parseSection(arg->getValue()); + // Handle /align + if (auto *arg = args.getLastArg(OPT_align)) { + parseNumbers(arg->getValue(), &config->align); + if (!isPowerOf2_64(config->align)) + error("/align: not a power of two: " + StringRef(arg->getValue())); + } + // Handle /aligncomm for (auto *arg : args.filtered(OPT_aligncomm)) parseAligncomm(arg->getValue()); @@ -1464,6 +1510,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) { getOldNewOptions(args, OPT_thinlto_prefix_replace); config->thinLTOObjectSuffixReplace = getOldNewOptions(args, OPT_thinlto_object_suffix_replace); + config->ltoObjPath = args.getLastArgValue(OPT_lto_obj_path); // Handle miscellaneous boolean flags. config->allowBind = args.hasFlag(OPT_allowbind, OPT_allowbind_no, true); config->allowIsolation = @@ -1528,19 +1575,45 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) { return false; }; - // Create a list of input files. Files can be given as arguments - // for /defaultlib option. - for (auto *arg : args.filtered(OPT_INPUT, OPT_wholearchive_file)) - if (Optional<StringRef> path = findFile(arg->getValue())) - enqueuePath(*path, isWholeArchive(*path)); + // Create a list of input files. These can be given as OPT_INPUT options + // and OPT_wholearchive_file options, and we also need to track OPT_start_lib + // and OPT_end_lib. + bool inLib = false; + for (auto *arg : args) { + switch (arg->getOption().getID()) { + case OPT_end_lib: + if (!inLib) + error("stray " + arg->getSpelling()); + inLib = false; + break; + case OPT_start_lib: + if (inLib) + error("nested " + arg->getSpelling()); + inLib = true; + break; + case OPT_wholearchive_file: + if (Optional<StringRef> path = findFile(arg->getValue())) + enqueuePath(*path, true, inLib); + break; + case OPT_INPUT: + if (Optional<StringRef> path = findFile(arg->getValue())) + enqueuePath(*path, isWholeArchive(*path), inLib); + break; + default: + // Ignore other options. + break; + } + } + // Process files specified as /defaultlib. These should be enequeued after + // other files, which is why they are in a separate loop. for (auto *arg : args.filtered(OPT_defaultlib)) if (Optional<StringRef> path = findLib(arg->getValue())) - enqueuePath(*path, false); + enqueuePath(*path, false, false); // Windows specific -- Create a resource file containing a manifest file. if (config->manifest == Configuration::Embed) - addBuffer(createManifestRes(), false); + addBuffer(createManifestRes(), false, false); // Read all input files given via the command line. run(); @@ -1565,12 +1638,6 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) { for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt)) parseFunctionPadMin(arg, config->machine); - // Input files can be Windows resource files (.res files). We use - // WindowsResource to convert resource files to a regular COFF file, - // then link the resulting file normally. - if (!resources.empty()) - symtab->addFile(make<ObjFile>(convertResToCOFF(resources))); - if (tar) tar->append("response.txt", createResponseFile(args, filePaths, @@ -1746,33 +1813,24 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) { u->weakAlias = symtab->addUndefined(to); } + // If any inputs are bitcode files, the LTO code generator may create + // references to library functions that are not explicit in the bitcode + // file's symbol table. If any of those library functions are defined in a + // bitcode file in an archive member, we need to arrange to use LTO to + // compile those archive members by adding them to the link beforehand. + if (!BitcodeFile::instances.empty()) + for (auto *s : lto::LTO::getRuntimeLibcallSymbols()) + symtab->addLibcall(s); + // Windows specific -- if __load_config_used can be resolved, resolve it. if (symtab->findUnderscore("_load_config_used")) addUndefined(mangle("_load_config_used")); } while (run()); - if (errorCount()) - return; - - // Do LTO by compiling bitcode input files to a set of native COFF files then - // link those files (unless -thinlto-index-only was given, in which case we - // resolve symbols and write indices, but don't generate native code or link). - symtab->addCombinedLTOObjects(); - - // If -thinlto-index-only is given, we should create only "index - // files" and not object files. Index file creation is already done - // in addCombinedLTOObject, so we are done if that's the case. - if (config->thinLTOIndexOnly) - return; - - // If we generated native object files from bitcode files, this resolves - // references to the symbols we use from them. - run(); - if (args.hasArg(OPT_include_optional)) { // Handle /includeoptional for (auto *arg : args.filtered(OPT_include_optional)) - if (dyn_cast_or_null<Lazy>(symtab->find(arg->getValue()))) + if (dyn_cast_or_null<LazyArchive>(symtab->find(arg->getValue()))) addUndefined(arg->getValue()); while (run()); } @@ -1795,11 +1853,36 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) { run(); } - // Make sure we have resolved all symbols. - symtab->reportRemainingUndefines(); + // At this point, we should not have any symbols that cannot be resolved. + // If we are going to do codegen for link-time optimization, check for + // unresolvable symbols first, so we don't spend time generating code that + // will fail to link anyway. + if (!BitcodeFile::instances.empty() && !config->forceUnresolved) + symtab->reportUnresolvable(); + if (errorCount()) + return; + + // Do LTO by compiling bitcode input files to a set of native COFF files then + // link those files (unless -thinlto-index-only was given, in which case we + // resolve symbols and write indices, but don't generate native code or link). + symtab->addCombinedLTOObjects(); + + // If -thinlto-index-only is given, we should create only "index + // files" and not object files. Index file creation is already done + // in addCombinedLTOObject, so we are done if that's the case. + if (config->thinLTOIndexOnly) + return; + + // If we generated native object files from bitcode files, this resolves + // references to the symbols we use from them. + run(); + + // Resolve remaining undefined symbols and warn about imported locals. + symtab->resolveRemainingUndefines(); if (errorCount()) return; + config->hadExplicitExports = !config->exports.empty(); if (config->mingw) { // In MinGW, all symbols are automatically exported if no symbols // are chosen to be exported. @@ -1823,10 +1906,12 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) { } // Windows specific -- when we are creating a .dll file, we also - // need to create a .lib file. + // need to create a .lib file. In MinGW mode, we only do that when the + // -implib option is given explicitly, for compatibility with GNU ld. if (!config->exports.empty() || config->dll) { fixupExports(); - createImportLibrary(/*asLib=*/false); + if (!config->mingw || !config->implib.empty()) + createImportLibrary(/*asLib=*/false); assignExportOrdinals(); } @@ -1870,7 +1955,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) { markLive(symtab->getChunks()); // Needs to happen after the last call to addFile(). - diagnoseMultipleResourceObjFiles(); + convertResources(); // Identify identical COMDAT sections to merge them. if (config->doICF) { diff --git a/COFF/Driver.h b/COFF/Driver.h index 6100c3ca0c9e..cc2f25a6f95e 100644 --- a/COFF/Driver.h +++ b/COFF/Driver.h @@ -43,8 +43,8 @@ public: class ArgParser { public: - // Concatenate LINK environment variable and given arguments and parse them. - llvm::opt::InputArgList parseLINK(std::vector<const char *> args); + // Parses command line options. + llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> args); // Tokenizes a given string and then parses as command line options. llvm::opt::InputArgList parse(StringRef s) { return parse(tokenize(s)); } @@ -56,8 +56,8 @@ public: parseDirectives(StringRef s); private: - // Parses command line options. - llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> args); + // Concatenate LINK environment variable. + void addLINK(SmallVector<const char *, 256> &argv); std::vector<const char *> tokenize(StringRef s); @@ -72,12 +72,12 @@ public: void parseDirectives(InputFile *file); // Used by ArchiveFile to enqueue members. - void enqueueArchiveMember(const Archive::Child &c, StringRef symName, + void enqueueArchiveMember(const Archive::Child &c, const Archive::Symbol &sym, StringRef parentName); MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb); - void enqueuePath(StringRef path, bool wholeArchive); + void enqueuePath(StringRef path, bool wholeArchive, bool lazy); private: std::unique_ptr<llvm::TarWriter> tar; // for /linkrepro @@ -98,6 +98,10 @@ private: // Library search path. The first element is always "" (current directory). std::vector<StringRef> searchPaths; + // Convert resource files and potentially merge input resource object + // trees into one resource tree. + void convertResources(); + void maybeExportMinGWSymbols(const llvm::opt::InputArgList &args); // We don't want to add the same file more than once. @@ -120,7 +124,8 @@ private: StringRef findDefaultEntry(); WindowsSubsystem inferSubsystem(); - void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive); + void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive, + bool lazy); void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName, StringRef parentName, uint64_t offsetInArchive); @@ -184,7 +189,8 @@ void assignExportOrdinals(); void checkFailIfMismatch(StringRef arg, InputFile *source); // Convert Windows resource files (.res files) to a .obj file. -MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs); +MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs, + ArrayRef<ObjFile *> objs); void runMSVCLinker(std::string rsp, ArrayRef<StringRef> objects); diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp index 4360ac23b262..e08b855740a4 100644 --- a/COFF/DriverUtils.cpp +++ b/COFF/DriverUtils.cpp @@ -322,7 +322,7 @@ public: if (!contents.empty()) { std::error_code ec; - raw_fd_ostream os(path, ec, sys::fs::F_None); + raw_fd_ostream os(path, ec, sys::fs::OF_None); if (ec) fatal("failed to open " + path + ": " + ec.message()); os << contents; @@ -410,7 +410,7 @@ static std::string createManifestXmlWithExternalMt(StringRef defaultXml) { // Create the default manifest file as a temporary file. TemporaryFile Default("defaultxml", "manifest"); std::error_code ec; - raw_fd_ostream os(Default.path, ec, sys::fs::F_Text); + raw_fd_ostream os(Default.path, ec, sys::fs::OF_Text); if (ec) fatal("failed to open " + Default.path + ": " + ec.message()); os << defaultXml; @@ -511,7 +511,7 @@ void createSideBySideManifest() { if (path == "") path = config->outputFile + ".manifest"; std::error_code ec; - raw_fd_ostream out(path, ec, sys::fs::F_Text); + raw_fd_ostream out(path, ec, sys::fs::OF_Text); if (ec) fatal("failed to create manifest: " + ec.message()); out << createManifestXml(); @@ -700,26 +700,42 @@ void checkFailIfMismatch(StringRef arg, InputFile *source) { // Convert Windows resource files (.res files) to a .obj file. // Does what cvtres.exe does, but in-process and cross-platform. -MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs) { - object::WindowsResourceParser parser; +MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs, + ArrayRef<ObjFile *> objs) { + object::WindowsResourceParser parser(/* MinGW */ config->mingw); + std::vector<std::string> duplicates; for (MemoryBufferRef mb : mbs) { std::unique_ptr<object::Binary> bin = check(object::createBinary(mb)); object::WindowsResource *rf = dyn_cast<object::WindowsResource>(bin.get()); if (!rf) fatal("cannot compile non-resource file as resource"); - std::vector<std::string> duplicates; if (auto ec = parser.parse(rf, duplicates)) fatal(toString(std::move(ec))); + } + + // Note: This processes all .res files before all objs. Ideally they'd be + // handled in the same order they were linked (to keep the right one, if + // there are duplicates that are tolerated due to forceMultipleRes). + for (ObjFile *f : objs) { + object::ResourceSectionRef rsf; + if (auto ec = rsf.load(f->getCOFFObj())) + fatal(toString(f) + ": " + toString(std::move(ec))); - for (const auto &dupeDiag : duplicates) - if (config->forceMultipleRes) - warn(dupeDiag); - else - error(dupeDiag); + if (auto ec = parser.parse(rsf, f->getName(), duplicates)) + fatal(toString(std::move(ec))); } + if (config->mingw) + parser.cleanUpManifests(duplicates); + + for (const auto &dupeDiag : duplicates) + if (config->forceMultipleRes) + warn(dupeDiag); + else + error(dupeDiag); + Expected<std::unique_ptr<MemoryBuffer>> e = llvm::object::writeWindowsResourceCOFF(config->machine, parser, config->timestamp); @@ -757,15 +773,15 @@ static void handleColorDiagnostics(opt::InputArgList &args) { if (!arg) return; if (arg->getOption().getID() == OPT_color_diagnostics) { - errorHandler().colorDiagnostics = true; + enableColors(true); } else if (arg->getOption().getID() == OPT_no_color_diagnostics) { - errorHandler().colorDiagnostics = false; + enableColors(false); } else { StringRef s = arg->getValue(); if (s == "always") - errorHandler().colorDiagnostics = true; + enableColors(true); else if (s == "never") - errorHandler().colorDiagnostics = false; + enableColors(false); else if (s != "auto") error("unknown option: --color-diagnostics=" + s); } @@ -792,13 +808,17 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) { // We need to get the quoting style for response files before parsing all // options so we parse here before and ignore all the options but - // --rsp-quoting. + // --rsp-quoting and /lldignoreenv. + // (This means --rsp-quoting can't be added through %LINK%.) opt::InputArgList args = table.ParseArgs(argv, missingIndex, missingCount); - // Expand response files (arguments in the form of @<filename>) - // and then parse the argument again. + + // Expand response files (arguments in the form of @<filename>) and insert + // flags from %LINK% and %_LINK_%, and then parse the argument again. SmallVector<const char *, 256> expandedArgv(argv.data(), argv.data() + argv.size()); + if (!args.hasArg(OPT_lldignoreenv)) + addLINK(expandedArgv); cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv); args = table.ParseArgs(makeArrayRef(expandedArgv).drop_front(), missingIndex, missingCount); @@ -868,7 +888,7 @@ ArgParser::parseDirectives(StringRef s) { // link.exe has an interesting feature. If LINK or _LINK_ environment // variables exist, their contents are handled as command line strings. // So you can pass extra arguments using them. -opt::InputArgList ArgParser::parseLINK(std::vector<const char *> argv) { +void ArgParser::addLINK(SmallVector<const char *, 256> &argv) { // Concatenate LINK env and command line arguments, and then parse them. if (Optional<std::string> s = Process::GetEnv("LINK")) { std::vector<const char *> v = tokenize(*s); @@ -878,7 +898,6 @@ opt::InputArgList ArgParser::parseLINK(std::vector<const char *> argv) { std::vector<const char *> v = tokenize(*s); argv.insert(std::next(argv.begin()), v.begin(), v.end()); } - return parse(argv); } std::vector<const char *> ArgParser::tokenize(StringRef s) { diff --git a/COFF/ICF.cpp b/COFF/ICF.cpp index 2b2818de3889..c821569e3457 100644 --- a/COFF/ICF.cpp +++ b/COFF/ICF.cpp @@ -13,7 +13,7 @@ // // On Windows, ICF is enabled by default. // -// See ELF/ICF.cpp for the details about the algortihm. +// See ELF/ICF.cpp for the details about the algorithm. // //===----------------------------------------------------------------------===// @@ -77,7 +77,7 @@ private: // section is insignificant to the user program and the behaviour matches that // of the Visual C++ linker. bool ICF::isEligible(SectionChunk *c) { - // Non-comdat chunks, dead chunks, and writable chunks are not elegible. + // Non-comdat chunks, dead chunks, and writable chunks are not eligible. bool writable = c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE; if (!c->isCOMDAT() || !c->live || writable) return false; @@ -274,7 +274,7 @@ void ICF::run(ArrayRef<Chunk *> vec) { for (Symbol *b : sc->symbols()) if (auto *sym = dyn_cast_or_null<DefinedRegular>(b)) hash += sym->getChunk()->eqClass[cnt % 2]; - // Set MSB to 1 to avoid collisions with non-hash classs. + // Set MSB to 1 to avoid collisions with non-hash classes. sc->eqClass[(cnt + 1) % 2] = hash | (1U << 31); }); } @@ -297,7 +297,7 @@ void ICF::run(ArrayRef<Chunk *> vec) { log("ICF needed " + Twine(cnt) + " iterations"); - // Merge sections in the same classs. + // Merge sections in the same classes. forEachClass([&](size_t begin, size_t end) { if (end - begin == 1) return; diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp index c00d5c5b494e..faec3ba160a5 100644 --- a/COFF/InputFiles.cpp +++ b/COFF/InputFiles.cpp @@ -47,6 +47,24 @@ using llvm::Triple; using llvm::support::ulittle32_t; namespace lld { + +// Returns the last element of a path, which is supposed to be a filename. +static StringRef getBasename(StringRef path) { + return sys::path::filename(path, sys::path::Style::windows); +} + +// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)". +std::string toString(const coff::InputFile *file) { + if (!file) + return "<internal>"; + if (file->parentName.empty() || file->kind() == coff::InputFile::ImportKind) + return file->getName(); + + return (getBasename(file->parentName) + "(" + getBasename(file->getName()) + + ")") + .str(); +} + namespace coff { std::vector<ObjFile *> ObjFile::instances; @@ -73,6 +91,10 @@ static void checkAndSetWeakAlias(SymbolTable *symtab, InputFile *f, } } +static bool ignoredSymbolName(StringRef name) { + return name == "@feat.00" || name == "@comp.id"; +} + ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {} void ArchiveFile::parse() { @@ -81,20 +103,20 @@ void ArchiveFile::parse() { // Read the symbol table to construct Lazy objects. for (const Archive::Symbol &sym : file->symbols()) - symtab->addLazy(this, sym); + symtab->addLazyArchive(this, sym); } // Returns a buffer pointing to a member file containing a given symbol. -void ArchiveFile::addMember(const Archive::Symbol *sym) { +void ArchiveFile::addMember(const Archive::Symbol &sym) { const Archive::Child &c = - CHECK(sym->getMember(), - "could not get the member for symbol " + sym->getName()); + CHECK(sym.getMember(), + "could not get the member for symbol " + toCOFFString(sym)); // Return an empty buffer if we have already returned the same buffer. if (!seen.insert(c.getChildOffset()).second) return; - driver->enqueueArchiveMember(c, sym->getName(), getName()); + driver->enqueueArchiveMember(c, sym, getName()); } std::vector<MemoryBufferRef> getArchiveMembers(Archive *file) { @@ -116,6 +138,49 @@ std::vector<MemoryBufferRef> getArchiveMembers(Archive *file) { return v; } +void LazyObjFile::fetch() { + if (mb.getBuffer().empty()) + return; + + InputFile *file; + if (isBitcode(mb)) + file = make<BitcodeFile>(mb, "", 0, std::move(symbols)); + else + file = make<ObjFile>(mb, std::move(symbols)); + mb = {}; + symtab->addFile(file); +} + +void LazyObjFile::parse() { + if (isBitcode(this->mb)) { + // Bitcode file. + std::unique_ptr<lto::InputFile> obj = + CHECK(lto::InputFile::create(this->mb), this); + for (const lto::InputFile::Symbol &sym : obj->symbols()) { + if (!sym.isUndefined()) + symtab->addLazyObject(this, sym.getName()); + } + return; + } + + // Native object file. + std::unique_ptr<Binary> coffObjPtr = CHECK(createBinary(mb), this); + COFFObjectFile *coffObj = cast<COFFObjectFile>(coffObjPtr.get()); + uint32_t numSymbols = coffObj->getNumberOfSymbols(); + for (uint32_t i = 0; i < numSymbols; ++i) { + COFFSymbolRef coffSym = check(coffObj->getSymbol(i)); + if (coffSym.isUndefined() || !coffSym.isExternal() || + coffSym.isWeakExternal()) + continue; + StringRef name; + coffObj->getSymbolName(coffSym, name); + if (coffSym.isAbsolute() && ignoredSymbolName(name)) + continue; + symtab->addLazyObject(this, name); + i += coffSym.getNumberOfAuxSymbols(); + } +} + void ObjFile::parse() { // Parse a memory buffer as a COFF file. std::unique_ptr<Binary> bin = CHECK(createBinary(mb), this); @@ -206,10 +271,6 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber, if (def) c->checksum = def->CheckSum; - // link.exe uses the presence of .rsrc$01 for LNK4078, so match that. - if (name == ".rsrc$01") - isResourceObjFile = true; - // CodeView sections are stored to a different vector because they are not // linked in the regular manner. if (c->isCodeView()) @@ -226,12 +287,18 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber, // relocations, in .rdata, leader symbol name matches the MSVC name mangling // for string literals) are subject to string tail merging. MergeChunk::addSection(c); + else if (name == ".rsrc" || name.startswith(".rsrc$")) + resourceChunks.push_back(c); else chunks.push_back(c); return c; } +void ObjFile::includeResourceChunks() { + chunks.insert(chunks.end(), resourceChunks.begin(), resourceChunks.end()); +} + void ObjFile::readAssociativeDefinition( COFFSymbolRef sym, const coff_aux_section_definition *def) { readAssociativeDefinition(sym, def, def->getNumber(sym.isBigObj())); @@ -315,7 +382,8 @@ Symbol *ObjFile::createRegular(COFFSymbolRef sym) { StringRef name; coffObj->getSymbolName(sym, name); if (sc) - return symtab->addRegular(this, name, sym.getGeneric(), sc); + return symtab->addRegular(this, name, sym.getGeneric(), sc, + sym.getValue()); // For MinGW symbols named .weak.* that point to a discarded section, // don't create an Undefined symbol. If nothing ever refers to the symbol, // everything should be fine. If something actually refers to the symbol @@ -469,7 +537,7 @@ void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection, // if the two comdat sections have e.g. different alignment. // Match that. if (leaderChunk->getContents() != newChunk.getContents()) - symtab->reportDuplicate(leader, this); + symtab->reportDuplicate(leader, this, &newChunk, sym.getValue()); break; } @@ -524,13 +592,11 @@ Optional<Symbol *> ObjFile::createDefined( if (sym.isAbsolute()) { StringRef name = getName(); - // Skip special symbols. - if (name == "@comp.id") - return nullptr; - if (name == "@feat.00") { + if (name == "@feat.00") feat00Flags = sym.getValue(); + // Skip special symbols. + if (ignoredSymbolName(name)) return nullptr; - } if (sym.isExternal()) return symtab->addAbsolute(name, sym); @@ -552,7 +618,7 @@ Optional<Symbol *> ObjFile::createDefined( // Comdat handling. // A comdat symbol consists of two symbol table entries. // The first symbol entry has the name of the section (e.g. .text), fixed - // values for the other fields, and one auxilliary record. + // values for the other fields, and one auxiliary record. // The second symbol entry has the name of the comdat symbol, called the // "comdat leader". // When this function is called for the first symbol entry of a comdat, @@ -622,7 +688,7 @@ ArrayRef<uint8_t> ObjFile::getDebugSection(StringRef secName) { return {}; } -// OBJ files systematically store critical informations in a .debug$S stream, +// OBJ files systematically store critical information in a .debug$S stream, // even if the TU was compiled with no debug info. At least two records are // always there. S_OBJNAME stores a 32-bit signature, which is loaded into the // PCHSignature member. S_COMPILE3 stores compile-time cmd-line flags. This is @@ -723,6 +789,37 @@ void ObjFile::initializeDependencies() { debugTypesObj = makeTpiSource(this); } +// Used only for DWARF debug info, which is not common (except in MinGW +// environments). This returns an optional pair of file name and line +// number for where the variable was defined. +Optional<std::pair<StringRef, uint32_t>> +ObjFile::getVariableLocation(StringRef var) { + if (!dwarf) { + dwarf = make<DWARFCache>(DWARFContext::create(*getCOFFObj())); + if (!dwarf) + return None; + } + if (config->machine == I386) + var.consume_front("_"); + Optional<std::pair<std::string, unsigned>> ret = dwarf->getVariableLoc(var); + if (!ret) + return None; + return std::make_pair(saver.save(ret->first), ret->second); +} + +// Used only for DWARF debug info, which is not common (except in MinGW +// environments). +Optional<DILineInfo> ObjFile::getDILineInfo(uint32_t offset, + uint32_t sectionIndex) { + if (!dwarf) { + dwarf = make<DWARFCache>(DWARFContext::create(*getCOFFObj())); + if (!dwarf) + return None; + } + + return dwarf->getDILineInfo(offset, sectionIndex); +} + StringRef ltrim1(StringRef s, const char *chars) { if (!s.empty() && strchr(chars, s[0])) return s.substr(1); @@ -780,8 +877,9 @@ void ImportFile::parse() { } BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, - uint64_t offsetInArchive) - : InputFile(BitcodeKind, mb) { + uint64_t offsetInArchive, + std::vector<Symbol *> &&symbols) + : InputFile(BitcodeKind, mb), symbols(std::move(symbols)) { std::string path = mb.getBufferIdentifier().str(); if (config->thinLTOIndexOnly) path = replaceThinLTOSuffix(mb.getBufferIdentifier()); @@ -860,22 +958,6 @@ std::string replaceThinLTOSuffix(StringRef path) { return (path + repl).str(); return path; } + } // namespace coff } // namespace lld - -// Returns the last element of a path, which is supposed to be a filename. -static StringRef getBasename(StringRef path) { - return sys::path::filename(path, sys::path::Style::windows); -} - -// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)". -std::string lld::toString(const coff::InputFile *file) { - if (!file) - return "<internal>"; - if (file->parentName.empty() || file->kind() == coff::InputFile::ImportKind) - return file->getName(); - - return (getBasename(file->parentName) + "(" + getBasename(file->getName()) + - ")") - .str(); -} diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h index dfad9814a397..672461cd84ba 100644 --- a/COFF/InputFiles.h +++ b/COFF/InputFiles.h @@ -10,10 +10,12 @@ #define LLD_COFF_INPUT_FILES_H #include "Config.h" +#include "lld/Common/DWARF.h" #include "lld/Common/LLVM.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/BinaryFormat/Magic.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/LTO/LTO.h" #include "llvm/Object/Archive.h" @@ -24,6 +26,7 @@ #include <vector> namespace llvm { +struct DILineInfo; namespace pdb { class DbiModuleDescriptorBuilder; } @@ -47,7 +50,6 @@ class Defined; class DefinedImportData; class DefinedImportThunk; class DefinedRegular; -class Lazy; class SectionChunk; class Symbol; class Undefined; @@ -56,7 +58,13 @@ class TpiSource; // The root class of input files. class InputFile { public: - enum Kind { ArchiveKind, ObjectKind, ImportKind, BitcodeKind }; + enum Kind { + ArchiveKind, + ObjectKind, + LazyObjectKind, + ImportKind, + BitcodeKind + }; Kind kind() const { return fileKind; } virtual ~InputFile() {} @@ -96,17 +104,35 @@ public: // Enqueues an archive member load for the given symbol. If we've already // enqueued a load for the same archive member, this function does nothing, // which ensures that we don't load the same member more than once. - void addMember(const Archive::Symbol *sym); + void addMember(const Archive::Symbol &sym); private: std::unique_ptr<Archive> file; llvm::DenseSet<uint64_t> seen; }; +// .obj or .o file between -start-lib and -end-lib. +class LazyObjFile : public InputFile { +public: + explicit LazyObjFile(MemoryBufferRef m) : InputFile(LazyObjectKind, m) {} + static bool classof(const InputFile *f) { + return f->kind() == LazyObjectKind; + } + // Makes this object file part of the link. + void fetch(); + // Adds the symbols in this file to the symbol table as LazyObject symbols. + void parse() override; + +private: + std::vector<Symbol *> symbols; +}; + // .obj or .o file. This may be a member of an archive file. class ObjFile : public InputFile { public: explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {} + explicit ObjFile(MemoryBufferRef m, std::vector<Symbol *> &&symbols) + : InputFile(ObjectKind, m), symbols(std::move(symbols)) {} static bool classof(const InputFile *f) { return f->kind() == ObjectKind; } void parse() override; MachineTypes getMachineType() override; @@ -135,6 +161,10 @@ public: return symbols.size() - 1; } + void includeResourceChunks(); + + bool isResourceObjFile() const { return !resourceChunks.empty(); } + static std::vector<ObjFile *> instances; // Flags in the absolute @feat.00 symbol if it is present. These usually @@ -162,9 +192,6 @@ public: // precompiled object. Any difference indicates out-of-date objects. llvm::Optional<uint32_t> pchSignature; - // Whether this is an object file created from .res files. - bool isResourceObjFile = false; - // Whether this file was compiled with /hotpatch. bool hotPatchable = false; @@ -177,6 +204,12 @@ public: // The .debug$T stream if there's one. llvm::Optional<llvm::codeview::CVTypeArray> debugTypes; + llvm::Optional<std::pair<StringRef, uint32_t>> + getVariableLocation(StringRef var); + + llvm::Optional<llvm::DILineInfo> getDILineInfo(uint32_t offset, + uint32_t sectionIndex); + private: const coff_section* getSection(uint32_t i); const coff_section *getSection(COFFSymbolRef sym) { @@ -234,6 +267,8 @@ private: // chunks and non-section chunks for common symbols. std::vector<Chunk *> chunks; + std::vector<SectionChunk *> resourceChunks; + // CodeView debug info sections. std::vector<SectionChunk *> debugChunks; @@ -258,6 +293,8 @@ private: // index. Nonexistent indices (which are occupied by auxiliary // symbols in the real symbol table) are filled with null pointers. std::vector<Symbol *> symbols; + + DWARFCache *dwarf = nullptr; }; // This type represents import library members that contain DLL names @@ -299,7 +336,11 @@ public: class BitcodeFile : public InputFile { public: BitcodeFile(MemoryBufferRef mb, StringRef archiveName, - uint64_t offsetInArchive); + uint64_t offsetInArchive) + : BitcodeFile(mb, archiveName, offsetInArchive, {}) {} + explicit BitcodeFile(MemoryBufferRef m, StringRef archiveName, + uint64_t offsetInArchive, + std::vector<Symbol *> &&symbols); static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; } ArrayRef<Symbol *> getSymbols() { return symbols; } MachineTypes getMachineType() override; @@ -312,6 +353,10 @@ private: std::vector<Symbol *> symbols; }; +inline bool isBitcode(MemoryBufferRef mb) { + return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode; +} + std::string replaceThinLTOSuffix(StringRef path); } // namespace coff diff --git a/COFF/LTO.cpp b/COFF/LTO.cpp index eb3c60d66077..1c21236dce2b 100644 --- a/COFF/LTO.cpp +++ b/COFF/LTO.cpp @@ -39,14 +39,14 @@ using namespace llvm; using namespace llvm::object; -using namespace lld; -using namespace lld::coff; +namespace lld { +namespace coff { // Creates an empty file to and returns a raw_fd_ostream to write to it. static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) { std::error_code ec; auto ret = - llvm::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::F_None); + std::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::OF_None); if (ec) { error("cannot open " + file + ": " + ec.message()); return nullptr; @@ -105,7 +105,7 @@ BitcodeCompiler::BitcodeCompiler() { backend = lto::createInProcessThinBackend(config->thinLTOJobs); } - ltoObj = llvm::make_unique<lto::LTO>(createConfig(), backend, + ltoObj = std::make_unique<lto::LTO>(createConfig(), backend, config->ltoPartitions); } @@ -160,8 +160,8 @@ std::vector<StringRef> BitcodeCompiler::compile() { checkError(ltoObj->run( [&](size_t task) { - return llvm::make_unique<lto::NativeObjectStream>( - llvm::make_unique<raw_svector_ostream>(buf[task])); + return std::make_unique<lto::NativeObjectStream>( + std::make_unique<raw_svector_ostream>(buf[task])); }, cache)); @@ -177,6 +177,8 @@ std::vector<StringRef> BitcodeCompiler::compile() { // files. After that, we exit from linker and ThinLTO backend runs in a // distributed environment. if (config->thinLTOIndexOnly) { + if (!config->ltoObjPath.empty()) + saveBuffer(buf[0], config->ltoObjPath); if (indexFile) indexFile->close(); return {}; @@ -204,3 +206,6 @@ std::vector<StringRef> BitcodeCompiler::compile() { return ret; } + +} // namespace coff +} // namespace lld diff --git a/COFF/MapFile.cpp b/COFF/MapFile.cpp index f98cf8fa6130..0fea60aab99b 100644 --- a/COFF/MapFile.cpp +++ b/COFF/MapFile.cpp @@ -29,14 +29,14 @@ using namespace llvm; using namespace llvm::object; -using namespace lld; -using namespace lld::coff; +namespace lld { +namespace coff { using SymbolMapTy = DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>; -static const std::string indent8 = " "; // 8 spaces -static const std::string indent16 = " "; // 16 spaces +static constexpr char indent8[] = " "; // 8 spaces +static constexpr char indent16[] = " "; // 16 spaces // Print out the first three columns of a line. static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size, @@ -87,12 +87,12 @@ getSymbolStrings(ArrayRef<DefinedRegular *> syms) { return ret; } -void coff::writeMapFile(ArrayRef<OutputSection *> outputSections) { +void writeMapFile(ArrayRef<OutputSection *> outputSections) { if (config->mapFile.empty()) return; std::error_code ec; - raw_fd_ostream os(config->mapFile, ec, sys::fs::F_None); + raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None); if (ec) fatal("cannot open " + config->mapFile + ": " + ec.message()); @@ -122,3 +122,6 @@ void coff::writeMapFile(ArrayRef<OutputSection *> outputSections) { } } } + +} // namespace coff +} // namespace lld diff --git a/COFF/MinGW.cpp b/COFF/MinGW.cpp index 2ca8ca0c058c..270cdaab4d9c 100644 --- a/COFF/MinGW.cpp +++ b/COFF/MinGW.cpp @@ -13,11 +13,12 @@ #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" -using namespace lld; -using namespace lld::coff; using namespace llvm; using namespace llvm::COFF; +namespace lld { +namespace coff { + AutoExporter::AutoExporter() { excludeLibs = { "libgcc", @@ -55,7 +56,7 @@ AutoExporter::AutoExporter() { // C++ symbols "__rtti_", "__builtin_", - // Artifical symbols such as .refptr + // Artificial symbols such as .refptr ".", }; @@ -146,9 +147,9 @@ bool AutoExporter::shouldExport(Defined *sym) const { return !excludeObjects.count(fileName); } -void coff::writeDefFile(StringRef name) { +void writeDefFile(StringRef name) { std::error_code ec; - raw_fd_ostream os(name, ec, sys::fs::F_None); + raw_fd_ostream os(name, ec, sys::fs::OF_None); if (ec) fatal("cannot open " + name + ": " + ec.message()); @@ -164,3 +165,6 @@ void coff::writeDefFile(StringRef name) { os << "\n"; } } + +} // namespace coff +} // namespace lld diff --git a/COFF/Options.td b/COFF/Options.td index 024b7be8f78d..9e58d92e634f 100644 --- a/COFF/Options.td +++ b/COFF/Options.td @@ -21,9 +21,9 @@ def aligncomm : P<"aligncomm", "Set common symbol alignment">; def alternatename : P<"alternatename", "Define weak alias">; def base : P<"base", "Base address of the program">; def color_diagnostics: Flag<["--"], "color-diagnostics">, - HelpText<"Use colors in diagnostics">; + HelpText<"Use colors in diagnostics">; def color_diagnostics_eq: Joined<["--"], "color-diagnostics=">, - HelpText<"Use colors in diagnostics; one of 'always', 'never', 'auto'">; + HelpText<"Use colors in diagnostics; one of 'always', 'never', 'auto'">; 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">; @@ -34,7 +34,8 @@ def export : P<"export", "Export a function">; def failifmismatch : P<"failifmismatch", "">; def filealign : P<"filealign", "Section alignment in the output file">; def functionpadmin : F<"functionpadmin">; -def functionpadmin_opt : P<"functionpadmin", "Prepares an image for hotpatching">; +def functionpadmin_opt : P<"functionpadmin", + "Prepares an image for hotpatching">; def guard : P<"guard", "Control flow guard">; def heap : P<"heap", "Size of the heap">; def ignore : P<"ignore", "Specify warning codes to ignore">; @@ -42,9 +43,14 @@ def implib : P<"implib", "Import library name">; def lib : F<"lib">, HelpText<"Act like lib.exe; must be first argument if present">; def libpath : P<"libpath", "Additional library search path">; -def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">; -def lldltocache : P<"lldltocache", "Path to ThinLTO cached object file directory">; -def lldltocachepolicy : P<"lldltocachepolicy", "Pruning policy for the ThinLTO cache">; +def linkrepro : P<"linkrepro", + "Dump linker invocation and input files for debugging">; +def lldignoreenv : F<"lldignoreenv">, + HelpText<"Ignore environment variables like %LIB%">; +def lldltocache : P<"lldltocache", + "Path to ThinLTO cached object file directory">; +def lldltocachepolicy : P<"lldltocachepolicy", + "Pruning policy for the ThinLTO cache">; def lldsavetemps : F<"lldsavetemps">, HelpText<"Save temporary files instead of deleting them">; def machine : P<"machine", "Specify target platform">; @@ -56,7 +62,7 @@ def order : P<"order", "Put functions in order">; def out : P<"out", "Path to file to write output">; def natvis : P<"natvis", "Path to natvis file to embed in the PDB">; def no_color_diagnostics: F<"no-color-diagnostics">, - HelpText<"Do not use colors in diagnostics">; + HelpText<"Do not use colors in diagnostics">; def pdb : P<"pdb", "PDB file path">; def pdbaltpath : P<"pdbaltpath", "PDB file path to embed in the image">; def section : P<"section", "Specify section attributes">; @@ -65,7 +71,8 @@ def stub : P<"stub", "Specify DOS stub file">; def subsystem : P<"subsystem", "Specify subsystem">; def timestamp : P<"timestamp", "Specify the PE header timestamp">; def version : P<"version", "Specify a version number in the PE header">; -def wholearchive_file : P<"wholearchive", "Include all object files from this archive">; +def wholearchive_file : P<"wholearchive", + "Include all object files from this library">; def disallowlib : Joined<["/", "-", "/?", "-?"], "disallowlib:">, Alias<nodefaultlib>; @@ -105,6 +112,8 @@ def noentry : F<"noentry">, def profile : F<"profile">; def repro : F<"Brepro">, HelpText<"Use a hash of the executable as the PE header timestamp">; +def reproduce : P<"reproduce", + "Dump linker invocation and input files for debugging">; def swaprun : P<"swaprun", "Comma-separated list of 'cd' or 'net'">; def swaprun_cd : F<"swaprun:cd">, Alias<swaprun>, AliasArgs<["cd"]>, @@ -112,10 +121,11 @@ def swaprun_cd : F<"swaprun:cd">, Alias<swaprun>, AliasArgs<["cd"]>, def swaprun_net : F<"swaprun:net">, Alias<swaprun>, AliasArgs<["net"]>, HelpText<"Make loader run output binary from swap instead of from network">; def verbose : F<"verbose">; -def wholearchive_flag : F<"wholearchive">; +def wholearchive_flag : F<"wholearchive">, + HelpText<"Include all object files from all libraries">; def force : F<"force">, - HelpText<"Allow undefined and multiply defined symbols when creating executables">; + HelpText<"Allow undefined and multiply defined symbols">; def force_unresolved : F<"force:unresolved">, HelpText<"Allow undefined symbols when creating executables">; def force_multiple : F<"force:multiple">, @@ -162,6 +172,8 @@ def help : F<"help">; def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias<help>; // LLD extensions +def end_lib : F<"end-lib">, + HelpText<"Ends group of objects treated as if they were in a library">; def exclude_all_symbols : F<"exclude-all-symbols">; def export_all_symbols : F<"export-all-symbols">; defm demangle : B<"demangle", @@ -173,9 +185,11 @@ def kill_at : F<"kill-at">; def lldmingw : F<"lldmingw">; def output_def : Joined<["/", "-", "/?", "-?"], "output-def:">; def pdb_source_path : P<"pdbsourcepath", - "Base path used to make relative source file path absolute in PDB">; + "Base path used to make relative source file path absolute in PDB">; def rsp_quoting : Joined<["--"], "rsp-quoting=">, HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">; +def start_lib : F<"start-lib">, + HelpText<"Starts group of objects treated as if they were in a library">; def thinlto_emit_imports_files : F<"thinlto-emit-imports-files">, HelpText<"Emit .imports files with -thinlto-index-only">; @@ -191,6 +205,9 @@ def thinlto_object_suffix_replace : P< def thinlto_prefix_replace: P< "thinlto-prefix-replace", "'old;new' replace old prefix with new prefix in ThinLTO outputs">; +def lto_obj_path : P< + "lto-obj-path", + "output native object for merged LTO unit to this path">; def dash_dash_version : Flag<["--"], "version">, HelpText<"Print version information">; defm threads: B<"threads", diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp index a55e5136e040..ea99583b1d80 100644 --- a/COFF/PDB.cpp +++ b/COFF/PDB.cpp @@ -51,21 +51,22 @@ #include "llvm/Object/COFF.h" #include "llvm/Object/CVDebugRecord.h" #include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/CRC.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FormatVariadic.h" -#include "llvm/Support/JamCRC.h" #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" #include <memory> -using namespace lld; -using namespace lld::coff; using namespace llvm; using namespace llvm::codeview; using llvm::object::coff_section; +namespace lld { +namespace coff { + static ExitOnError exitOnErr; static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root()); @@ -513,16 +514,15 @@ static bool equals_path(StringRef path1, StringRef path2) { return path1.equals(path2); #endif } - // Find by name an OBJ provided on the command line -static ObjFile *findObjByName(StringRef fileNameOnly) { - SmallString<128> currentPath; - +static ObjFile *findObjWithPrecompSignature(StringRef fileNameOnly, + uint32_t precompSignature) { for (ObjFile *f : ObjFile::instances) { StringRef currentFileName = sys::path::filename(f->getName()); - // Compare based solely on the file name (link.exe behavior) - if (equals_path(currentFileName, fileNameOnly)) + if (f->pchSignature.hasValue() && + f->pchSignature.getValue() == precompSignature && + equals_path(fileNameOnly, currentFileName)) return f; } return nullptr; @@ -559,22 +559,15 @@ Expected<const CVIndexMap &> PDBLinker::aquirePrecompObj(ObjFile *file) { // link.exe requires that a precompiled headers object must always be provided // on the command-line, even if that's not necessary. - auto precompFile = findObjByName(precompFileName); + auto precompFile = + findObjWithPrecompSignature(precompFileName, precomp.Signature); if (!precompFile) return createFileError( - precompFileName.str(), - make_error<pdb::PDBError>(pdb::pdb_error_code::external_cmdline_ref)); + precomp.getPrecompFilePath().str(), + make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch)); addObjFile(precompFile, &indexMap); - if (!precompFile->pchSignature) - fatal(precompFile->getName() + " is not a precompiled headers object"); - - if (precomp.getSignature() != precompFile->pchSignature.getValueOr(0)) - return createFileError( - precomp.getPrecompFilePath().str(), - make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date)); - return indexMap; } @@ -965,9 +958,7 @@ static pdb::SectionContrib createSectionContrib(const Chunk *c, uint32_t modi) { sc.Imod = secChunk->file->moduleDBI->getModuleIndex(); ArrayRef<uint8_t> contents = secChunk->getContents(); JamCRC crc(0); - ArrayRef<char> charContents = makeArrayRef( - reinterpret_cast<const char *>(contents.data()), contents.size()); - crc.update(charContents); + crc.update(contents); sc.DataCrc = crc.getCRC(); } else { sc.Characteristics = os ? os->header.Characteristics : 0; @@ -1150,7 +1141,7 @@ void DebugSHandler::finish() { // string table. Generally the string table subsection appears after the // checksum table, so we have to do this after looping over all the // subsections. - auto newChecksums = make_unique<DebugChecksumsSubsection>(linker.pdbStrTab); + auto newChecksums = std::make_unique<DebugChecksumsSubsection>(linker.pdbStrTab); for (FileChecksumEntry &fc : checksums) { SmallString<128> filename = exitOnErr(cVStrTab.getString(fc.FileNameOffset)); @@ -1599,7 +1590,7 @@ void PDBLinker::addImportFilesToPDB(ArrayRef<OutputSection *> outputSections) { } // Creates a PDB file. -void coff::createPDB(SymbolTable *symtab, +void createPDB(SymbolTable *symtab, ArrayRef<OutputSection *> outputSections, ArrayRef<uint8_t> sectionTable, llvm::codeview::DebugInfo *buildId) { @@ -1693,6 +1684,7 @@ void PDBLinker::addSections(ArrayRef<OutputSection *> outputSections, } void PDBLinker::commit(codeview::GUID *guid) { + ExitOnError exitOnErr((config->pdbPath + ": ").str()); // Write to a file. exitOnErr(builder.commit(config->pdbPath, guid)); } @@ -1797,10 +1789,10 @@ static bool findLineTable(const SectionChunk *c, uint32_t addr, } // Use CodeView line tables to resolve a file and line number for the given -// offset into the given chunk and return them, or {"", 0} if a line table was +// offset into the given chunk and return them, or None if a line table was // not found. -std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *c, - uint32_t addr) { +Optional<std::pair<StringRef, uint32_t>> +getFileLineCodeView(const SectionChunk *c, uint32_t addr) { ExitOnError exitOnErr; DebugStringTableSubsectionRef cVStrTab; @@ -1809,7 +1801,7 @@ std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *c, uint32_t offsetInLinetable; if (!findLineTable(c, addr, cVStrTab, checksums, lines, offsetInLinetable)) - return {"", 0}; + return None; Optional<uint32_t> nameIndex; Optional<uint32_t> lineNumber; @@ -1823,14 +1815,17 @@ std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *c, } StringRef filename = exitOnErr(getFileName(cVStrTab, checksums, *nameIndex)); - return {filename, *lineNumber}; + return std::make_pair(filename, *lineNumber); } nameIndex = entry.NameIndex; lineNumber = li.getStartLine(); } } if (!nameIndex) - return {"", 0}; + return None; StringRef filename = exitOnErr(getFileName(cVStrTab, checksums, *nameIndex)); - return {filename, *lineNumber}; + return std::make_pair(filename, *lineNumber); } + +} // namespace coff +} // namespace lld diff --git a/COFF/PDB.h b/COFF/PDB.h index 3ac1adc85c5d..273609ea788c 100644 --- a/COFF/PDB.h +++ b/COFF/PDB.h @@ -10,6 +10,7 @@ #define LLD_COFF_PDB_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" namespace llvm { @@ -29,9 +30,9 @@ void createPDB(SymbolTable *symtab, llvm::ArrayRef<uint8_t> sectionTable, llvm::codeview::DebugInfo *buildId); -std::pair<llvm::StringRef, uint32_t> getFileLine(const SectionChunk *c, - uint32_t addr); -} -} +llvm::Optional<std::pair<llvm::StringRef, uint32_t>> +getFileLineCodeView(const SectionChunk *c, uint32_t addr); +} // namespace coff +} // namespace lld #endif diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp index 0aff164ee567..869dfc7a2ee5 100644 --- a/COFF/SymbolTable.cpp +++ b/COFF/SymbolTable.cpp @@ -15,6 +15,7 @@ #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "lld/Common/Timer.h" +#include "llvm/DebugInfo/Symbolize/Symbolize.h" #include "llvm/IR/LLVMContext.h" #include "llvm/Object/WindowsMachineFlag.h" #include "llvm/Support/Debug.h" @@ -61,6 +62,24 @@ static void errorOrWarn(const Twine &s) { error(s); } +// Causes the file associated with a lazy symbol to be linked in. +static void forceLazy(Symbol *s) { + s->pendingArchiveLoad = true; + switch (s->kind()) { + case Symbol::Kind::LazyArchiveKind: { + auto *l = cast<LazyArchive>(s); + l->file->addMember(l->sym); + break; + } + case Symbol::Kind::LazyObjectKind: + cast<LazyObject>(s)->file->fetch(); + break; + default: + llvm_unreachable( + "symbol passed to forceLazy is not a LazyArchive or LazyObject"); + } +} + // Returns the symbol in SC whose value is <= Addr that is closest to Addr. // This is generally the global variable or function whose definition contains // Addr. @@ -69,7 +88,8 @@ static Symbol *getSymbol(SectionChunk *sc, uint32_t addr) { for (Symbol *s : sc->file->getSymbols()) { auto *d = dyn_cast_or_null<DefinedRegular>(s); - if (!d || !d->data || d->getChunk() != sc || d->getValue() > addr || + if (!d || !d->data || d->file != sc->file || d->getChunk() != sc || + d->getValue() > addr || (candidate && d->getValue() < candidate->getValue())) continue; @@ -79,6 +99,38 @@ static Symbol *getSymbol(SectionChunk *sc, uint32_t addr) { return candidate; } +static std::vector<std::string> getSymbolLocations(BitcodeFile *file) { + std::string res("\n>>> referenced by "); + StringRef source = file->obj->getSourceFileName(); + if (!source.empty()) + res += source.str() + "\n>>> "; + res += toString(file); + return {res}; +} + +static Optional<std::pair<StringRef, uint32_t>> +getFileLineDwarf(const SectionChunk *c, uint32_t addr) { + Optional<DILineInfo> optionalLineInfo = + c->file->getDILineInfo(addr, c->getSectionNumber() - 1); + if (!optionalLineInfo) + return None; + const DILineInfo &lineInfo = *optionalLineInfo; + if (lineInfo.FileName == DILineInfo::BadString) + return None; + return std::make_pair(saver.save(lineInfo.FileName), lineInfo.Line); +} + +static Optional<std::pair<StringRef, uint32_t>> +getFileLine(const SectionChunk *c, uint32_t addr) { + // MinGW can optionally use codeview, even if the default is dwarf. + Optional<std::pair<StringRef, uint32_t>> fileLine = + getFileLineCodeView(c, addr); + // If codeview didn't yield any result, check dwarf in MinGW mode. + if (!fileLine && config->mingw) + fileLine = getFileLineDwarf(c, addr); + return fileLine; +} + // Given a file and the index of a symbol in that file, returns a description // of all references to that symbol from that file. If no debug information is // available, returns just the name of the file, else one string per actual @@ -97,11 +149,13 @@ std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex) { for (const coff_relocation &r : sc->getRelocs()) { if (r.SymbolTableIndex != symIndex) continue; - std::pair<StringRef, uint32_t> fileLine = + Optional<std::pair<StringRef, uint32_t>> fileLine = getFileLine(sc, r.VirtualAddress); Symbol *sym = getSymbol(sc, r.VirtualAddress); - if (!fileLine.first.empty() || sym) - locations.push_back({sym, fileLine}); + if (fileLine) + locations.push_back({sym, *fileLine}); + else if (sym) + locations.push_back({sym, {"", 0}}); } } @@ -123,13 +177,23 @@ std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex) { return symbolLocations; } +std::vector<std::string> getSymbolLocations(InputFile *file, + uint32_t symIndex) { + if (auto *o = dyn_cast<ObjFile>(file)) + return getSymbolLocations(o, symIndex); + if (auto *b = dyn_cast<BitcodeFile>(file)) + return getSymbolLocations(b); + llvm_unreachable("unsupported file type passed to getSymbolLocations"); + return {}; +} + // For an undefined symbol, stores all files referencing it and the index of // the undefined symbol in each file. struct UndefinedDiag { Symbol *sym; struct File { - ObjFile *oFile; - uint64_t symIndex; + InputFile *file; + uint32_t symIndex; }; std::vector<File> files; }; @@ -143,7 +207,7 @@ static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) { size_t i = 0, numRefs = 0; for (const UndefinedDiag::File &ref : undefDiag.files) { std::vector<std::string> symbolLocations = - getSymbolLocations(ref.oFile, ref.symIndex); + getSymbolLocations(ref.file, ref.symIndex); numRefs += symbolLocations.size(); for (const std::string &s : symbolLocations) { if (i >= maxUndefReferences) @@ -165,28 +229,33 @@ void SymbolTable::loadMinGWAutomaticImports() { continue; if (!sym->isUsedInRegularObj) continue; + if (undef->getWeakAlias()) + continue; StringRef name = undef->getName(); if (name.startswith("__imp_")) continue; - // If we have an undefined symbol, but we have a Lazy representing a - // symbol we could load from file, make sure to load that. - Lazy *l = dyn_cast_or_null<Lazy>(find(("__imp_" + name).str())); - if (!l || l->pendingArchiveLoad) + // If we have an undefined symbol, but we have a lazy symbol we could + // load, load it. + Symbol *l = find(("__imp_" + name).str()); + if (!l || l->pendingArchiveLoad || !l->isLazy()) continue; - log("Loading lazy " + l->getName() + " from " + l->file->getName() + + log("Loading lazy " + l->getName() + " from " + l->getFile()->getName() + " for automatic import"); - l->pendingArchiveLoad = true; - l->file->addMember(&l->sym); + forceLazy(l); } } -bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) { +Defined *SymbolTable::impSymbol(StringRef name) { if (name.startswith("__imp_")) - return false; - Defined *imp = dyn_cast_or_null<Defined>(find(("__imp_" + name).str())); + return nullptr; + return dyn_cast_or_null<Defined>(find(("__imp_" + name).str())); +} + +bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) { + Defined *imp = impSymbol(name); if (!imp) return false; @@ -232,7 +301,97 @@ bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) { return true; } -void SymbolTable::reportRemainingUndefines() { +/// Helper function for reportUnresolvable and resolveRemainingUndefines. +/// This function emits an "undefined symbol" diagnostic for each symbol in +/// undefs. If localImports is not nullptr, it also emits a "locally +/// defined symbol imported" diagnostic for symbols in localImports. +/// objFiles and bitcodeFiles (if not nullptr) are used to report where +/// undefined symbols are referenced. +static void +reportProblemSymbols(const SmallPtrSetImpl<Symbol *> &undefs, + const DenseMap<Symbol *, Symbol *> *localImports, + const std::vector<ObjFile *> objFiles, + const std::vector<BitcodeFile *> *bitcodeFiles) { + + // Return early if there is nothing to report (which should be + // the common case). + if (undefs.empty() && (!localImports || localImports->empty())) + return; + + for (Symbol *b : config->gcroot) { + if (undefs.count(b)) + errorOrWarn("<root>: undefined symbol: " + toString(*b)); + if (localImports) + if (Symbol *imp = localImports->lookup(b)) + warn("<root>: locally defined symbol imported: " + toString(*imp) + + " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); + } + + std::vector<UndefinedDiag> undefDiags; + DenseMap<Symbol *, int> firstDiag; + + auto processFile = [&](InputFile *file, ArrayRef<Symbol *> symbols) { + uint32_t symIndex = (uint32_t)-1; + for (Symbol *sym : symbols) { + ++symIndex; + if (!sym) + continue; + if (undefs.count(sym)) { + auto it = firstDiag.find(sym); + if (it == firstDiag.end()) { + firstDiag[sym] = undefDiags.size(); + undefDiags.push_back({sym, {{file, symIndex}}}); + } else { + undefDiags[it->second].files.push_back({file, symIndex}); + } + } + if (localImports) + if (Symbol *imp = localImports->lookup(sym)) + warn(toString(file) + + ": locally defined symbol imported: " + toString(*imp) + + " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); + } + }; + + for (ObjFile *file : objFiles) + processFile(file, file->getSymbols()); + + if (bitcodeFiles) + for (BitcodeFile *file : *bitcodeFiles) + processFile(file, file->getSymbols()); + + for (const UndefinedDiag &undefDiag : undefDiags) + reportUndefinedSymbol(undefDiag); +} + +void SymbolTable::reportUnresolvable() { + SmallPtrSet<Symbol *, 8> undefs; + for (auto &i : symMap) { + Symbol *sym = i.second; + auto *undef = dyn_cast<Undefined>(sym); + if (!undef) + continue; + if (undef->getWeakAlias()) + continue; + StringRef name = undef->getName(); + if (name.startswith("__imp_")) { + Symbol *imp = find(name.substr(strlen("__imp_"))); + if (imp && isa<Defined>(imp)) + continue; + } + if (name.contains("_PchSym_")) + continue; + if (config->mingw && impSymbol(name)) + continue; + undefs.insert(sym); + } + + reportProblemSymbols(undefs, + /* localImports */ nullptr, ObjFile::instances, + &BitcodeFile::instances); +} + +void SymbolTable::resolveRemainingUndefines() { SmallPtrSet<Symbol *, 8> undefs; DenseMap<Symbol *, Symbol *> localImports; @@ -290,46 +449,9 @@ void SymbolTable::reportRemainingUndefines() { undefs.insert(sym); } - if (undefs.empty() && localImports.empty()) - return; - - for (Symbol *b : config->gcroot) { - if (undefs.count(b)) - errorOrWarn("<root>: undefined symbol: " + toString(*b)); - if (config->warnLocallyDefinedImported) - if (Symbol *imp = localImports.lookup(b)) - warn("<root>: locally defined symbol imported: " + toString(*imp) + - " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); - } - - std::vector<UndefinedDiag> undefDiags; - DenseMap<Symbol *, int> firstDiag; - - for (ObjFile *file : ObjFile::instances) { - size_t symIndex = (size_t)-1; - for (Symbol *sym : file->getSymbols()) { - ++symIndex; - if (!sym) - continue; - if (undefs.count(sym)) { - auto it = firstDiag.find(sym); - if (it == firstDiag.end()) { - firstDiag[sym] = undefDiags.size(); - undefDiags.push_back({sym, {{file, symIndex}}}); - } else { - undefDiags[it->second].files.push_back({file, symIndex}); - } - } - if (config->warnLocallyDefinedImported) - if (Symbol *imp = localImports.lookup(sym)) - warn(toString(file) + - ": locally defined symbol imported: " + toString(*imp) + - " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); - } - } - - for (const UndefinedDiag& undefDiag : undefDiags) - reportUndefinedSymbol(undefDiag); + reportProblemSymbols( + undefs, config->warnLocallyDefinedImported ? &localImports : nullptr, + ObjFile::instances, /* bitcode files no longer needed */ nullptr); } std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) { @@ -356,44 +478,109 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f, Symbol *s; bool wasInserted; std::tie(s, wasInserted) = insert(name, f); - if (wasInserted || (isa<Lazy>(s) && isWeakAlias)) { + if (wasInserted || (s->isLazy() && isWeakAlias)) { replaceSymbol<Undefined>(s, name); return s; } - if (auto *l = dyn_cast<Lazy>(s)) { - if (!s->pendingArchiveLoad) { - s->pendingArchiveLoad = true; - l->file->addMember(&l->sym); - } - } + if (s->isLazy()) + forceLazy(s); return s; } -void SymbolTable::addLazy(ArchiveFile *f, const Archive::Symbol sym) { +void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) { StringRef name = sym.getName(); Symbol *s; bool wasInserted; std::tie(s, wasInserted) = insert(name); if (wasInserted) { - replaceSymbol<Lazy>(s, f, sym); + replaceSymbol<LazyArchive>(s, f, sym); + return; + } + auto *u = dyn_cast<Undefined>(s); + if (!u || u->weakAlias || s->pendingArchiveLoad) + return; + s->pendingArchiveLoad = true; + f->addMember(sym); +} + +void SymbolTable::addLazyObject(LazyObjFile *f, StringRef n) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, f); + if (wasInserted) { + replaceSymbol<LazyObject>(s, f, n); return; } auto *u = dyn_cast<Undefined>(s); if (!u || u->weakAlias || s->pendingArchiveLoad) return; s->pendingArchiveLoad = true; - f->addMember(&sym); + f->fetch(); } -void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile) { - std::string msg = "duplicate symbol: " + toString(*existing) + " in " + - toString(existing->getFile()) + " and in " + - toString(newFile); +static std::string getSourceLocationBitcode(BitcodeFile *file) { + std::string res("\n>>> defined at "); + StringRef source = file->obj->getSourceFileName(); + if (!source.empty()) + res += source.str() + "\n>>> "; + res += toString(file); + return res; +} + +static std::string getSourceLocationObj(ObjFile *file, SectionChunk *sc, + uint32_t offset, StringRef name) { + Optional<std::pair<StringRef, uint32_t>> fileLine; + if (sc) + fileLine = getFileLine(sc, offset); + if (!fileLine) + fileLine = file->getVariableLocation(name); + + std::string res; + llvm::raw_string_ostream os(res); + os << "\n>>> defined at "; + if (fileLine) + os << fileLine->first << ":" << fileLine->second << "\n>>> "; + os << toString(file); + return os.str(); +} + +static std::string getSourceLocation(InputFile *file, SectionChunk *sc, + uint32_t offset, StringRef name) { + if (auto *o = dyn_cast<ObjFile>(file)) + return getSourceLocationObj(o, sc, offset, name); + if (auto *b = dyn_cast<BitcodeFile>(file)) + return getSourceLocationBitcode(b); + return "\n>>> defined at " + toString(file); +} + +// Construct and print an error message in the form of: +// +// lld-link: error: duplicate symbol: foo +// >>> defined at bar.c:30 +// >>> bar.o +// >>> defined at baz.c:563 +// >>> baz.o +void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile, + SectionChunk *newSc, + uint32_t newSectionOffset) { + std::string msg; + llvm::raw_string_ostream os(msg); + os << "duplicate symbol: " << toString(*existing); + + DefinedRegular *d = cast<DefinedRegular>(existing); + if (d && isa<ObjFile>(d->getFile())) { + os << getSourceLocation(d->getFile(), d->getChunk(), d->getValue(), + existing->getName()); + } else { + os << getSourceLocation(existing->getFile(), nullptr, 0, ""); + } + os << getSourceLocation(newFile, newSc, newSectionOffset, + existing->getName()); if (config->forceMultiple) - warn(msg); + warn(os.str()); else - error(msg); + error(os.str()); } Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) { @@ -401,7 +588,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) { bool wasInserted; std::tie(s, wasInserted) = insert(n, nullptr); s->isUsedInRegularObj = true; - if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) + if (wasInserted || isa<Undefined>(s) || s->isLazy()) replaceSymbol<DefinedAbsolute>(s, n, sym); else if (!isa<DefinedCOFF>(s)) reportDuplicate(s, nullptr); @@ -413,7 +600,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) { bool wasInserted; std::tie(s, wasInserted) = insert(n, nullptr); s->isUsedInRegularObj = true; - if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) + if (wasInserted || isa<Undefined>(s) || s->isLazy()) replaceSymbol<DefinedAbsolute>(s, n, va); else if (!isa<DefinedCOFF>(s)) reportDuplicate(s, nullptr); @@ -425,7 +612,7 @@ Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) { bool wasInserted; std::tie(s, wasInserted) = insert(n, nullptr); s->isUsedInRegularObj = true; - if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) + if (wasInserted || isa<Undefined>(s) || s->isLazy()) replaceSymbol<DefinedSynthetic>(s, n, c); else if (!isa<DefinedCOFF>(s)) reportDuplicate(s, nullptr); @@ -433,8 +620,8 @@ Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) { } Symbol *SymbolTable::addRegular(InputFile *f, StringRef n, - const coff_symbol_generic *sym, - SectionChunk *c) { + const coff_symbol_generic *sym, SectionChunk *c, + uint32_t sectionOffset) { Symbol *s; bool wasInserted; std::tie(s, wasInserted) = insert(n, f); @@ -442,7 +629,7 @@ Symbol *SymbolTable::addRegular(InputFile *f, StringRef n, replaceSymbol<DefinedRegular>(s, f, n, /*IsCOMDAT*/ false, /*IsExternal*/ true, sym, c); else - reportDuplicate(s, f); + reportDuplicate(s, f, c, sectionOffset); return s; } @@ -481,7 +668,7 @@ Symbol *SymbolTable::addImportData(StringRef n, ImportFile *f) { bool wasInserted; std::tie(s, wasInserted) = insert(n, nullptr); s->isUsedInRegularObj = true; - if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) { + if (wasInserted || isa<Undefined>(s) || s->isLazy()) { replaceSymbol<DefinedImportData>(s, n, f); return s; } @@ -496,7 +683,7 @@ Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id, bool wasInserted; std::tie(s, wasInserted) = insert(name, nullptr); s->isUsedInRegularObj = true; - if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) { + if (wasInserted || isa<Undefined>(s) || s->isLazy()) { replaceSymbol<DefinedImportThunk>(s, name, id, machine); return s; } @@ -505,6 +692,21 @@ Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id, return nullptr; } +void SymbolTable::addLibcall(StringRef name) { + Symbol *sym = findUnderscore(name); + if (!sym) + return; + + if (auto *l = dyn_cast<LazyArchive>(sym)) { + MemoryBufferRef mb = l->getMemberBuffer(); + if (isBitcode(mb)) + addUndefined(sym->getName()); + } else if (LazyObject *o = dyn_cast<LazyObject>(sym)) { + if (isBitcode(o->file->mb)) + addUndefined(sym->getName()); + } +} + std::vector<Chunk *> SymbolTable::getChunks() { std::vector<Chunk *> res; for (ObjFile *file : ObjFile::instances) { diff --git a/COFF/SymbolTable.h b/COFF/SymbolTable.h index 88f47cbe9e78..cd8a53dcecdc 100644 --- a/COFF/SymbolTable.h +++ b/COFF/SymbolTable.h @@ -29,7 +29,7 @@ class Defined; class DefinedAbsolute; class DefinedRegular; class DefinedRelative; -class Lazy; +class LazyArchive; class SectionChunk; class Symbol; @@ -49,10 +49,13 @@ class SymbolTable { public: void addFile(InputFile *file); + // Emit errors for symbols that cannot be resolved. + void reportUnresolvable(); + // Try to resolve any undefined symbols and update the symbol table // accordingly, then print an error message for any remaining undefined - // symbols. - void reportRemainingUndefines(); + // symbols and warn about imported local symbols. + void resolveRemainingUndefines(); void loadMinGWAutomaticImports(); bool handleMinGWAutomaticImport(Symbol *sym, StringRef name); @@ -83,11 +86,12 @@ public: Symbol *addAbsolute(StringRef n, uint64_t va); Symbol *addUndefined(StringRef name, InputFile *f, bool isWeakAlias); - void addLazy(ArchiveFile *f, const Archive::Symbol sym); + void addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym); + void addLazyObject(LazyObjFile *f, StringRef n); Symbol *addAbsolute(StringRef n, COFFSymbolRef s); Symbol *addRegular(InputFile *f, StringRef n, const llvm::object::coff_symbol_generic *s = nullptr, - SectionChunk *c = nullptr); + SectionChunk *c = nullptr, uint32_t sectionOffset = 0); std::pair<DefinedRegular *, bool> addComdat(InputFile *f, StringRef n, const llvm::object::coff_symbol_generic *s = nullptr); @@ -97,8 +101,11 @@ public: Symbol *addImportData(StringRef n, ImportFile *f); Symbol *addImportThunk(StringRef name, DefinedImportData *s, uint16_t machine); + void addLibcall(StringRef name); - void reportDuplicate(Symbol *existing, InputFile *newFile); + void reportDuplicate(Symbol *existing, InputFile *newFile, + SectionChunk *newSc = nullptr, + uint32_t newSectionOffset = 0); // A list of chunks which to be added to .rdata. std::vector<Chunk *> localImportChunks; @@ -110,6 +117,9 @@ public: } private: + /// Given a name without "__imp_" prefix, returns a defined symbol + /// with the "__imp_" prefix, if it exists. + Defined *impSymbol(StringRef name); /// Inserts symbol if not already present. std::pair<Symbol *, bool> insert(StringRef name); /// Same as insert(Name), but also sets isUsedInRegularObj. diff --git a/COFF/Symbols.cpp b/COFF/Symbols.cpp index 3583d4cb28c1..938c9c527ffa 100644 --- a/COFF/Symbols.cpp +++ b/COFF/Symbols.cpp @@ -12,6 +12,7 @@ #include "lld/Common/Memory.h" #include "lld/Common/Strings.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Demangle/Demangle.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" @@ -20,18 +21,35 @@ using namespace llvm::object; using namespace lld::coff; +namespace lld { + static_assert(sizeof(SymbolUnion) <= 48, "symbols should be optimized for memory usage"); // Returns a symbol name for an error message. -std::string lld::toString(coff::Symbol &b) { - if (config->demangle) - if (Optional<std::string> s = lld::demangleMSVC(b.getName())) - return *s; - return b.getName(); +static std::string maybeDemangleSymbol(StringRef symName) { + if (config->demangle) { + std::string prefix; + StringRef prefixless = symName; + if (prefixless.consume_front("__imp_")) + prefix = "__declspec(dllimport) "; + StringRef demangleInput = prefixless; + if (config->machine == I386) + demangleInput.consume_front("_"); + std::string demangled = demangle(demangleInput); + if (demangled != demangleInput) + return prefix + demangle(demangleInput); + return (prefix + prefixless).str(); + } + return symName; +} +std::string toString(coff::Symbol &b) { + return maybeDemangleSymbol(b.getName()); +} +std::string toCOFFString(const Archive::Symbol &b) { + return maybeDemangleSymbol(b.getName()); } -namespace lld { namespace coff { StringRef Symbol::getName() { @@ -56,7 +74,9 @@ StringRef Symbol::getName() { InputFile *Symbol::getFile() { if (auto *sym = dyn_cast<DefinedCOFF>(this)) return sym->file; - if (auto *sym = dyn_cast<Lazy>(this)) + if (auto *sym = dyn_cast<LazyArchive>(this)) + return sym->file; + if (auto *sym = dyn_cast<LazyObject>(this)) return sym->file; return nullptr; } @@ -113,5 +133,14 @@ Defined *Undefined::getWeakAlias() { return d; return nullptr; } + +MemoryBufferRef LazyArchive::getMemberBuffer() { + Archive::Child c = + CHECK(sym.getMember(), + "could not get the member for symbol " + toCOFFString(sym)); + return CHECK(c.getMemoryBufferRef(), + "could not get the buffer for the member defining symbol " + + toCOFFString(sym)); +} } // namespace coff } // namespace lld diff --git a/COFF/Symbols.h b/COFF/Symbols.h index 86cd4f585e50..fd79bd5065b1 100644 --- a/COFF/Symbols.h +++ b/COFF/Symbols.h @@ -21,6 +21,14 @@ #include <vector> namespace lld { + +std::string toString(coff::Symbol &b); + +// There are two different ways to convert an Archive::Symbol to a string: +// One for Microsoft name mangling and one for Itanium name mangling. +// Call the functions toCOFFString and toELFString, not just toString. +std::string toCOFFString(const coff::Archive::Symbol &b); + namespace coff { using llvm::object::Archive; @@ -51,7 +59,8 @@ public: DefinedSyntheticKind, UndefinedKind, - LazyKind, + LazyArchiveKind, + LazyObjectKind, LastDefinedCOFFKind = DefinedCommonKind, LastDefinedKind = DefinedSyntheticKind, @@ -71,6 +80,10 @@ public: // after calling markLive. bool isLive() const; + bool isLazy() const { + return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind; + } + protected: friend SymbolTable; explicit Symbol(Kind k, StringRef n = "") @@ -248,24 +261,29 @@ private: // This class represents a symbol defined in an archive file. It is // created from an archive file header, and it knows how to load an // object file from an archive to replace itself with a defined -// symbol. If the resolver finds both Undefined and Lazy for -// the same name, it will ask the Lazy to load a file. -class Lazy : public Symbol { +// symbol. If the resolver finds both Undefined and LazyArchive for +// the same name, it will ask the LazyArchive to load a file. +class LazyArchive : public Symbol { public: - Lazy(ArchiveFile *f, const Archive::Symbol s) - : Symbol(LazyKind, s.getName()), file(f), sym(s) {} + LazyArchive(ArchiveFile *f, const Archive::Symbol s) + : Symbol(LazyArchiveKind, s.getName()), file(f), sym(s) {} - static bool classof(const Symbol *s) { return s->kind() == LazyKind; } - - ArchiveFile *file; + static bool classof(const Symbol *s) { return s->kind() == LazyArchiveKind; } -private: - friend SymbolTable; + MemoryBufferRef getMemberBuffer(); -private: + ArchiveFile *file; const Archive::Symbol sym; }; +class LazyObject : public Symbol { +public: + LazyObject(LazyObjFile *f, StringRef n) + : Symbol(LazyObjectKind, n), file(f) {} + static bool classof(const Symbol *s) { return s->kind() == LazyObjectKind; } + LazyObjFile *file; +}; + // Undefined symbols. class Undefined : public Symbol { public: @@ -371,7 +389,8 @@ inline uint64_t Defined::getRVA() { return cast<DefinedCommon>(this)->getRVA(); case DefinedRegularKind: return cast<DefinedRegular>(this)->getRVA(); - case LazyKind: + case LazyArchiveKind: + case LazyObjectKind: case UndefinedKind: llvm_unreachable("Cannot get the address for an undefined symbol."); } @@ -394,7 +413,8 @@ inline Chunk *Defined::getChunk() { return cast<DefinedLocalImport>(this)->getChunk(); case DefinedCommonKind: return cast<DefinedCommon>(this)->getChunk(); - case LazyKind: + case LazyArchiveKind: + case LazyObjectKind: case UndefinedKind: llvm_unreachable("Cannot get the chunk of an undefined symbol."); } @@ -409,11 +429,12 @@ union SymbolUnion { alignas(DefinedCommon) char b[sizeof(DefinedCommon)]; alignas(DefinedAbsolute) char c[sizeof(DefinedAbsolute)]; alignas(DefinedSynthetic) char d[sizeof(DefinedSynthetic)]; - alignas(Lazy) char e[sizeof(Lazy)]; + alignas(LazyArchive) char e[sizeof(LazyArchive)]; alignas(Undefined) char f[sizeof(Undefined)]; alignas(DefinedImportData) char g[sizeof(DefinedImportData)]; alignas(DefinedImportThunk) char h[sizeof(DefinedImportThunk)]; alignas(DefinedLocalImport) char i[sizeof(DefinedLocalImport)]; + alignas(LazyObject) char j[sizeof(LazyObject)]; }; template <typename T, typename... ArgT> @@ -429,7 +450,6 @@ void replaceSymbol(Symbol *s, ArgT &&... arg) { } } // namespace coff -std::string toString(coff::Symbol &b); } // namespace lld #endif diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp index 36ef87de4263..9729c6938ec8 100644 --- a/COFF/Writer.cpp +++ b/COFF/Writer.cpp @@ -40,8 +40,9 @@ using namespace llvm::COFF; using namespace llvm::object; using namespace llvm::support; using namespace llvm::support::endian; -using namespace lld; -using namespace lld::coff; + +namespace lld { +namespace coff { /* To re-generate DOSProgram: $ cat > /tmp/DOSProgram.asm @@ -240,6 +241,8 @@ private: IdataContents idata; Chunk *importTableStart = nullptr; uint64_t importTableSize = 0; + Chunk *edataStart = nullptr; + Chunk *edataEnd = nullptr; Chunk *iatStart = nullptr; uint64_t iatSize = 0; DelayLoadContents delayIdata; @@ -283,9 +286,6 @@ private: }; } // anonymous namespace -namespace lld { -namespace coff { - static Timer codeLayoutTimer("Code Layout", Timer::root()); static Timer diskCommitTimer("Commit Output File", Timer::root()); @@ -331,9 +331,6 @@ void OutputSection::addContributingPartialSection(PartialSection *sec) { contribSections.push_back(sec); } -} // namespace coff -} // namespace lld - // Check whether the target address S is in range from a relocation // of type relType at address P. static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) { @@ -626,6 +623,9 @@ void Writer::run() { writeMapFile(outputSections); + if (errorCount()) + return; + ScopedTimer t2(diskCommitTimer); if (auto e = buffer->commit()) fatal("failed to write the output file: " + toString(std::move(e))); @@ -738,7 +738,8 @@ void Writer::addSyntheticIdata() { add(".idata$2", idata.dirs); add(".idata$4", idata.lookups); add(".idata$5", idata.addresses); - add(".idata$6", idata.hints); + if (!idata.hints.empty()) + add(".idata$6", idata.hints); add(".idata$7", idata.dllNames); } @@ -762,6 +763,28 @@ void Writer::locateImportTables() { } } +// Return whether a SectionChunk's suffix (the dollar and any trailing +// suffix) should be removed and sorted into the main suffixless +// PartialSection. +static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name) { + // On MinGW, comdat groups are formed by putting the comdat group name + // after the '$' in the section name. For .eh_frame$<symbol>, that must + // still be sorted before the .eh_frame trailer from crtend.o, thus just + // strip the section name trailer. For other sections, such as + // .tls$$<symbol> (where non-comdat .tls symbols are otherwise stored in + // ".tls$"), they must be strictly sorted after .tls. And for the + // hypothetical case of comdat .CRT$XCU, we definitely need to keep the + // suffix for sorting. Thus, to play it safe, only strip the suffix for + // the standard sections. + if (!config->mingw) + return false; + if (!sc || !sc->isCOMDAT()) + return false; + return name.startswith(".text$") || name.startswith(".data$") || + name.startswith(".rdata$") || name.startswith(".pdata$") || + name.startswith(".xdata$") || name.startswith(".eh_frame$"); +} + // Create output section objects and add them to OutputSections. void Writer::createSections() { // First, create the builtin sections. @@ -807,10 +830,7 @@ void Writer::createSections() { continue; } StringRef name = c->getSectionName(); - // On MinGW, comdat groups are formed by putting the comdat group name - // after the '$' in the section name. Such a section name suffix shouldn't - // imply separate alphabetical sorting of those section chunks though. - if (config->mingw && sc && sc->isCOMDAT()) + if (shouldStripSectionSuffix(sc, name)) name = name.split('$').first; PartialSection *pSec = createPartialSection(name, c->getOutputCharacteristics()); @@ -818,6 +838,7 @@ void Writer::createSections() { } fixPartialSectionChars(".rsrc", data | r); + fixPartialSectionChars(".edata", data | r); // Even in non MinGW cases, we might need to link against GNU import // libraries. bool hasIdata = fixGnuImportChunks(); @@ -992,10 +1013,19 @@ void Writer::appendImportThunks() { } void Writer::createExportTable() { - if (config->exports.empty()) - return; - for (Chunk *c : edata.chunks) - edataSec->addChunk(c); + if (!edataSec->chunks.empty()) { + // Allow using a custom built export table from input object files, instead + // of having the linker synthesize the tables. + if (config->hadExplicitExports) + warn("literal .edata sections override exports"); + } else if (!config->exports.empty()) { + for (Chunk *c : edata.chunks) + edataSec->addChunk(c); + } + if (!edataSec->chunks.empty()) { + edataStart = edataSec->chunks.front(); + edataEnd = edataSec->chunks.back(); + } } void Writer::removeUnusedSections() { @@ -1076,6 +1106,13 @@ Optional<coff_symbol16> Writer::createSymbol(Defined *def) { } } + // Symbols that are runtime pseudo relocations don't point to the actual + // symbol data itself (as they are imported), but points to the IAT entry + // instead. Avoid emitting them to the symbol table, as they can confuse + // debuggers. + if (def->isRuntimePseudoReloc) + return None; + StringRef name = def->getName(); if (name.size() > COFF::NameSize) { sym.Name.Offset.Zeroes = 0; @@ -1179,9 +1216,11 @@ void Writer::assignAddresses() { sizeOfHeaders += config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header); sizeOfHeaders = alignTo(sizeOfHeaders, config->fileAlign); - uint64_t rva = pageSize; // The first page is kept unmapped. fileSize = sizeOfHeaders; + // The first page is kept unmapped. + uint64_t rva = alignTo(sizeOfHeaders, config->align); + for (OutputSection *sec : outputSections) { if (sec == relocSec) addBaserels(); @@ -1211,10 +1250,10 @@ void Writer::assignAddresses() { sec->header.SizeOfRawData = rawSize; if (rawSize != 0) sec->header.PointerToRawData = fileSize; - rva += alignTo(virtualSize, pageSize); + rva += alignTo(virtualSize, config->align); fileSize += alignTo(rawSize, config->fileAlign); } - sizeOfImage = alignTo(rva, pageSize); + sizeOfImage = alignTo(rva, config->align); // Assign addresses to sections in MergeChunks. for (MergeChunk *mc : MergeChunk::instances) @@ -1283,7 +1322,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() { pe->MinorLinkerVersion = 0; pe->ImageBase = config->imageBase; - pe->SectionAlignment = pageSize; + pe->SectionAlignment = config->align; pe->FileAlignment = config->fileAlign; pe->MajorImageVersion = config->majorImageVersion; pe->MinorImageVersion = config->minorImageVersion; @@ -1335,9 +1374,10 @@ template <typename PEHeaderTy> void Writer::writeHeader() { // Write data directory auto *dir = reinterpret_cast<data_directory *>(buf); buf += sizeof(*dir) * numberOfDataDirectory; - if (!config->exports.empty()) { - dir[EXPORT_TABLE].RelativeVirtualAddress = edata.getRVA(); - dir[EXPORT_TABLE].Size = edata.getSize(); + if (edataStart) { + dir[EXPORT_TABLE].RelativeVirtualAddress = edataStart->getRVA(); + dir[EXPORT_TABLE].Size = + edataEnd->getRVA() + edataEnd->getSize() - edataStart->getRVA(); } if (importTableStart) { dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA(); @@ -1475,7 +1515,8 @@ static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms, // Absolute is never code, synthetic generally isn't and usually isn't // determinable. break; - case Symbol::LazyKind: + case Symbol::LazyArchiveKind: + case Symbol::LazyObjectKind: case Symbol::UndefinedKind: // Undefined symbols resolve to zero, so they don't have an RVA. Lazy // symbols shouldn't have relocations. @@ -1899,3 +1940,6 @@ PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) { return it->second; return nullptr; } + +} // namespace coff +} // namespace lld |