Index: vendor/lld/dist/COFF/Config.h =================================================================== --- vendor/lld/dist/COFF/Config.h (revision 327307) +++ vendor/lld/dist/COFF/Config.h (revision 327308) @@ -1,184 +1,185 @@ //===- Config.h -------------------------------------------------*- C++ -*-===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLD_COFF_CONFIG_H #define LLD_COFF_CONFIG_H #include "llvm/ADT/StringRef.h" #include "llvm/Object/COFF.h" #include "llvm/Support/CachePruning.h" #include #include #include #include namespace lld { namespace coff { using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN; using llvm::COFF::WindowsSubsystem; using llvm::StringRef; class DefinedAbsolute; class DefinedRelative; class StringChunk; class Symbol; // Short aliases. static const auto AMD64 = llvm::COFF::IMAGE_FILE_MACHINE_AMD64; static const auto ARM64 = llvm::COFF::IMAGE_FILE_MACHINE_ARM64; static const auto ARMNT = llvm::COFF::IMAGE_FILE_MACHINE_ARMNT; static const auto I386 = llvm::COFF::IMAGE_FILE_MACHINE_I386; // Represents an /export option. struct Export { StringRef Name; // N in /export:N or /export:E=N StringRef ExtName; // E in /export:E=N Symbol *Sym = nullptr; uint16_t Ordinal = 0; bool Noname = false; bool Data = false; bool Private = false; bool Constant = false; // If an export is a form of /export:foo=dllname.bar, that means // that foo should be exported as an alias to bar in the DLL. // ForwardTo is set to "dllname.bar" part. Usually empty. StringRef ForwardTo; StringChunk *ForwardChunk = nullptr; // True if this /export option was in .drectves section. bool Directives = false; StringRef SymbolName; StringRef ExportName; // Name in DLL bool operator==(const Export &E) { return (Name == E.Name && ExtName == E.ExtName && Ordinal == E.Ordinal && Noname == E.Noname && Data == E.Data && Private == E.Private); } }; enum class DebugType { None = 0x0, CV = 0x1, /// CodeView PData = 0x2, /// Procedure Data Fixup = 0x4, /// Relocation Table }; // Global configuration. struct Configuration { enum ManifestKind { SideBySide, Embed, No }; bool is64() { return Machine == AMD64 || Machine == ARM64; } llvm::COFF::MachineTypes Machine = IMAGE_FILE_MACHINE_UNKNOWN; bool Verbose = false; WindowsSubsystem Subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN; Symbol *Entry = nullptr; bool NoEntry = false; std::string OutputFile; std::string ImportName; bool DoGC = true; bool DoICF = true; bool Relocatable = true; bool Force = false; bool Debug = false; bool DebugDwarf = false; bool DebugGHashes = false; unsigned DebugTypes = static_cast(DebugType::None); llvm::SmallString<128> PDBPath; std::vector Argv; // Symbols in this set are considered as live by the garbage collector. std::vector GCRoot; std::set NoDefaultLibs; bool NoDefaultLibAll = false; // True if we are creating a DLL. bool DLL = false; StringRef Implib; std::vector Exports; std::set DelayLoads; std::map DLLOrder; Symbol *DelayLoadHelper = nullptr; bool SaveTemps = false; // Used for SafeSEH. Symbol *SEHTable = nullptr; Symbol *SEHCount = nullptr; // Used for /opt:lldlto=N unsigned LTOOptLevel = 2; // Used for /opt:lldltojobs=N unsigned LTOJobs = 0; // Used for /opt:lldltopartitions=N unsigned LTOPartitions = 1; // Used for /opt:lldltocache=path StringRef LTOCache; // Used for /opt:lldltocachepolicy=policy llvm::CachePruningPolicy LTOCachePolicy; // Used for /merge:from=to (e.g. /merge:.rdata=.text) std::map Merge; // Used for /section=.name,{DEKPRSW} to set section attributes. std::map Section; // Options for manifest files. ManifestKind Manifest = No; int ManifestID = 1; StringRef ManifestDependency; bool ManifestUAC = true; std::vector ManifestInput; StringRef ManifestLevel = "'asInvoker'"; StringRef ManifestUIAccess = "'false'"; StringRef ManifestFile; // Used for /aligncomm. std::map AlignComm; // Used for /failifmismatch. std::map MustMatch; // Used for /alternatename. std::map AlternateNames; // Used for /lldmap. std::string MapFile; uint64_t ImageBase = -1; uint64_t StackReserve = 1024 * 1024; uint64_t StackCommit = 4096; uint64_t HeapReserve = 1024 * 1024; uint64_t HeapCommit = 4096; uint32_t MajorImageVersion = 0; uint32_t MinorImageVersion = 0; uint32_t MajorOSVersion = 6; uint32_t MinorOSVersion = 0; bool CanExitEarly = false; bool DynamicBase = true; bool AllowBind = true; bool NxCompat = true; bool AllowIsolation = true; bool TerminalServerAware = true; bool LargeAddressAware = false; bool HighEntropyVA = false; bool AppContainer = false; bool MinGW = false; + bool WarnLocallyDefinedImported = true; }; extern Configuration *Config; } // namespace coff } // namespace lld #endif Index: vendor/lld/dist/COFF/Driver.cpp =================================================================== --- vendor/lld/dist/COFF/Driver.cpp (revision 327307) +++ vendor/lld/dist/COFF/Driver.cpp (revision 327308) @@ -1,1320 +1,1334 @@ //===- Driver.cpp ---------------------------------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "Driver.h" #include "Config.h" #include "InputFiles.h" #include "MinGW.h" #include "SymbolTable.h" #include "Symbols.h" #include "Writer.h" #include "lld/Common/Driver.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "lld/Common/Version.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/Magic.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/COFFImportFile.h" #include "llvm/Object/COFFModuleDefinition.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ToolDrivers/llvm-lib/LibDriver.h" #include #include #include using namespace llvm; using namespace llvm::object; using namespace llvm::COFF; using llvm::sys::Process; namespace lld { namespace coff { Configuration *Config; LinkerDriver *Driver; bool link(ArrayRef Args, bool CanExitEarly, raw_ostream &Diag) { errorHandler().LogName = 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)"; Config = make(); Config->Argv = {Args.begin(), Args.end()}; Config->CanExitEarly = CanExitEarly; Symtab = make(); Driver = make(); Driver->link(Args); // Call exit() if we can to avoid calling destructors. if (CanExitEarly) exitLld(errorCount() ? 1 : 0); freeArena(); return !errorCount(); } // Drop directory components and replace extension with ".exe" or ".dll". static std::string getOutputPath(StringRef Path) { auto P = Path.find_last_of("\\/"); StringRef S = (P == StringRef::npos) ? Path : Path.substr(P + 1); const char* E = Config->DLL ? ".dll" : ".exe"; return (S.substr(0, S.rfind('.')) + E).str(); } // ErrorOr is not default constructible, so it cannot be used as the type // parameter of a future. // FIXME: We could open the file in createFutureForFile and avoid needing to // return an error here, but for the moment that would cost us a file descriptor // (a limited resource on Windows) for the duration that the future is pending. typedef std::pair, std::error_code> MBErrPair; // Create a std::future that opens and maps a file using the best strategy for // the host platform. static std::future createFutureForFile(std::string Path) { #if LLVM_ON_WIN32 // On Windows, file I/O is relatively slow so it is best to do this // asynchronously. auto Strategy = std::launch::async; #else auto Strategy = std::launch::deferred; #endif return std::async(Strategy, [=]() { auto MBOrErr = MemoryBuffer::getFile(Path); if (!MBOrErr) return MBErrPair{nullptr, MBOrErr.getError()}; return MBErrPair{std::move(*MBOrErr), std::error_code()}; }); } MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr MB) { MemoryBufferRef MBRef = *MB; make>(std::move(MB)); // take ownership if (Driver->Tar) Driver->Tar->append(relativeToRoot(MBRef.getBufferIdentifier()), MBRef.getBuffer()); return MBRef; } void LinkerDriver::addBuffer(std::unique_ptr MB, bool WholeArchive) { MemoryBufferRef MBRef = takeBuffer(std::move(MB)); FilePaths.push_back(MBRef.getBufferIdentifier()); // File type is detected by contents, not by file extension. switch (identify_magic(MBRef.getBuffer())) { case file_magic::windows_resource: Resources.push_back(MBRef); break; case file_magic::archive: if (WholeArchive) { std::unique_ptr File = CHECK(Archive::create(MBRef), MBRef.getBufferIdentifier() + ": failed to parse archive"); for (MemoryBufferRef M : getArchiveMembers(File.get())) addArchiveBuffer(M, "", MBRef.getBufferIdentifier()); return; } Symtab->addFile(make(MBRef)); break; case file_magic::bitcode: Symtab->addFile(make(MBRef)); break; case file_magic::coff_cl_gl_object: error(MBRef.getBufferIdentifier() + ": is not a native COFF file. " "Recompile without /GL"); break; default: Symtab->addFile(make(MBRef)); break; } } void LinkerDriver::enqueuePath(StringRef Path, bool WholeArchive) { auto Future = std::make_shared>(createFutureForFile(Path)); std::string PathStr = Path; enqueueTask([=]() { auto MBOrErr = Future->get(); if (MBOrErr.second) error("could not open " + PathStr + ": " + MBOrErr.second.message()); else Driver->addBuffer(std::move(MBOrErr.first), WholeArchive); }); } void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName, StringRef ParentName) { file_magic Magic = identify_magic(MB.getBuffer()); if (Magic == file_magic::coff_import_library) { Symtab->addFile(make(MB)); return; } InputFile *Obj; if (Magic == file_magic::coff_object) { Obj = make(MB); } else if (Magic == file_magic::bitcode) { Obj = make(MB); } else { error("unknown file type: " + MB.getBufferIdentifier()); return; } Obj->ParentName = ParentName; Symtab->addFile(Obj); log("Loaded " + toString(Obj) + " for " + SymName); } void LinkerDriver::enqueueArchiveMember(const Archive::Child &C, StringRef SymName, StringRef ParentName) { if (!C.getParent()->isThin()) { MemoryBufferRef MB = CHECK( C.getMemoryBufferRef(), "could not get the buffer for the member defining symbol " + SymName); enqueueTask([=]() { Driver->addArchiveBuffer(MB, SymName, ParentName); }); return; } auto Future = std::make_shared>(createFutureForFile( CHECK(C.getFullName(), "could not get the filename for the member defining symbol " + SymName))); enqueueTask([=]() { auto MBOrErr = Future->get(); if (MBOrErr.second) fatal("could not get the buffer for the member defining " + SymName + ": " + MBOrErr.second.message()); Driver->addArchiveBuffer(takeBuffer(std::move(MBOrErr.first)), SymName, ParentName); }); } static bool isDecorated(StringRef Sym) { return Sym.startswith("@") || Sym.contains("@@") || Sym.startswith("?") || (!Config->MinGW && Sym.contains('@')); } // Parses .drectve section contents and returns a list of files // specified by /defaultlib. void LinkerDriver::parseDirectives(StringRef S) { ArgParser Parser; // .drectve is always tokenized using Windows shell rules. - opt::InputArgList Args = Parser.parse(S); + opt::InputArgList Args = Parser.parseDirectives(S); for (auto *Arg : Args) { switch (Arg->getOption().getUnaliasedOption().getID()) { case OPT_aligncomm: parseAligncomm(Arg->getValue()); break; case OPT_alternatename: parseAlternateName(Arg->getValue()); break; case OPT_defaultlib: if (Optional Path = findLib(Arg->getValue())) enqueuePath(*Path, false); break; case OPT_entry: Config->Entry = addUndefined(mangle(Arg->getValue())); break; case OPT_export: { + // If a common header file contains dllexported function + // declarations, many object files may end up with having the + // same /EXPORT options. In order to save cost of parsing them, + // we dedup them first. + if (!DirectivesExports.insert(Arg->getValue()).second) + break; + Export E = parseExport(Arg->getValue()); if (Config->Machine == I386 && Config->MinGW) { if (!isDecorated(E.Name)) E.Name = Saver.save("_" + E.Name); if (!E.ExtName.empty() && !isDecorated(E.ExtName)) E.ExtName = Saver.save("_" + E.ExtName); } E.Directives = true; Config->Exports.push_back(E); break; } case OPT_failifmismatch: checkFailIfMismatch(Arg->getValue()); break; case OPT_incl: addUndefined(Arg->getValue()); break; case OPT_merge: parseMerge(Arg->getValue()); break; case OPT_nodefaultlib: Config->NoDefaultLibs.insert(doFindLib(Arg->getValue())); break; case OPT_section: parseSection(Arg->getValue()); break; case OPT_subsystem: parseSubsystem(Arg->getValue(), &Config->Subsystem, &Config->MajorOSVersion, &Config->MinorOSVersion); break; case OPT_editandcontinue: case OPT_fastfail: case OPT_guardsym: case OPT_natvis: case OPT_throwingnew: break; default: error(Arg->getSpelling() + " is not allowed in .drectve"); } } } // Find file from search paths. You can omit ".obj", this function takes // care of that. Note that the returned path is not guaranteed to exist. StringRef LinkerDriver::doFindFile(StringRef Filename) { bool HasPathSep = (Filename.find_first_of("/\\") != StringRef::npos); if (HasPathSep) return Filename; bool HasExt = Filename.contains('.'); for (StringRef Dir : SearchPaths) { SmallString<128> Path = Dir; sys::path::append(Path, Filename); if (sys::fs::exists(Path.str())) return Saver.save(Path.str()); if (!HasExt) { Path.append(".obj"); if (sys::fs::exists(Path.str())) return Saver.save(Path.str()); } } return Filename; } // Resolves a file path. This never returns the same path // (in that case, it returns None). Optional LinkerDriver::findFile(StringRef Filename) { StringRef Path = doFindFile(Filename); bool Seen = !VisitedFiles.insert(Path.lower()).second; if (Seen) return None; if (Path.endswith_lower(".lib")) VisitedLibs.insert(sys::path::filename(Path)); return Path; } // Find library file from search path. StringRef LinkerDriver::doFindLib(StringRef Filename) { // Add ".lib" to Filename if that has no file extension. bool HasExt = Filename.contains('.'); if (!HasExt) Filename = Saver.save(Filename + ".lib"); return doFindFile(Filename); } // Resolves a library path. /nodefaultlib options are taken into // consideration. This never returns the same path (in that case, // it returns None). Optional LinkerDriver::findLib(StringRef Filename) { if (Config->NoDefaultLibAll) return None; if (!VisitedLibs.insert(Filename.lower()).second) return None; StringRef Path = doFindLib(Filename); if (Config->NoDefaultLibs.count(Path)) return None; if (!VisitedFiles.insert(Path.lower()).second) return None; return Path; } // Parses LIB environment which contains a list of search paths. void LinkerDriver::addLibSearchPaths() { Optional EnvOpt = Process::GetEnv("LIB"); if (!EnvOpt.hasValue()) return; StringRef Env = Saver.save(*EnvOpt); while (!Env.empty()) { StringRef Path; std::tie(Path, Env) = Env.split(';'); SearchPaths.push_back(Path); } } Symbol *LinkerDriver::addUndefined(StringRef Name) { Symbol *B = Symtab->addUndefined(Name); if (!B->IsGCRoot) { B->IsGCRoot = true; Config->GCRoot.push_back(B); } return B; } // Symbol names are mangled by appending "_" prefix on x86. StringRef LinkerDriver::mangle(StringRef Sym) { assert(Config->Machine != IMAGE_FILE_MACHINE_UNKNOWN); if (Config->Machine == I386) return Saver.save("_" + Sym); return Sym; } // Windows specific -- find default entry point name. StringRef LinkerDriver::findDefaultEntry() { // User-defined main functions and their corresponding entry points. static const char *Entries[][2] = { {"main", "mainCRTStartup"}, {"wmain", "wmainCRTStartup"}, {"WinMain", "WinMainCRTStartup"}, {"wWinMain", "wWinMainCRTStartup"}, }; for (auto E : Entries) { StringRef Entry = Symtab->findMangle(mangle(E[0])); if (!Entry.empty() && !isa(Symtab->find(Entry))) return mangle(E[1]); } return ""; } WindowsSubsystem LinkerDriver::inferSubsystem() { if (Config->DLL) return IMAGE_SUBSYSTEM_WINDOWS_GUI; if (Symtab->findUnderscore("main") || Symtab->findUnderscore("wmain")) return IMAGE_SUBSYSTEM_WINDOWS_CUI; if (Symtab->findUnderscore("WinMain") || Symtab->findUnderscore("wWinMain")) return IMAGE_SUBSYSTEM_WINDOWS_GUI; return IMAGE_SUBSYSTEM_UNKNOWN; } static uint64_t getDefaultImageBase() { if (Config->is64()) return Config->DLL ? 0x180000000 : 0x140000000; return Config->DLL ? 0x10000000 : 0x400000; } static std::string createResponseFile(const opt::InputArgList &Args, ArrayRef FilePaths, ArrayRef SearchPaths) { SmallString<0> Data; raw_svector_ostream OS(Data); for (auto *Arg : Args) { switch (Arg->getOption().getID()) { case OPT_linkrepro: case OPT_INPUT: case OPT_defaultlib: case OPT_libpath: case OPT_manifest: case OPT_manifest_colon: case OPT_manifestdependency: case OPT_manifestfile: case OPT_manifestinput: case OPT_manifestuac: break; default: OS << toString(*Arg) << "\n"; } } for (StringRef Path : SearchPaths) { std::string RelPath = relativeToRoot(Path); OS << "/libpath:" << quote(RelPath) << "\n"; } for (StringRef Path : FilePaths) OS << quote(relativeToRoot(Path)) << "\n"; return Data.str(); } static unsigned getDefaultDebugType(const opt::InputArgList &Args) { unsigned DebugTypes = static_cast(DebugType::CV); if (Args.hasArg(OPT_driver)) DebugTypes |= static_cast(DebugType::PData); if (Args.hasArg(OPT_profile)) DebugTypes |= static_cast(DebugType::Fixup); return DebugTypes; } static unsigned parseDebugType(StringRef Arg) { SmallVector Types; Arg.split(Types, ',', /*KeepEmpty=*/false); unsigned DebugTypes = static_cast(DebugType::None); for (StringRef Type : Types) DebugTypes |= StringSwitch(Type.lower()) .Case("cv", static_cast(DebugType::CV)) .Case("pdata", static_cast(DebugType::PData)) .Case("fixup", static_cast(DebugType::Fixup)) .Default(0); return DebugTypes; } static std::string getMapFile(const opt::InputArgList &Args) { auto *Arg = Args.getLastArg(OPT_lldmap, OPT_lldmap_file); if (!Arg) return ""; if (Arg->getOption().getID() == OPT_lldmap_file) return Arg->getValue(); assert(Arg->getOption().getID() == OPT_lldmap); StringRef OutFile = Config->OutputFile; return (OutFile.substr(0, OutFile.rfind('.')) + ".map").str(); } static std::string getImplibPath() { if (!Config->Implib.empty()) return Config->Implib; SmallString<128> Out = StringRef(Config->OutputFile); sys::path::replace_extension(Out, ".lib"); return Out.str(); } // // The import name is caculated as the following: // // | LIBRARY w/ ext | LIBRARY w/o ext | no LIBRARY // -----+----------------+---------------------+------------------ // LINK | {value} | {value}.{.dll/.exe} | {output name} // LIB | {value} | {value}.dll | {output name}.dll // static std::string getImportName(bool AsLib) { SmallString<128> Out; if (Config->ImportName.empty()) { Out.assign(sys::path::filename(Config->OutputFile)); if (AsLib) sys::path::replace_extension(Out, ".dll"); } else { Out.assign(Config->ImportName); if (!sys::path::has_extension(Out)) sys::path::replace_extension(Out, (Config->DLL || AsLib) ? ".dll" : ".exe"); } return Out.str(); } static void createImportLibrary(bool AsLib) { std::vector Exports; for (Export &E1 : Config->Exports) { COFFShortExport E2; E2.Name = E1.Name; E2.SymbolName = E1.SymbolName; E2.ExtName = E1.ExtName; E2.Ordinal = E1.Ordinal; E2.Noname = E1.Noname; E2.Data = E1.Data; E2.Private = E1.Private; E2.Constant = E1.Constant; Exports.push_back(E2); } auto E = writeImportLibrary(getImportName(AsLib), getImplibPath(), Exports, Config->Machine, false); handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) { error(EIB.message()); }); } static void parseModuleDefs(StringRef Path) { std::unique_ptr MB = CHECK( MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path); COFFModuleDefinition M = check(parseCOFFModuleDefinition( MB->getMemBufferRef(), Config->Machine, Config->MinGW)); if (Config->OutputFile.empty()) Config->OutputFile = Saver.save(M.OutputFile); Config->ImportName = Saver.save(M.ImportName); if (M.ImageBase) Config->ImageBase = M.ImageBase; if (M.StackReserve) Config->StackReserve = M.StackReserve; if (M.StackCommit) Config->StackCommit = M.StackCommit; if (M.HeapReserve) Config->HeapReserve = M.HeapReserve; if (M.HeapCommit) Config->HeapCommit = M.HeapCommit; if (M.MajorImageVersion) Config->MajorImageVersion = M.MajorImageVersion; if (M.MinorImageVersion) Config->MinorImageVersion = M.MinorImageVersion; if (M.MajorOSVersion) Config->MajorOSVersion = M.MajorOSVersion; if (M.MinorOSVersion) Config->MinorOSVersion = M.MinorOSVersion; for (COFFShortExport E1 : M.Exports) { Export E2; E2.Name = Saver.save(E1.Name); if (E1.isWeak()) E2.ExtName = Saver.save(E1.ExtName); E2.Ordinal = E1.Ordinal; E2.Noname = E1.Noname; E2.Data = E1.Data; E2.Private = E1.Private; E2.Constant = E1.Constant; Config->Exports.push_back(E2); } } // A helper function for filterBitcodeFiles. static bool needsRebuilding(MemoryBufferRef MB) { // The MSVC linker doesn't support thin archives, so if it's a thin // archive, we always need to rebuild it. std::unique_ptr File = CHECK(Archive::create(MB), "Failed to read " + MB.getBufferIdentifier()); if (File->isThin()) return true; // Returns true if the archive contains at least one bitcode file. for (MemoryBufferRef Member : getArchiveMembers(File.get())) if (identify_magic(Member.getBuffer()) == file_magic::bitcode) return true; return false; } // Opens a given path as an archive file and removes bitcode files // from them if exists. This function is to appease the MSVC linker as // their linker doesn't like archive files containing non-native // object files. // // If a given archive doesn't contain bitcode files, the archive path // is returned as-is. Otherwise, a new temporary file is created and // its path is returned. static Optional filterBitcodeFiles(StringRef Path, std::vector &TemporaryFiles) { std::unique_ptr MB = CHECK( MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path); MemoryBufferRef MBRef = MB->getMemBufferRef(); file_magic Magic = identify_magic(MBRef.getBuffer()); if (Magic == file_magic::bitcode) return None; if (Magic != file_magic::archive) return Path.str(); if (!needsRebuilding(MBRef)) return Path.str(); std::unique_ptr File = CHECK(Archive::create(MBRef), MBRef.getBufferIdentifier() + ": failed to parse archive"); std::vector New; for (MemoryBufferRef Member : getArchiveMembers(File.get())) if (identify_magic(Member.getBuffer()) != file_magic::bitcode) New.emplace_back(Member); if (New.empty()) return None; log("Creating a temporary archive for " + Path + " to remove bitcode files"); SmallString<128> S; if (auto EC = sys::fs::createTemporaryFile("lld-" + sys::path::stem(Path), ".lib", S)) fatal("cannot create a temporary file: " + EC.message()); std::string Temp = S.str(); TemporaryFiles.push_back(Temp); Error E = llvm::writeArchive(Temp, New, /*WriteSymtab=*/true, Archive::Kind::K_GNU, /*Deterministics=*/true, /*Thin=*/false); handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) { error("failed to create a new archive " + S.str() + ": " + EI.message()); }); return Temp; } // Create response file contents and invoke the MSVC linker. void LinkerDriver::invokeMSVC(opt::InputArgList &Args) { std::string Rsp = "/nologo\n"; std::vector Temps; // Write out archive members that we used in symbol resolution and pass these // to MSVC before any archives, so that MSVC uses the same objects to satisfy // references. for (ObjFile *Obj : ObjFile::Instances) { if (Obj->ParentName.empty()) continue; SmallString<128> S; int Fd; if (auto EC = sys::fs::createTemporaryFile( "lld-" + sys::path::filename(Obj->ParentName), ".obj", Fd, S)) fatal("cannot create a temporary file: " + EC.message()); raw_fd_ostream OS(Fd, /*shouldClose*/ true); OS << Obj->MB.getBuffer(); Temps.push_back(S.str()); Rsp += quote(S) + "\n"; } for (auto *Arg : Args) { switch (Arg->getOption().getID()) { case OPT_linkrepro: case OPT_lldmap: case OPT_lldmap_file: case OPT_lldsavetemps: case OPT_msvclto: // LLD-specific options are stripped. break; case OPT_opt: if (!StringRef(Arg->getValue()).startswith("lld")) Rsp += toString(*Arg) + " "; break; case OPT_INPUT: { if (Optional Path = doFindFile(Arg->getValue())) { if (Optional S = filterBitcodeFiles(*Path, Temps)) Rsp += quote(*S) + "\n"; continue; } Rsp += quote(Arg->getValue()) + "\n"; break; } default: Rsp += toString(*Arg) + "\n"; } } std::vector ObjFiles = Symtab->compileBitcodeFiles(); runMSVCLinker(Rsp, ObjFiles); for (StringRef Path : Temps) sys::fs::remove(Path); } void LinkerDriver::enqueueTask(std::function Task) { TaskQueue.push_back(std::move(Task)); } bool LinkerDriver::run() { bool DidWork = !TaskQueue.empty(); while (!TaskQueue.empty()) { TaskQueue.front()(); TaskQueue.pop_front(); } return DidWork; } void LinkerDriver::link(ArrayRef ArgsArr) { // If the first command line argument is "/lib", link.exe acts like lib.exe. // We call our own implementation of lib.exe that understands bitcode files. if (ArgsArr.size() > 1 && StringRef(ArgsArr[1]).equals_lower("/lib")) { if (llvm::libDriverMain(ArgsArr.slice(1)) != 0) fatal("lib failed"); return; } // Needed for LTO. InitializeAllTargetInfos(); InitializeAllTargets(); InitializeAllTargetMCs(); InitializeAllAsmParsers(); InitializeAllAsmPrinters(); InitializeAllDisassemblers(); // Parse command line options. ArgParser Parser; opt::InputArgList Args = Parser.parseLINK(ArgsArr.slice(1)); // Parse and evaluate -mllvm options. std::vector V; V.push_back("lld-link (LLVM option parsing)"); for (auto *Arg : Args.filtered(OPT_mllvm)) V.push_back(Arg->getValue()); cl::ParseCommandLineOptions(V.size(), V.data()); // Handle /errorlimit early, because error() depends on it. if (auto *Arg = Args.getLastArg(OPT_errorlimit)) { int N = 20; StringRef S = Arg->getValue(); if (S.getAsInteger(10, N)) error(Arg->getSpelling() + " number expected, but got " + S); errorHandler().ErrorLimit = N; } // Handle /help if (Args.hasArg(OPT_help)) { printHelp(ArgsArr[0]); return; } // Handle --version, which is an lld extension. This option is a bit odd // because it doesn't start with "/", but we deliberately chose "--" to // avoid conflict with /version and for compatibility with clang-cl. if (Args.hasArg(OPT_dash_dash_version)) { outs() << getLLDVersion() << "\n"; return; } // Handle /lldmingw early, since it can potentially affect how other // 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"); Expected> ErrOrWriter = TarWriter::create(Path, "repro"); if (ErrOrWriter) { Tar = std::move(*ErrOrWriter); } else { error("/linkrepro: failed to open " + Path + ": " + toString(ErrOrWriter.takeError())); } } if (!Args.hasArg(OPT_INPUT)) { if (Args.hasArg(OPT_deffile)) Config->NoEntry = true; else fatal("no input files"); } // Construct search path list. SearchPaths.push_back(""); for (auto *Arg : Args.filtered(OPT_libpath)) SearchPaths.push_back(Arg->getValue()); addLibSearchPaths(); + + // Handle /ignore + for (auto *Arg : Args.filtered(OPT_ignore)) { + if (StringRef(Arg->getValue()) == "4217") + Config->WarnLocallyDefinedImported = false; + // Other warning numbers are ignored. + } // Handle /out if (auto *Arg = Args.getLastArg(OPT_out)) Config->OutputFile = Arg->getValue(); // Handle /verbose if (Args.hasArg(OPT_verbose)) Config->Verbose = true; errorHandler().Verbose = Config->Verbose; // Handle /force or /force:unresolved if (Args.hasArg(OPT_force, OPT_force_unresolved)) Config->Force = true; // Handle /debug if (Args.hasArg(OPT_debug, OPT_debug_dwarf, OPT_debug_ghash)) { Config->Debug = true; if (auto *Arg = Args.getLastArg(OPT_debugtype)) Config->DebugTypes = parseDebugType(Arg->getValue()); else Config->DebugTypes = getDefaultDebugType(Args); } // Handle /pdb bool ShouldCreatePDB = Args.hasArg(OPT_debug, OPT_debug_ghash); if (ShouldCreatePDB) if (auto *Arg = Args.getLastArg(OPT_pdb)) Config->PDBPath = Arg->getValue(); // Handle /noentry if (Args.hasArg(OPT_noentry)) { if (Args.hasArg(OPT_dll)) Config->NoEntry = true; else error("/noentry must be specified with /dll"); } // Handle /dll if (Args.hasArg(OPT_dll)) { Config->DLL = true; Config->ManifestID = 2; } // Handle /dynamicbase and /fixed. We can't use hasFlag for /dynamicbase // because we need to explicitly check whether that option or its inverse was // present in the argument list in order to handle /fixed. auto *DynamicBaseArg = Args.getLastArg(OPT_dynamicbase, OPT_dynamicbase_no); if (DynamicBaseArg && DynamicBaseArg->getOption().getID() == OPT_dynamicbase_no) Config->DynamicBase = false; bool Fixed = Args.hasFlag(OPT_fixed, OPT_fixed_no, false); if (Fixed) { if (DynamicBaseArg && DynamicBaseArg->getOption().getID() == OPT_dynamicbase) { error("/fixed must not be specified with /dynamicbase"); } else { Config->Relocatable = false; Config->DynamicBase = false; } } // Handle /appcontainer Config->AppContainer = Args.hasFlag(OPT_appcontainer, OPT_appcontainer_no, false); // Handle /machine if (auto *Arg = Args.getLastArg(OPT_machine)) Config->Machine = getMachineType(Arg->getValue()); // Handle /nodefaultlib: for (auto *Arg : Args.filtered(OPT_nodefaultlib)) Config->NoDefaultLibs.insert(doFindLib(Arg->getValue())); // Handle /nodefaultlib if (Args.hasArg(OPT_nodefaultlib_all)) Config->NoDefaultLibAll = true; // Handle /base if (auto *Arg = Args.getLastArg(OPT_base)) parseNumbers(Arg->getValue(), &Config->ImageBase); // Handle /stack if (auto *Arg = Args.getLastArg(OPT_stack)) parseNumbers(Arg->getValue(), &Config->StackReserve, &Config->StackCommit); // Handle /heap if (auto *Arg = Args.getLastArg(OPT_heap)) parseNumbers(Arg->getValue(), &Config->HeapReserve, &Config->HeapCommit); // Handle /version if (auto *Arg = Args.getLastArg(OPT_version)) parseVersion(Arg->getValue(), &Config->MajorImageVersion, &Config->MinorImageVersion); // Handle /subsystem if (auto *Arg = Args.getLastArg(OPT_subsystem)) parseSubsystem(Arg->getValue(), &Config->Subsystem, &Config->MajorOSVersion, &Config->MinorOSVersion); // Handle /alternatename for (auto *Arg : Args.filtered(OPT_alternatename)) parseAlternateName(Arg->getValue()); // Handle /include for (auto *Arg : Args.filtered(OPT_incl)) addUndefined(Arg->getValue()); // Handle /implib if (auto *Arg = Args.getLastArg(OPT_implib)) Config->Implib = Arg->getValue(); // Handle /opt. bool DoGC = !Args.hasArg(OPT_debug); unsigned ICFLevel = 1; // 0: off, 1: limited, 2: on for (auto *Arg : Args.filtered(OPT_opt)) { std::string Str = StringRef(Arg->getValue()).lower(); SmallVector Vec; StringRef(Str).split(Vec, ','); for (StringRef S : Vec) { if (S == "ref") { DoGC = true; } else if (S == "noref") { DoGC = false; } else if (S == "icf" || S.startswith("icf=")) { ICFLevel = 2; } else if (S == "noicf") { ICFLevel = 0; } else if (S.startswith("lldlto=")) { StringRef OptLevel = S.substr(7); if (OptLevel.getAsInteger(10, Config->LTOOptLevel) || Config->LTOOptLevel > 3) error("/opt:lldlto: invalid optimization level: " + OptLevel); } else if (S.startswith("lldltojobs=")) { StringRef Jobs = S.substr(11); if (Jobs.getAsInteger(10, Config->LTOJobs) || Config->LTOJobs == 0) error("/opt:lldltojobs: invalid job count: " + Jobs); } else if (S.startswith("lldltopartitions=")) { StringRef N = S.substr(17); if (N.getAsInteger(10, Config->LTOPartitions) || Config->LTOPartitions == 0) error("/opt:lldltopartitions: invalid partition count: " + N); } else if (S != "lbr" && S != "nolbr") error("/opt: unknown option: " + S); } } // Limited ICF is enabled if GC is enabled and ICF was never mentioned // explicitly. // FIXME: LLD only implements "limited" ICF, i.e. it only merges identical // code. If the user passes /OPT:ICF explicitly, LLD should merge identical // comdat readonly data. if (ICFLevel == 1 && !DoGC) ICFLevel = 0; Config->DoGC = DoGC; Config->DoICF = ICFLevel > 0; // Handle /lldsavetemps if (Args.hasArg(OPT_lldsavetemps)) Config->SaveTemps = true; // Handle /lldltocache if (auto *Arg = Args.getLastArg(OPT_lldltocache)) Config->LTOCache = Arg->getValue(); // Handle /lldsavecachepolicy if (auto *Arg = Args.getLastArg(OPT_lldltocachepolicy)) Config->LTOCachePolicy = CHECK( parseCachePruningPolicy(Arg->getValue()), Twine("/lldltocachepolicy: invalid cache policy: ") + Arg->getValue()); // Handle /failifmismatch for (auto *Arg : Args.filtered(OPT_failifmismatch)) checkFailIfMismatch(Arg->getValue()); // Handle /merge for (auto *Arg : Args.filtered(OPT_merge)) parseMerge(Arg->getValue()); // Handle /section for (auto *Arg : Args.filtered(OPT_section)) parseSection(Arg->getValue()); // Handle /aligncomm for (auto *Arg : Args.filtered(OPT_aligncomm)) parseAligncomm(Arg->getValue()); // Handle /manifestdependency. This enables /manifest unless /manifest:no is // also passed. if (auto *Arg = Args.getLastArg(OPT_manifestdependency)) { Config->ManifestDependency = Arg->getValue(); Config->Manifest = Configuration::SideBySide; } // Handle /manifest and /manifest: if (auto *Arg = Args.getLastArg(OPT_manifest, OPT_manifest_colon)) { if (Arg->getOption().getID() == OPT_manifest) Config->Manifest = Configuration::SideBySide; else parseManifest(Arg->getValue()); } // Handle /manifestuac if (auto *Arg = Args.getLastArg(OPT_manifestuac)) parseManifestUAC(Arg->getValue()); // Handle /manifestfile if (auto *Arg = Args.getLastArg(OPT_manifestfile)) Config->ManifestFile = Arg->getValue(); // Handle /manifestinput for (auto *Arg : Args.filtered(OPT_manifestinput)) Config->ManifestInput.push_back(Arg->getValue()); if (!Config->ManifestInput.empty() && Config->Manifest != Configuration::Embed) { fatal("/MANIFESTINPUT: requires /MANIFEST:EMBED"); } // Handle miscellaneous boolean flags. Config->AllowBind = Args.hasFlag(OPT_allowbind, OPT_allowbind_no, true); Config->AllowIsolation = Args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true); Config->NxCompat = Args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true); Config->TerminalServerAware = Args.hasFlag(OPT_tsaware, OPT_tsaware_no, true); Config->DebugDwarf = Args.hasArg(OPT_debug_dwarf); Config->DebugGHashes = Args.hasArg(OPT_debug_ghash); Config->MapFile = getMapFile(Args); if (errorCount()) return; bool WholeArchiveFlag = Args.hasArg(OPT_wholearchive_flag); // Create a list of input files. Files can be given as arguments // for /defaultlib option. std::vector MBs; for (auto *Arg : Args.filtered(OPT_INPUT, OPT_wholearchive_file)) { switch (Arg->getOption().getID()) { case OPT_INPUT: if (Optional Path = findFile(Arg->getValue())) enqueuePath(*Path, WholeArchiveFlag); break; case OPT_wholearchive_file: if (Optional Path = findFile(Arg->getValue())) enqueuePath(*Path, true); break; } } for (auto *Arg : Args.filtered(OPT_defaultlib)) if (Optional Path = findLib(Arg->getValue())) enqueuePath(*Path, false); // Windows specific -- Create a resource file containing a manifest file. if (Config->Manifest == Configuration::Embed) addBuffer(createManifestRes(), false); // Read all input files given via the command line. run(); // We should have inferred a machine type by now from the input files, but if // not we assume x64. if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) { warn("/machine is not specified. x64 is assumed"); Config->Machine = AMD64; } // 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(convertResToCOFF(Resources))); if (Tar) Tar->append("response.txt", createResponseFile(Args, FilePaths, ArrayRef(SearchPaths).slice(1))); // Handle /largeaddressaware Config->LargeAddressAware = Args.hasFlag( OPT_largeaddressaware, OPT_largeaddressaware_no, Config->is64()); // Handle /highentropyva Config->HighEntropyVA = Config->is64() && Args.hasFlag(OPT_highentropyva, OPT_highentropyva_no, true); if (!Config->DynamicBase && (Config->Machine == ARMNT || Config->Machine == ARM64)) error("/dynamicbase:no is not compatible with " + machineToStr(Config->Machine)); // Handle /entry and /dll if (auto *Arg = Args.getLastArg(OPT_entry)) { Config->Entry = addUndefined(mangle(Arg->getValue())); } else if (!Config->Entry && !Config->NoEntry) { if (Args.hasArg(OPT_dll)) { StringRef S = (Config->Machine == I386) ? "__DllMainCRTStartup@12" : "_DllMainCRTStartup"; Config->Entry = addUndefined(S); } else { // Windows specific -- If entry point name is not given, we need to // infer that from user-defined entry name. StringRef S = findDefaultEntry(); if (S.empty()) fatal("entry point must be defined"); Config->Entry = addUndefined(S); log("Entry name inferred: " + S); } } // Handle /export for (auto *Arg : Args.filtered(OPT_export)) { Export E = parseExport(Arg->getValue()); if (Config->Machine == I386) { if (!isDecorated(E.Name)) E.Name = Saver.save("_" + E.Name); if (!E.ExtName.empty() && !isDecorated(E.ExtName)) E.ExtName = Saver.save("_" + E.ExtName); } Config->Exports.push_back(E); } // Handle /def if (auto *Arg = Args.getLastArg(OPT_deffile)) { // parseModuleDefs mutates Config object. parseModuleDefs(Arg->getValue()); } // Handle generation of import library from a def file. if (!Args.hasArg(OPT_INPUT)) { fixupExports(); createImportLibrary(/*AsLib=*/true); return; } // Handle /delayload for (auto *Arg : Args.filtered(OPT_delayload)) { Config->DelayLoads.insert(StringRef(Arg->getValue()).lower()); if (Config->Machine == I386) { Config->DelayLoadHelper = addUndefined("___delayLoadHelper2@8"); } else { Config->DelayLoadHelper = addUndefined("__delayLoadHelper2"); } } // Set default image name if neither /out or /def set it. if (Config->OutputFile.empty()) { Config->OutputFile = getOutputPath((*Args.filtered(OPT_INPUT).begin())->getValue()); } // Put the PDB next to the image if no /pdb flag was passed. if (ShouldCreatePDB && Config->PDBPath.empty()) { Config->PDBPath = Config->OutputFile; sys::path::replace_extension(Config->PDBPath, ".pdb"); } // Set default image base if /base is not given. if (Config->ImageBase == uint64_t(-1)) Config->ImageBase = getDefaultImageBase(); Symtab->addSynthetic(mangle("__ImageBase"), nullptr); if (Config->Machine == I386) { Symtab->addAbsolute("___safe_se_handler_table", 0); Symtab->addAbsolute("___safe_se_handler_count", 0); } // We do not support /guard:cf (control flow protection) yet. // Define CFG symbols anyway so that we can link MSVC 2015 CRT. Symtab->addAbsolute(mangle("__guard_fids_count"), 0); Symtab->addAbsolute(mangle("__guard_fids_table"), 0); Symtab->addAbsolute(mangle("__guard_flags"), 0x100); Symtab->addAbsolute(mangle("__guard_iat_count"), 0); Symtab->addAbsolute(mangle("__guard_iat_table"), 0); Symtab->addAbsolute(mangle("__guard_longjmp_count"), 0); Symtab->addAbsolute(mangle("__guard_longjmp_table"), 0); // Needed for MSVC 2017 15.5 CRT. Symtab->addAbsolute(mangle("__enclave_config"), 0); // This code may add new undefined symbols to the link, which may enqueue more // symbol resolution tasks, so we need to continue executing tasks until we // converge. do { // Windows specific -- if entry point is not found, // search for its mangled names. if (Config->Entry) Symtab->mangleMaybe(Config->Entry); // Windows specific -- Make sure we resolve all dllexported symbols. for (Export &E : Config->Exports) { if (!E.ForwardTo.empty()) continue; E.Sym = addUndefined(E.Name); if (!E.Directives) Symtab->mangleMaybe(E.Sym); } // Add weak aliases. Weak aliases is a mechanism to give remaining // undefined symbols final chance to be resolved successfully. for (auto Pair : Config->AlternateNames) { StringRef From = Pair.first; StringRef To = Pair.second; Symbol *Sym = Symtab->find(From); if (!Sym) continue; if (auto *U = dyn_cast(Sym)) if (!U->WeakAlias) U->WeakAlias = Symtab->addUndefined(To); } // 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; // If /msvclto is given, we use the MSVC linker to link LTO output files. // This is useful because MSVC link.exe can generate complete PDBs. if (Args.hasArg(OPT_msvclto)) { invokeMSVC(Args); return; } // Do LTO by compiling bitcode input files to a set of native COFF files then // link those files. Symtab->addCombinedLTOObjects(); run(); // Make sure we have resolved all symbols. Symtab->reportRemainingUndefines(); if (errorCount()) return; // Windows specific -- if no /subsystem is given, we need to infer // that from entry point name. if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) { Config->Subsystem = inferSubsystem(); if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) fatal("subsystem must be defined"); } // Handle /safeseh. if (Args.hasFlag(OPT_safeseh, OPT_safeseh_no, false)) { for (ObjFile *File : ObjFile::Instances) if (!File->SEHCompat) error("/safeseh: " + File->getName() + " is not compatible with SEH"); if (errorCount()) return; } // In MinGW, all symbols are automatically exported if no symbols // are chosen to be exported. if (Config->DLL && ((Config->MinGW && Config->Exports.empty()) || Args.hasArg(OPT_export_all_symbols))) { AutoExporter Exporter; Symtab->forEachSymbol([=](Symbol *S) { auto *Def = dyn_cast(S); if (!Exporter.shouldExport(Def)) return; Export E; E.Name = Def->getName(); E.Sym = Def; if (Def->getChunk() && !(Def->getChunk()->getPermissions() & IMAGE_SCN_MEM_EXECUTE)) E.Data = true; Config->Exports.push_back(E); }); } // Windows specific -- when we are creating a .dll file, we also // need to create a .lib file. if (!Config->Exports.empty() || Config->DLL) { fixupExports(); createImportLibrary(/*AsLib=*/false); assignExportOrdinals(); } // Handle /output-def (MinGW specific). if (auto *Arg = Args.getLastArg(OPT_output_def)) writeDefFile(Arg->getValue()); // Set extra alignment for .comm symbols for (auto Pair : Config->AlignComm) { StringRef Name = Pair.first; uint32_t Alignment = Pair.second; Symbol *Sym = Symtab->find(Name); if (!Sym) { warn("/aligncomm symbol " + Name + " not found"); continue; } auto *DC = dyn_cast(Sym); if (!DC) { warn("/aligncomm symbol " + Name + " of wrong kind"); continue; } CommonChunk *C = DC->getChunk(); C->Alignment = std::max(C->Alignment, Alignment); } // Windows specific -- Create a side-by-side manifest file. if (Config->Manifest == Configuration::SideBySide) createSideBySideManifest(); // Identify unreferenced COMDAT sections. if (Config->DoGC) markLive(Symtab->getChunks()); // Identify identical COMDAT sections to merge them. if (Config->DoICF) doICF(Symtab->getChunks()); // Write the result. writeResult(); } } // namespace coff } // namespace lld Index: vendor/lld/dist/COFF/Driver.h =================================================================== --- vendor/lld/dist/COFF/Driver.h (revision 327307) +++ vendor/lld/dist/COFF/Driver.h (revision 327308) @@ -1,189 +1,196 @@ //===- Driver.h -------------------------------------------------*- C++ -*-===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLD_COFF_DRIVER_H #define LLD_COFF_DRIVER_H #include "Config.h" #include "SymbolTable.h" #include "lld/Common/LLVM.h" #include "lld/Common/Reproduce.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/TarWriter.h" #include #include #include namespace lld { namespace coff { class LinkerDriver; extern LinkerDriver *Driver; using llvm::COFF::MachineTypes; using llvm::COFF::WindowsSubsystem; using llvm::Optional; // Implemented in MarkLive.cpp. void markLive(ArrayRef Chunks); // Implemented in ICF.cpp. void doICF(ArrayRef Chunks); class COFFOptTable : public llvm::opt::OptTable { public: COFFOptTable(); }; class ArgParser { public: // Concatenate LINK environment variable and given arguments and parse them. llvm::opt::InputArgList parseLINK(std::vector Args); // Tokenizes a given string and then parses as command line options. llvm::opt::InputArgList parse(StringRef S) { return parse(tokenize(S)); } + // Tokenizes a given string and then parses as command line options in + // .drectve section. + llvm::opt::InputArgList parseDirectives(StringRef S); + private: // Parses command line options. llvm::opt::InputArgList parse(llvm::ArrayRef Args); std::vector tokenize(StringRef S); COFFOptTable Table; }; class LinkerDriver { public: void link(llvm::ArrayRef Args); // Used by the resolver to parse .drectve section contents. void parseDirectives(StringRef S); // Used by ArchiveFile to enqueue members. void enqueueArchiveMember(const Archive::Child &C, StringRef SymName, StringRef ParentName); MemoryBufferRef takeBuffer(std::unique_ptr MB); private: std::unique_ptr Tar; // for /linkrepro // Opens a file. Path has to be resolved already. MemoryBufferRef openFile(StringRef Path); // Searches a file from search paths. Optional findFile(StringRef Filename); Optional findLib(StringRef Filename); StringRef doFindFile(StringRef Filename); StringRef doFindLib(StringRef Filename); // Parses LIB environment which contains a list of search paths. void addLibSearchPaths(); // Library search path. The first element is always "" (current directory). std::vector SearchPaths; std::set VisitedFiles; std::set VisitedLibs; Symbol *addUndefined(StringRef Sym); StringRef mangle(StringRef Sym); // Windows specific -- "main" is not the only main function in Windows. // You can choose one from these four -- {w,}{WinMain,main}. // There are four different entry point functions for them, // {w,}{WinMain,main}CRTStartup, respectively. The linker needs to // choose the right one depending on which "main" function is defined. // This function looks up the symbol table and resolve corresponding // entry point name. StringRef findDefaultEntry(); WindowsSubsystem inferSubsystem(); void invokeMSVC(llvm::opt::InputArgList &Args); void addBuffer(std::unique_ptr MB, bool WholeArchive); void addArchiveBuffer(MemoryBufferRef MBRef, StringRef SymName, StringRef ParentName); void enqueuePath(StringRef Path, bool WholeArchive); void enqueueTask(std::function Task); bool run(); std::list> TaskQueue; std::vector FilePaths; std::vector Resources; + + llvm::StringSet<> DirectivesExports; }; // Functions below this line are defined in DriverUtils.cpp. void printHelp(const char *Argv0); // For /machine option. MachineTypes getMachineType(StringRef Arg); StringRef machineToStr(MachineTypes MT); // Parses a string in the form of "[,]". void parseNumbers(StringRef Arg, uint64_t *Addr, uint64_t *Size = nullptr); // Parses a string in the form of "[.]". // Minor's default value is 0. void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor); // Parses a string in the form of "[,[.]]". void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major, uint32_t *Minor); void parseAlternateName(StringRef); void parseMerge(StringRef); void parseSection(StringRef); void parseAligncomm(StringRef); // Parses a string in the form of "EMBED[,=]|NO". void parseManifest(StringRef Arg); // Parses a string in the form of "level=|uiAccess=" void parseManifestUAC(StringRef Arg); // Create a resource file containing a manifest XML. std::unique_ptr createManifestRes(); void createSideBySideManifest(); // Used for dllexported symbols. Export parseExport(StringRef Arg); void fixupExports(); void assignExportOrdinals(); // Parses a string in the form of "key=value" and check // if value matches previous values for the key. // This feature used in the directive section to reject // incompatible objects. void checkFailIfMismatch(StringRef Arg); // Convert Windows resource files (.res files) to a .obj file. MemoryBufferRef convertResToCOFF(ArrayRef MBs); void runMSVCLinker(std::string Rsp, ArrayRef Objects); // Create enum with OPT_xxx values for each option in Options.td enum { OPT_INVALID = 0, #define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID, #include "Options.inc" #undef OPTION }; } // namespace coff } // namespace lld #endif Index: vendor/lld/dist/COFF/DriverUtils.cpp =================================================================== --- vendor/lld/dist/COFF/DriverUtils.cpp (revision 327307) +++ vendor/lld/dist/COFF/DriverUtils.cpp (revision 327308) @@ -1,780 +1,796 @@ //===- DriverUtils.cpp ----------------------------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file contains utility functions for the driver. Because there // are so many small functions, we created this separate file to make // Driver.cpp less cluttered. // //===----------------------------------------------------------------------===// #include "Config.h" #include "Driver.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFF.h" #include "llvm/Object/WindowsResource.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileUtilities.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" #include "llvm/WindowsManifest/WindowsManifestMerger.h" #include using namespace llvm::COFF; using namespace llvm; using llvm::sys::Process; namespace lld { namespace coff { namespace { const uint16_t SUBLANG_ENGLISH_US = 0x0409; const uint16_t RT_MANIFEST = 24; class Executor { public: explicit Executor(StringRef S) : Prog(Saver.save(S)) {} void add(StringRef S) { Args.push_back(Saver.save(S)); } void add(std::string &S) { Args.push_back(Saver.save(S)); } void add(Twine S) { Args.push_back(Saver.save(S)); } void add(const char *S) { Args.push_back(Saver.save(S)); } void run() { ErrorOr ExeOrErr = sys::findProgramByName(Prog); if (auto EC = ExeOrErr.getError()) fatal("unable to find " + Prog + " in PATH: " + EC.message()); StringRef Exe = Saver.save(*ExeOrErr); Args.insert(Args.begin(), Exe); std::vector Vec; for (StringRef S : Args) Vec.push_back(S.data()); Vec.push_back(nullptr); if (sys::ExecuteAndWait(Args[0], Vec.data()) != 0) fatal("ExecuteAndWait failed: " + llvm::join(Args.begin(), Args.end(), " ")); } private: StringRef Prog; std::vector Args; }; } // anonymous namespace // Returns /machine's value. MachineTypes getMachineType(StringRef S) { MachineTypes MT = StringSwitch(S.lower()) .Cases("x64", "amd64", AMD64) .Cases("x86", "i386", I386) .Case("arm", ARMNT) .Case("arm64", ARM64) .Default(IMAGE_FILE_MACHINE_UNKNOWN); if (MT != IMAGE_FILE_MACHINE_UNKNOWN) return MT; fatal("unknown /machine argument: " + S); } StringRef machineToStr(MachineTypes MT) { switch (MT) { case ARMNT: return "arm"; case ARM64: return "arm64"; case AMD64: return "x64"; case I386: return "x86"; default: llvm_unreachable("unknown machine type"); } } // Parses a string in the form of "[,]". void parseNumbers(StringRef Arg, uint64_t *Addr, uint64_t *Size) { StringRef S1, S2; std::tie(S1, S2) = Arg.split(','); if (S1.getAsInteger(0, *Addr)) fatal("invalid number: " + S1); if (Size && !S2.empty() && S2.getAsInteger(0, *Size)) fatal("invalid number: " + S2); } // Parses a string in the form of "[.]". // If second number is not present, Minor is set to 0. void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor) { StringRef S1, S2; std::tie(S1, S2) = Arg.split('.'); if (S1.getAsInteger(0, *Major)) fatal("invalid number: " + S1); *Minor = 0; if (!S2.empty() && S2.getAsInteger(0, *Minor)) fatal("invalid number: " + S2); } // Parses a string in the form of "[,[.]]". void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major, uint32_t *Minor) { StringRef SysStr, Ver; std::tie(SysStr, Ver) = Arg.split(','); *Sys = StringSwitch(SysStr.lower()) .Case("boot_application", IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION) .Case("console", IMAGE_SUBSYSTEM_WINDOWS_CUI) .Case("efi_application", IMAGE_SUBSYSTEM_EFI_APPLICATION) .Case("efi_boot_service_driver", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) .Case("efi_rom", IMAGE_SUBSYSTEM_EFI_ROM) .Case("efi_runtime_driver", IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) .Case("native", IMAGE_SUBSYSTEM_NATIVE) .Case("posix", IMAGE_SUBSYSTEM_POSIX_CUI) .Case("windows", IMAGE_SUBSYSTEM_WINDOWS_GUI) .Default(IMAGE_SUBSYSTEM_UNKNOWN); if (*Sys == IMAGE_SUBSYSTEM_UNKNOWN) fatal("unknown subsystem: " + SysStr); if (!Ver.empty()) parseVersion(Ver, Major, Minor); } // Parse a string of the form of "=". // Results are directly written to Config. void parseAlternateName(StringRef S) { StringRef From, To; std::tie(From, To) = S.split('='); if (From.empty() || To.empty()) fatal("/alternatename: invalid argument: " + S); auto It = Config->AlternateNames.find(From); if (It != Config->AlternateNames.end() && It->second != To) fatal("/alternatename: conflicts: " + S); Config->AlternateNames.insert(It, std::make_pair(From, To)); } // Parse a string of the form of "=". // Results are directly written to Config. void parseMerge(StringRef S) { StringRef From, To; std::tie(From, To) = S.split('='); if (From.empty() || To.empty()) fatal("/merge: invalid argument: " + S); auto Pair = Config->Merge.insert(std::make_pair(From, To)); bool Inserted = Pair.second; if (!Inserted) { StringRef Existing = Pair.first->second; if (Existing != To) warn(S + ": already merged into " + Existing); } } static uint32_t parseSectionAttributes(StringRef S) { uint32_t Ret = 0; for (char C : S.lower()) { switch (C) { case 'd': Ret |= IMAGE_SCN_MEM_DISCARDABLE; break; case 'e': Ret |= IMAGE_SCN_MEM_EXECUTE; break; case 'k': Ret |= IMAGE_SCN_MEM_NOT_CACHED; break; case 'p': Ret |= IMAGE_SCN_MEM_NOT_PAGED; break; case 'r': Ret |= IMAGE_SCN_MEM_READ; break; case 's': Ret |= IMAGE_SCN_MEM_SHARED; break; case 'w': Ret |= IMAGE_SCN_MEM_WRITE; break; default: fatal("/section: invalid argument: " + S); } } return Ret; } // Parses /section option argument. void parseSection(StringRef S) { StringRef Name, Attrs; std::tie(Name, Attrs) = S.split(','); if (Name.empty() || Attrs.empty()) fatal("/section: invalid argument: " + S); Config->Section[Name] = parseSectionAttributes(Attrs); } // Parses /aligncomm option argument. void parseAligncomm(StringRef S) { StringRef Name, Align; std::tie(Name, Align) = S.split(','); if (Name.empty() || Align.empty()) { error("/aligncomm: invalid argument: " + S); return; } int V; if (Align.getAsInteger(0, V)) { error("/aligncomm: invalid argument: " + S); return; } Config->AlignComm[Name] = std::max(Config->AlignComm[Name], 1 << V); } // Parses a string in the form of "EMBED[,=]|NO". // Results are directly written to Config. void parseManifest(StringRef Arg) { if (Arg.equals_lower("no")) { Config->Manifest = Configuration::No; return; } if (!Arg.startswith_lower("embed")) fatal("invalid option " + Arg); Config->Manifest = Configuration::Embed; Arg = Arg.substr(strlen("embed")); if (Arg.empty()) return; if (!Arg.startswith_lower(",id=")) fatal("invalid option " + Arg); Arg = Arg.substr(strlen(",id=")); if (Arg.getAsInteger(0, Config->ManifestID)) fatal("invalid option " + Arg); } // Parses a string in the form of "level=|uiAccess=|NO". // Results are directly written to Config. void parseManifestUAC(StringRef Arg) { if (Arg.equals_lower("no")) { Config->ManifestUAC = false; return; } for (;;) { Arg = Arg.ltrim(); if (Arg.empty()) return; if (Arg.startswith_lower("level=")) { Arg = Arg.substr(strlen("level=")); std::tie(Config->ManifestLevel, Arg) = Arg.split(" "); continue; } if (Arg.startswith_lower("uiaccess=")) { Arg = Arg.substr(strlen("uiaccess=")); std::tie(Config->ManifestUIAccess, Arg) = Arg.split(" "); continue; } fatal("invalid option " + Arg); } } // An RAII temporary file class that automatically removes a temporary file. namespace { class TemporaryFile { public: TemporaryFile(StringRef Prefix, StringRef Extn, StringRef Contents = "") { SmallString<128> S; if (auto EC = sys::fs::createTemporaryFile("lld-" + Prefix, Extn, S)) fatal("cannot create a temporary file: " + EC.message()); Path = S.str(); if (!Contents.empty()) { std::error_code EC; raw_fd_ostream OS(Path, EC, sys::fs::F_None); if (EC) fatal("failed to open " + Path + ": " + EC.message()); OS << Contents; } } TemporaryFile(TemporaryFile &&Obj) { std::swap(Path, Obj.Path); } ~TemporaryFile() { if (Path.empty()) return; if (sys::fs::remove(Path)) fatal("failed to remove " + Path); } // Returns a memory buffer of this temporary file. // Note that this function does not leave the file open, // so it is safe to remove the file immediately after this function // is called (you cannot remove an opened file on Windows.) std::unique_ptr getMemoryBuffer() { // IsVolatileSize=true forces MemoryBuffer to not use mmap(). return CHECK(MemoryBuffer::getFile(Path, /*FileSize=*/-1, /*RequiresNullTerminator=*/false, /*IsVolatileSize=*/true), "could not open " + Path); } std::string Path; }; } static std::string createDefaultXml() { std::string Ret; raw_string_ostream OS(Ret); // Emit the XML. Note that we do *not* verify that the XML attributes are // syntactically correct. This is intentional for link.exe compatibility. OS << "\n" << "\n"; if (Config->ManifestUAC) { OS << " \n" << " \n" << " \n" << " \n" << " \n" << " \n" << " \n"; } if (!Config->ManifestDependency.empty()) { OS << " \n" << " \n" << " ManifestDependency << " />\n" << " \n" << " \n"; } OS << "\n"; return OS.str(); } static std::string createManifestXmlWithInternalMt(StringRef DefaultXml) { std::unique_ptr DefaultXmlCopy = MemoryBuffer::getMemBufferCopy(DefaultXml); windows_manifest::WindowsManifestMerger Merger; if (auto E = Merger.merge(*DefaultXmlCopy.get())) fatal("internal manifest tool failed on default xml: " + toString(std::move(E))); for (StringRef Filename : Config->ManifestInput) { std::unique_ptr Manifest = check(MemoryBuffer::getFile(Filename)); if (auto E = Merger.merge(*Manifest.get())) fatal("internal manifest tool failed on file " + Filename + ": " + toString(std::move(E))); } return Merger.getMergedManifest().get()->getBuffer(); } 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); if (EC) fatal("failed to open " + Default.Path + ": " + EC.message()); OS << DefaultXml; OS.close(); // Merge user-supplied manifests if they are given. Since libxml2 is not // enabled, we must shell out to Microsoft's mt.exe tool. TemporaryFile User("user", "manifest"); Executor E("mt.exe"); E.add("/manifest"); E.add(Default.Path); for (StringRef Filename : Config->ManifestInput) { E.add("/manifest"); E.add(Filename); } E.add("/nologo"); E.add("/out:" + StringRef(User.Path)); E.run(); return CHECK(MemoryBuffer::getFile(User.Path), "could not open " + User.Path) .get() ->getBuffer(); } static std::string createManifestXml() { std::string DefaultXml = createDefaultXml(); if (Config->ManifestInput.empty()) return DefaultXml; if (windows_manifest::isAvailable()) return createManifestXmlWithInternalMt(DefaultXml); return createManifestXmlWithExternalMt(DefaultXml); } static std::unique_ptr createMemoryBufferForManifestRes(size_t ManifestSize) { size_t ResSize = alignTo( object::WIN_RES_MAGIC_SIZE + object::WIN_RES_NULL_ENTRY_SIZE + sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) + sizeof(object::WinResHeaderSuffix) + ManifestSize, object::WIN_RES_DATA_ALIGNMENT); return MemoryBuffer::getNewMemBuffer(ResSize, Config->OutputFile + ".manifest.res"); } static void writeResFileHeader(char *&Buf) { memcpy(Buf, COFF::WinResMagic, sizeof(COFF::WinResMagic)); Buf += sizeof(COFF::WinResMagic); memset(Buf, 0, object::WIN_RES_NULL_ENTRY_SIZE); Buf += object::WIN_RES_NULL_ENTRY_SIZE; } static void writeResEntryHeader(char *&Buf, size_t ManifestSize) { // Write the prefix. auto *Prefix = reinterpret_cast(Buf); Prefix->DataSize = ManifestSize; Prefix->HeaderSize = sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) + sizeof(object::WinResHeaderSuffix); Buf += sizeof(object::WinResHeaderPrefix); // Write the Type/Name IDs. auto *IDs = reinterpret_cast(Buf); IDs->setType(RT_MANIFEST); IDs->setName(Config->ManifestID); Buf += sizeof(object::WinResIDs); // Write the suffix. auto *Suffix = reinterpret_cast(Buf); Suffix->DataVersion = 0; Suffix->MemoryFlags = object::WIN_RES_PURE_MOVEABLE; Suffix->Language = SUBLANG_ENGLISH_US; Suffix->Version = 0; Suffix->Characteristics = 0; Buf += sizeof(object::WinResHeaderSuffix); } // Create a resource file containing a manifest XML. std::unique_ptr createManifestRes() { std::string Manifest = createManifestXml(); std::unique_ptr Res = createMemoryBufferForManifestRes(Manifest.size()); char *Buf = const_cast(Res->getBufferStart()); writeResFileHeader(Buf); writeResEntryHeader(Buf, Manifest.size()); // Copy the manifest data into the .res file. std::copy(Manifest.begin(), Manifest.end(), Buf); return Res; } void createSideBySideManifest() { std::string Path = Config->ManifestFile; if (Path == "") Path = Config->OutputFile + ".manifest"; std::error_code EC; raw_fd_ostream Out(Path, EC, sys::fs::F_Text); if (EC) fatal("failed to create manifest: " + EC.message()); Out << createManifestXml(); } // Parse a string in the form of // "[=][,@ordinal[,NONAME]][,DATA][,PRIVATE]" // or "=.". // Used for parsing /export arguments. Export parseExport(StringRef Arg) { Export E; StringRef Rest; std::tie(E.Name, Rest) = Arg.split(","); if (E.Name.empty()) goto err; if (E.Name.contains('=')) { StringRef X, Y; std::tie(X, Y) = E.Name.split("="); // If "=.". if (Y.contains(".")) { E.Name = X; E.ForwardTo = Y; return E; } E.ExtName = X; E.Name = Y; if (E.Name.empty()) goto err; } // If "=[,@ordinal[,NONAME]][,DATA][,PRIVATE]" while (!Rest.empty()) { StringRef Tok; std::tie(Tok, Rest) = Rest.split(","); if (Tok.equals_lower("noname")) { if (E.Ordinal == 0) goto err; E.Noname = true; continue; } if (Tok.equals_lower("data")) { E.Data = true; continue; } if (Tok.equals_lower("constant")) { E.Constant = true; continue; } if (Tok.equals_lower("private")) { E.Private = true; continue; } if (Tok.startswith("@")) { int32_t Ord; if (Tok.substr(1).getAsInteger(0, Ord)) goto err; if (Ord <= 0 || 65535 < Ord) goto err; E.Ordinal = Ord; continue; } goto err; } return E; err: fatal("invalid /export: " + Arg); } static StringRef undecorate(StringRef Sym) { if (Config->Machine != I386) return Sym; return Sym.startswith("_") ? Sym.substr(1) : Sym; } // Performs error checking on all /export arguments. // It also sets ordinals. void fixupExports() { // Symbol ordinals must be unique. std::set Ords; for (Export &E : Config->Exports) { if (E.Ordinal == 0) continue; if (!Ords.insert(E.Ordinal).second) fatal("duplicate export ordinal: " + E.Name); } for (Export &E : Config->Exports) { Symbol *Sym = E.Sym; if (!E.ForwardTo.empty() || !Sym) { E.SymbolName = E.Name; } else { if (auto *U = dyn_cast(Sym)) if (U->WeakAlias) Sym = U->WeakAlias; E.SymbolName = Sym->getName(); } } for (Export &E : Config->Exports) { if (!E.ForwardTo.empty()) { E.ExportName = undecorate(E.Name); } else { E.ExportName = undecorate(E.ExtName.empty() ? E.Name : E.ExtName); } } // Uniquefy by name. DenseMap Map(Config->Exports.size()); std::vector V; for (Export &E : Config->Exports) { auto Pair = Map.insert(std::make_pair(E.ExportName, &E)); bool Inserted = Pair.second; if (Inserted) { V.push_back(E); continue; } Export *Existing = Pair.first->second; if (E == *Existing || E.Name != Existing->Name) continue; warn("duplicate /export option: " + E.Name); } Config->Exports = std::move(V); // Sort by name. std::sort(Config->Exports.begin(), Config->Exports.end(), [](const Export &A, const Export &B) { return A.ExportName < B.ExportName; }); } void assignExportOrdinals() { // Assign unique ordinals if default (= 0). uint16_t Max = 0; for (Export &E : Config->Exports) Max = std::max(Max, E.Ordinal); for (Export &E : Config->Exports) if (E.Ordinal == 0) E.Ordinal = ++Max; } // Parses a string in the form of "key=value" and check // if value matches previous values for the same key. void checkFailIfMismatch(StringRef Arg) { StringRef K, V; std::tie(K, V) = Arg.split('='); if (K.empty() || V.empty()) fatal("/failifmismatch: invalid argument: " + Arg); StringRef Existing = Config->MustMatch[K]; if (!Existing.empty() && V != Existing) fatal("/failifmismatch: mismatch detected: " + Existing + " and " + V + " for key " + K); Config->MustMatch[K] = V; } // Convert Windows resource files (.res files) to a .obj file. MemoryBufferRef convertResToCOFF(ArrayRef MBs) { object::WindowsResourceParser Parser; for (MemoryBufferRef MB : MBs) { std::unique_ptr Bin = check(object::createBinary(MB)); object::WindowsResource *RF = dyn_cast(Bin.get()); if (!RF) fatal("cannot compile non-resource file as resource"); if (auto EC = Parser.parse(RF)) fatal("failed to parse .res file: " + toString(std::move(EC))); } Expected> E = llvm::object::writeWindowsResourceCOFF(Config->Machine, Parser); if (!E) fatal("failed to write .res to COFF: " + toString(E.takeError())); MemoryBufferRef MBRef = **E; make>(std::move(*E)); // take ownership return MBRef; } // Run MSVC link.exe for given in-memory object files. // Command line options are copied from those given to LLD. // This is for the /msvclto option. void runMSVCLinker(std::string Rsp, ArrayRef Objects) { // Write the in-memory object files to disk. std::vector Temps; for (StringRef S : Objects) { Temps.emplace_back("lto", "obj", S); Rsp += quote(Temps.back().Path) + "\n"; } log("link.exe " + Rsp); // Run MSVC link.exe. Temps.emplace_back("lto", "rsp", Rsp); Executor E("link.exe"); E.add(Twine("@" + Temps.back().Path)); E.run(); } // Create OptTable // Create prefix string literals used in Options.td #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; #include "Options.inc" #undef PREFIX // Create table mapping all options defined in Options.td static const llvm::opt::OptTable::Info InfoTable[] = { #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \ {X1, X2, X10, X11, OPT_##ID, llvm::opt::Option::KIND##Class, \ X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12}, #include "Options.inc" #undef OPTION }; COFFOptTable::COFFOptTable() : OptTable(InfoTable, true) {} static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) { if (auto *Arg = Args.getLastArg(OPT_rsp_quoting)) { StringRef S = Arg->getValue(); if (S != "windows" && S != "posix") error("invalid response file quoting: " + S); if (S == "windows") return cl::TokenizeWindowsCommandLine; return cl::TokenizeGNUCommandLine; } // The COFF linker always defaults to Windows quoting. return cl::TokenizeWindowsCommandLine; } // Parses a given list of options. opt::InputArgList ArgParser::parse(ArrayRef Argv) { // Make InputArgList from string vectors. unsigned MissingIndex; unsigned MissingCount; SmallVector Vec(Argv.data(), Argv.data() + Argv.size()); // 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. opt::InputArgList Args = Table.ParseArgs(Vec, MissingIndex, MissingCount); // Expand response files (arguments in the form of @) // and then parse the argument again. cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), Vec); Args = Table.ParseArgs(Vec, MissingIndex, MissingCount); // Print the real command line if response files are expanded. if (Args.hasArg(OPT_verbose) && Argv.size() != Vec.size()) { std::string Msg = "Command line:"; for (const char *S : Vec) Msg += " " + std::string(S); message(Msg); } // Handle /WX early since it converts missing argument warnings to errors. errorHandler().FatalWarnings = Args.hasFlag(OPT_WX, OPT_WX_no, false); if (MissingCount) fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument"); for (auto *Arg : Args.filtered(OPT_UNKNOWN)) warn("ignoring unknown argument: " + Arg->getSpelling()); return Args; } +// Tokenizes and parses a given string as command line in .drective section. +opt::InputArgList ArgParser::parseDirectives(StringRef S) { + // Make InputArgList from string vectors. + unsigned MissingIndex; + unsigned MissingCount; + + opt::InputArgList Args = + Table.ParseArgs(tokenize(S), MissingIndex, MissingCount); + + if (MissingCount) + fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument"); + for (auto *Arg : Args.filtered(OPT_UNKNOWN)) + warn("ignoring unknown argument: " + Arg->getSpelling()); + return Args; +} + // 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 Argv) { // Concatenate LINK env and command line arguments, and then parse them. if (Optional S = Process::GetEnv("LINK")) { std::vector V = tokenize(*S); Argv.insert(Argv.begin(), V.begin(), V.end()); } if (Optional S = Process::GetEnv("_LINK_")) { std::vector V = tokenize(*S); Argv.insert(Argv.begin(), V.begin(), V.end()); } return parse(Argv); } std::vector ArgParser::tokenize(StringRef S) { SmallVector Tokens; cl::TokenizeWindowsCommandLine(S, Saver, Tokens); return std::vector(Tokens.begin(), Tokens.end()); } void printHelp(const char *Argv0) { COFFOptTable().PrintHelp(outs(), Argv0, "LLVM Linker", false); } } // namespace coff } // namespace lld Index: vendor/lld/dist/COFF/Options.td =================================================================== --- vendor/lld/dist/COFF/Options.td (revision 327307) +++ vendor/lld/dist/COFF/Options.td (revision 327308) @@ -1,165 +1,165 @@ include "llvm/Option/OptParser.td" // link.exe accepts options starting with either a dash or a slash. // Flag that takes no arguments. class F : Flag<["/", "-", "-?"], name>; // Flag that takes one argument after ":". class P : Joined<["/", "-", "-?"], name#":">, HelpText; // Boolean flag which can be suffixed by ":no". Using it unsuffixed turns the // flag on and using it suffixed by ":no" turns it off. multiclass B { def "" : F, HelpText; def _no : F, HelpText; } def align : P<"align", "Section alignment">; 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 defaultlib : P<"defaultlib", "Add the library to the list of input files">; def delayload : P<"delayload", "Delay loaded DLL name">; def entry : P<"entry", "Name of entry point symbol">; def errorlimit : P<"errorlimit", "Maximum number of errors to emit before stopping (0 = no limit)">; def export : P<"export", "Export a function">; // No help text because /failifmismatch is not intended to be used by the user. def failifmismatch : P<"failifmismatch", "">; def heap : P<"heap", "Size of the heap">; +def ignore : P<"ignore", "Specify warning codes to ignore">; def implib : P<"implib", "Import library name">; def libpath : P<"libpath", "Additional library search path">; def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">; def 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">; def merge : P<"merge", "Combine sections">; def mllvm : P<"mllvm", "Options to pass to LLVM">; def nodefaultlib : P<"nodefaultlib", "Remove a default library">; def opt : P<"opt", "Control optimizations">; def out : P<"out", "Path to file to write output">; def pdb : P<"pdb", "PDB file path">; def section : P<"section", "Specify section attributes">; def stack : P<"stack", "Size of the stack">; def stub : P<"stub", "Specify DOS stub file">; def subsystem : P<"subsystem", "Specify subsystem">; 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 disallowlib : Joined<["/", "-", "-?"], "disallowlib:">, Alias; def manifest : F<"manifest">; def manifest_colon : P<"manifest", "Create manifest file">; def manifestuac : P<"manifestuac", "User access control">; def manifestfile : P<"manifestfile", "Manifest file path">; def manifestdependency : P<"manifestdependency", "Attributes for in manifest file">; def manifestinput : P<"manifestinput", "Specify manifest file">; // We cannot use multiclass P because class name "incl" is different // from its command line option name. We do this because "include" is // a reserved keyword in tablegen. def incl : Joined<["/", "-"], "include:">, HelpText<"Force symbol to be added to symbol table as undefined one">; // "def" is also a keyword. def deffile : Joined<["/", "-"], "def:">, HelpText<"Use module-definition file">; def debug : F<"debug">, HelpText<"Embed a symbol table in the image">; def debugtype : P<"debugtype", "Debug Info Options">; def dll : F<"dll">, HelpText<"Create a DLL">; def driver : P<"driver", "Generate a Windows NT Kernel Mode Driver">; def nodefaultlib_all : F<"nodefaultlib">; def noentry : F<"noentry">; def profile : F<"profile">; def swaprun_cd : F<"swaprun:cd">; def swaprun_net : F<"swaprun:net">; def verbose : F<"verbose">; def wholearchive_flag : F<"wholearchive">; def force : F<"force">, HelpText<"Allow undefined symbols when creating executables">; def force_unresolved : F<"force:unresolved">; defm WX : B<"WX", "Treat warnings as errors", "Don't treat warnings as errors">; defm allowbind : B<"allowbind", "Enable DLL binding (default)", "Disable DLL binding">; defm allowisolation : B<"allowisolation", "Enable DLL isolation (default)", "Disable DLL isolation">; defm appcontainer : B<"appcontainer", "Image can only be run in an app container", "Image can run outside an app container (default)">; defm dynamicbase : B<"dynamicbase", "Enable ASLR (default unless /fixed)", "Disable ASLR (default when /fixed)">; defm fixed : B<"fixed", "Disable base relocations", "Enable base relocations (default)">; defm highentropyva : B<"highentropyva", "Enable 64-bit ASLR (default on 64-bit)", "Disable 64-bit ASLR">; defm largeaddressaware : B<"largeaddressaware", "Enable large addresses (default on 64-bit)", "Disable large addresses (default on 32-bit)">; defm nxcompat : B<"nxcompat", "Enable data execution prevention (default)", "Disable data execution provention">; defm safeseh : B<"safeseh", "Produce an image with Safe Exception Handler (only for x86)", "Don't produce an image with Safe Exception Handler">; defm tsaware : B<"tsaware", "Create Terminal Server aware executable (default)", "Create non-Terminal Server aware executable">; def help : F<"help">; def help_q : Flag<["/?", "-?"], "">, Alias; // LLD extensions def debug_ghash : F<"debug:ghash">; def debug_dwarf : F<"debug:dwarf">; def export_all_symbols : F<"export-all-symbols">; def lldmingw : F<"lldmingw">; def msvclto : F<"msvclto">; def output_def : Joined<["/", "-"], "output-def:">; def rsp_quoting : Joined<["--"], "rsp-quoting=">, HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">; def dash_dash_version : Flag<["--"], "version">, HelpText<"Print version information">; // Flags for debugging def lldmap : F<"lldmap">; def lldmap_file : Joined<["/", "-"], "lldmap:">; //============================================================================== // The flags below do nothing. They are defined only for link.exe compatibility. //============================================================================== class QF : Joined<["/", "-", "-?"], name#":">; multiclass QB { def "" : F; def _no : F; } def functionpadmin : F<"functionpadmin">; def ignoreidl : F<"ignoreidl">; def incremental : F<"incremental">; def no_incremental : F<"incremental:no">; def nologo : F<"nologo">; def throwingnew : F<"throwingnew">; def editandcontinue : F<"editandcontinue">; def fastfail : F<"fastfail">; def delay : QF<"delay">; def errorreport : QF<"errorreport">; def idlout : QF<"idlout">; -def ignore : QF<"ignore">; def maxilksize : QF<"maxilksize">; def natvis : QF<"natvis">; def pdbaltpath : QF<"pdbaltpath">; def tlbid : QF<"tlbid">; def tlbout : QF<"tlbout">; def verbose_all : QF<"verbose">; def guardsym : QF<"guardsym">; Index: vendor/lld/dist/COFF/SymbolTable.cpp =================================================================== --- vendor/lld/dist/COFF/SymbolTable.cpp (revision 327307) +++ vendor/lld/dist/COFF/SymbolTable.cpp (revision 327308) @@ -1,392 +1,395 @@ //===- SymbolTable.cpp ----------------------------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "SymbolTable.h" #include "Config.h" #include "Driver.h" #include "LTO.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "llvm/IR/LLVMContext.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include using namespace llvm; namespace lld { namespace coff { SymbolTable *Symtab; void SymbolTable::addFile(InputFile *File) { log("Reading " + toString(File)); File->parse(); MachineTypes MT = File->getMachineType(); if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) { Config->Machine = MT; } else if (MT != IMAGE_FILE_MACHINE_UNKNOWN && Config->Machine != MT) { fatal(toString(File) + ": machine type " + machineToStr(MT) + " conflicts with " + machineToStr(Config->Machine)); } if (auto *F = dyn_cast(File)) { ObjFile::Instances.push_back(F); } else if (auto *F = dyn_cast(File)) { BitcodeFile::Instances.push_back(F); } else if (auto *F = dyn_cast(File)) { ImportFile::Instances.push_back(F); } StringRef S = File->getDirectives(); if (S.empty()) return; log("Directives: " + toString(File) + ": " + S); Driver->parseDirectives(S); } static void errorOrWarn(const Twine &S) { if (Config->Force) warn(S); else error(S); } void SymbolTable::reportRemainingUndefines() { SmallPtrSet Undefs; DenseMap LocalImports; for (auto &I : SymMap) { Symbol *Sym = I.second; auto *Undef = dyn_cast(Sym); if (!Undef) continue; if (!Sym->IsUsedInRegularObj) continue; StringRef Name = Undef->getName(); // A weak alias may have been resolved, so check for that. if (Defined *D = Undef->getWeakAlias()) { // We want to replace Sym with D. However, we can't just blindly // copy sizeof(SymbolUnion) bytes from D to Sym because D may be an // internal symbol, and internal symbols are stored as "unparented" // Symbols. For that reason we need to check which type of symbol we // are dealing with and copy the correct number of bytes. if (isa(D)) memcpy(Sym, D, sizeof(DefinedRegular)); else if (isa(D)) memcpy(Sym, D, sizeof(DefinedAbsolute)); else memcpy(Sym, D, sizeof(SymbolUnion)); continue; } // If we can resolve a symbol by removing __imp_ prefix, do that. // This odd rule is for compatibility with MSVC linker. if (Name.startswith("__imp_")) { Symbol *Imp = find(Name.substr(strlen("__imp_"))); if (Imp && isa(Imp)) { auto *D = cast(Imp); replaceSymbol(Sym, Name, D); LocalImportChunks.push_back(cast(Sym)->getChunk()); LocalImports[Sym] = D; continue; } } // Remaining undefined symbols are not fatal if /force is specified. // They are replaced with dummy defined symbols. if (Config->Force) replaceSymbol(Sym, Name, 0); Undefs.insert(Sym); } if (Undefs.empty() && LocalImports.empty()) return; for (Symbol *B : Config->GCRoot) { if (Undefs.count(B)) errorOrWarn(": undefined symbol: " + B->getName()); - if (Symbol *Imp = LocalImports.lookup(B)) - warn(": locally defined symbol imported: " + Imp->getName() + - " (defined in " + toString(Imp->getFile()) + ")"); + if (Config->WarnLocallyDefinedImported) + if (Symbol *Imp = LocalImports.lookup(B)) + warn(": locally defined symbol imported: " + Imp->getName() + + " (defined in " + toString(Imp->getFile()) + ")"); } for (ObjFile *File : ObjFile::Instances) { for (Symbol *Sym : File->getSymbols()) { if (!Sym) continue; if (Undefs.count(Sym)) errorOrWarn(toString(File) + ": undefined symbol: " + Sym->getName()); - if (Symbol *Imp = LocalImports.lookup(Sym)) - warn(toString(File) + ": locally defined symbol imported: " + - Imp->getName() + " (defined in " + toString(Imp->getFile()) + ")"); + if (Config->WarnLocallyDefinedImported) + if (Symbol *Imp = LocalImports.lookup(Sym)) + warn(toString(File) + ": locally defined symbol imported: " + + Imp->getName() + " (defined in " + toString(Imp->getFile()) + + ")"); } } } std::pair SymbolTable::insert(StringRef Name) { Symbol *&Sym = SymMap[CachedHashStringRef(Name)]; if (Sym) return {Sym, false}; Sym = (Symbol *)make(); Sym->IsUsedInRegularObj = false; Sym->PendingArchiveLoad = false; return {Sym, true}; } Symbol *SymbolTable::addUndefined(StringRef Name, InputFile *F, bool IsWeakAlias) { Symbol *S; bool WasInserted; std::tie(S, WasInserted) = insert(Name); if (!F || !isa(F)) S->IsUsedInRegularObj = true; if (WasInserted || (isa(S) && IsWeakAlias)) { replaceSymbol(S, Name); return S; } if (auto *L = dyn_cast(S)) { if (!S->PendingArchiveLoad) { S->PendingArchiveLoad = true; L->File->addMember(&L->Sym); } } return S; } void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) { StringRef Name = Sym.getName(); Symbol *S; bool WasInserted; std::tie(S, WasInserted) = insert(Name); if (WasInserted) { replaceSymbol(S, F, Sym); return; } auto *U = dyn_cast(S); if (!U || U->WeakAlias || S->PendingArchiveLoad) return; S->PendingArchiveLoad = true; F->addMember(&Sym); } void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) { error("duplicate symbol: " + toString(*Existing) + " in " + toString(Existing->getFile()) + " and in " + toString(NewFile)); } Symbol *SymbolTable::addAbsolute(StringRef N, COFFSymbolRef Sym) { Symbol *S; bool WasInserted; std::tie(S, WasInserted) = insert(N); S->IsUsedInRegularObj = true; if (WasInserted || isa(S) || isa(S)) replaceSymbol(S, N, Sym); else if (!isa(S)) reportDuplicate(S, nullptr); return S; } Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) { Symbol *S; bool WasInserted; std::tie(S, WasInserted) = insert(N); S->IsUsedInRegularObj = true; if (WasInserted || isa(S) || isa(S)) replaceSymbol(S, N, VA); else if (!isa(S)) reportDuplicate(S, nullptr); return S; } Symbol *SymbolTable::addSynthetic(StringRef N, Chunk *C) { Symbol *S; bool WasInserted; std::tie(S, WasInserted) = insert(N); S->IsUsedInRegularObj = true; if (WasInserted || isa(S) || isa(S)) replaceSymbol(S, N, C); else if (!isa(S)) reportDuplicate(S, nullptr); return S; } Symbol *SymbolTable::addRegular(InputFile *F, StringRef N, const coff_symbol_generic *Sym, SectionChunk *C) { Symbol *S; bool WasInserted; std::tie(S, WasInserted) = insert(N); if (!isa(F)) S->IsUsedInRegularObj = true; if (WasInserted || !isa(S)) replaceSymbol(S, F, N, /*IsCOMDAT*/ false, /*IsExternal*/ true, Sym, C); else reportDuplicate(S, F); return S; } std::pair SymbolTable::addComdat(InputFile *F, StringRef N, const coff_symbol_generic *Sym) { Symbol *S; bool WasInserted; std::tie(S, WasInserted) = insert(N); if (!isa(F)) S->IsUsedInRegularObj = true; if (WasInserted || !isa(S)) { replaceSymbol(S, F, N, /*IsCOMDAT*/ true, /*IsExternal*/ true, Sym, nullptr); return {S, true}; } if (!cast(S)->isCOMDAT()) reportDuplicate(S, F); return {S, false}; } Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size, const coff_symbol_generic *Sym, CommonChunk *C) { Symbol *S; bool WasInserted; std::tie(S, WasInserted) = insert(N); if (!isa(F)) S->IsUsedInRegularObj = true; if (WasInserted || !isa(S)) replaceSymbol(S, F, N, Size, Sym, C); else if (auto *DC = dyn_cast(S)) if (Size > DC->getSize()) replaceSymbol(S, F, N, Size, Sym, C); return S; } DefinedImportData *SymbolTable::addImportData(StringRef N, ImportFile *F) { Symbol *S; bool WasInserted; std::tie(S, WasInserted) = insert(N); S->IsUsedInRegularObj = true; if (WasInserted || isa(S) || isa(S)) { replaceSymbol(S, N, F); return cast(S); } reportDuplicate(S, F); return nullptr; } DefinedImportThunk *SymbolTable::addImportThunk(StringRef Name, DefinedImportData *ID, uint16_t Machine) { Symbol *S; bool WasInserted; std::tie(S, WasInserted) = insert(Name); S->IsUsedInRegularObj = true; if (WasInserted || isa(S) || isa(S)) { replaceSymbol(S, Name, ID, Machine); return cast(S); } reportDuplicate(S, ID->File); return nullptr; } std::vector SymbolTable::getChunks() { std::vector Res; for (ObjFile *File : ObjFile::Instances) { ArrayRef V = File->getChunks(); Res.insert(Res.end(), V.begin(), V.end()); } return Res; } Symbol *SymbolTable::find(StringRef Name) { auto It = SymMap.find(CachedHashStringRef(Name)); if (It == SymMap.end()) return nullptr; return It->second; } Symbol *SymbolTable::findUnderscore(StringRef Name) { if (Config->Machine == I386) return find(("_" + Name).str()); return find(Name); } StringRef SymbolTable::findByPrefix(StringRef Prefix) { for (auto Pair : SymMap) { StringRef Name = Pair.first.val(); if (Name.startswith(Prefix)) return Name; } return ""; } StringRef SymbolTable::findMangle(StringRef Name) { if (Symbol *Sym = find(Name)) if (!isa(Sym)) return Name; if (Config->Machine != I386) return findByPrefix(("?" + Name + "@@Y").str()); if (!Name.startswith("_")) return ""; // Search for x86 stdcall function. StringRef S = findByPrefix((Name + "@").str()); if (!S.empty()) return S; // Search for x86 fastcall function. S = findByPrefix(("@" + Name.substr(1) + "@").str()); if (!S.empty()) return S; // Search for x86 vectorcall function. S = findByPrefix((Name.substr(1) + "@@").str()); if (!S.empty()) return S; // Search for x86 C++ non-member function. return findByPrefix(("?" + Name.substr(1) + "@@Y").str()); } void SymbolTable::mangleMaybe(Symbol *B) { auto *U = dyn_cast(B); if (!U || U->WeakAlias) return; StringRef Alias = findMangle(U->getName()); if (!Alias.empty()) { log(U->getName() + " aliased to " + Alias); U->WeakAlias = addUndefined(Alias); } } Symbol *SymbolTable::addUndefined(StringRef Name) { return addUndefined(Name, nullptr, false); } std::vector SymbolTable::compileBitcodeFiles() { LTO.reset(new BitcodeCompiler); for (BitcodeFile *F : BitcodeFile::Instances) LTO->add(*F); return LTO->compile(); } void SymbolTable::addCombinedLTOObjects() { if (BitcodeFile::Instances.empty()) return; for (StringRef Object : compileBitcodeFiles()) { auto *Obj = make(MemoryBufferRef(Object, "lto.tmp")); Obj->parse(); ObjFile::Instances.push_back(Obj); } } } // namespace coff } // namespace lld Index: vendor/lld/dist/ELF/Arch/X86.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/X86.cpp (revision 327307) +++ vendor/lld/dist/ELF/Arch/X86.cpp (revision 327308) @@ -1,405 +1,405 @@ //===- X86.cpp ------------------------------------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::support::endian; using namespace llvm::ELF; using namespace lld; using namespace lld::elf; namespace { class X86 final : public TargetInfo { public: X86(); RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const override; int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override; void writeGotPltHeader(uint8_t *Buf) const override; RelType getDynRel(RelType Type) const override; void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; void writeIgotPlt(uint8_t *Buf, const Symbol &S) const override; void writePltHeader(uint8_t *Buf) const override; void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, RelExpr Expr) const override; void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override; void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; }; } // namespace X86::X86() { GotBaseSymOff = -1; CopyRel = R_386_COPY; GotRel = R_386_GLOB_DAT; PltRel = R_386_JUMP_SLOT; IRelativeRel = R_386_IRELATIVE; RelativeRel = R_386_RELATIVE; TlsGotRel = R_386_TLS_TPOFF; TlsModuleIndexRel = R_386_TLS_DTPMOD32; TlsOffsetRel = R_386_TLS_DTPOFF32; GotEntrySize = 4; GotPltEntrySize = 4; PltEntrySize = 16; PltHeaderSize = 16; TlsGdRelaxSkip = 2; TrapInstr = 0xcccccccc; // 0xcc = INT3 } static bool hasBaseReg(uint8_t ModRM) { return (ModRM & 0xc7) != 0x5; } RelExpr X86::getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const { switch (Type) { case R_386_8: case R_386_16: case R_386_32: case R_386_TLS_LDO_32: return R_ABS; case R_386_TLS_GD: return R_TLSGD; case R_386_TLS_LDM: return R_TLSLD; case R_386_PLT32: return R_PLT_PC; case R_386_PC8: case R_386_PC16: case R_386_PC32: return R_PC; case R_386_GOTPC: return R_GOTONLY_PC_FROM_END; case R_386_TLS_IE: return R_GOT; case R_386_GOT32: case R_386_GOT32X: // These relocations are arguably mis-designed because their calculations // depend on the instructions they are applied to. This is bad because we // usually don't care about whether the target section contains valid // machine instructions or not. But this is part of the documented ABI, so // we had to implement as the standard requires. // // x86 does not support PC-relative data access. Therefore, in order to // access GOT contents, a GOT address needs to be known at link-time // (which means non-PIC) or compilers have to emit code to get a GOT // address at runtime (which means code is position-independent but // compilers need to emit extra code for each GOT access.) This decision // is made at compile-time. In the latter case, compilers emit code to // load an GOT address to a register, which is usually %ebx. // // So, there are two ways to refer to symbol foo's GOT entry: foo@GOT or // foo@GOT(%reg). // // foo@GOT is not usable in PIC. If we are creating a PIC output and if we // find such relocation, we should report an error. foo@GOT is resolved to // an *absolute* address of foo's GOT entry, because both GOT address and // foo's offset are known. In other words, it's G + A. // // foo@GOT(%reg) needs to be resolved to a *relative* offset from a GOT to // foo's GOT entry in the table, because GOT address is not known but foo's // offset in the table is known. It's G + A - GOT. // // It's unfortunate that compilers emit the same relocation for these // different use cases. In order to distinguish them, we have to read a // machine instruction. // // The following code implements it. We assume that Loc[0] is the first // byte of a displacement or an immediate field of a valid machine // instruction. That means a ModRM byte is at Loc[-1]. By taking a look at // the byte, we can determine whether the instruction is register-relative // (i.e. it was generated for foo@GOT(%reg)) or absolute (i.e. foo@GOT). return hasBaseReg(Loc[-1]) ? R_GOT_FROM_END : R_GOT; case R_386_TLS_GOTIE: return R_GOT_FROM_END; case R_386_GOTOFF: return R_GOTREL_FROM_END; case R_386_TLS_LE: return R_TLS; case R_386_TLS_LE_32: return R_NEG_TLS; case R_386_NONE: return R_NONE; default: return R_INVALID; } } RelExpr X86::adjustRelaxExpr(RelType Type, const uint8_t *Data, RelExpr Expr) const { switch (Expr) { default: return Expr; case R_RELAX_TLS_GD_TO_IE: return R_RELAX_TLS_GD_TO_IE_END; case R_RELAX_TLS_GD_TO_LE: return R_RELAX_TLS_GD_TO_LE_NEG; } } void X86::writeGotPltHeader(uint8_t *Buf) const { write32le(Buf, InX::Dynamic->getVA()); } void X86::writeGotPlt(uint8_t *Buf, const Symbol &S) const { // Entries in .got.plt initially points back to the corresponding // PLT entries with a fixed offset to skip the first instruction. write32le(Buf, S.getPltVA() + 6); } void X86::writeIgotPlt(uint8_t *Buf, const Symbol &S) const { // An x86 entry is the address of the ifunc resolver function. write32le(Buf, S.getVA()); } RelType X86::getDynRel(RelType Type) const { if (Type == R_386_TLS_LE) return R_386_TLS_TPOFF; if (Type == R_386_TLS_LE_32) return R_386_TLS_TPOFF32; return Type; } void X86::writePltHeader(uint8_t *Buf) const { if (Config->Pic) { const uint8_t V[] = { 0xff, 0xb3, 0x04, 0x00, 0x00, 0x00, // pushl GOTPLT+4(%ebx) 0xff, 0xa3, 0x08, 0x00, 0x00, 0x00, // jmp *GOTPLT+8(%ebx) 0x90, 0x90, 0x90, 0x90 // nop }; memcpy(Buf, V, sizeof(V)); uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize(); uint32_t GotPlt = InX::GotPlt->getVA() - Ebx; write32le(Buf + 2, GotPlt + 4); write32le(Buf + 8, GotPlt + 8); return; } const uint8_t PltData[] = { - 0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // pushl (GOTPLT+4) - 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *(GOTPLT+8) - 0x90, 0x90, 0x90, 0x90 // nop + 0xff, 0x35, 0, 0, 0, 0, // pushl (GOTPLT+4) + 0xff, 0x25, 0, 0, 0, 0, // jmp *(GOTPLT+8) + 0x90, 0x90, 0x90, 0x90, // nop }; memcpy(Buf, PltData, sizeof(PltData)); uint32_t GotPlt = InX::GotPlt->getVA(); write32le(Buf + 2, GotPlt + 4); write32le(Buf + 8, GotPlt + 8); } void X86::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const { const uint8_t Inst[] = { - 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, // jmp *foo_in_GOT|*foo@GOT(%ebx) - 0x68, 0x00, 0x00, 0x00, 0x00, // pushl $reloc_offset - 0xe9, 0x00, 0x00, 0x00, 0x00 // jmp .PLT0@PC + 0xff, 0x00, 0, 0, 0, 0, // jmp *foo_in_GOT or jmp *foo@GOT(%ebx) + 0x68, 0, 0, 0, 0, // pushl $reloc_offset + 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC }; memcpy(Buf, Inst, sizeof(Inst)); if (Config->Pic) { // jmp *foo@GOT(%ebx) uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize(); Buf[1] = 0xa3; write32le(Buf + 2, GotPltEntryAddr - Ebx); } else { // jmp *foo_in_GOT Buf[1] = 0x25; write32le(Buf + 2, GotPltEntryAddr); } write32le(Buf + 7, RelOff); write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16); } int64_t X86::getImplicitAddend(const uint8_t *Buf, RelType Type) const { switch (Type) { case R_386_8: case R_386_PC8: return SignExtend64<8>(*Buf); case R_386_16: case R_386_PC16: return SignExtend64<16>(read16le(Buf)); case R_386_32: case R_386_GOT32: case R_386_GOT32X: case R_386_GOTOFF: case R_386_GOTPC: case R_386_PC32: case R_386_PLT32: case R_386_TLS_LDO_32: case R_386_TLS_LE: return SignExtend64<32>(read32le(Buf)); default: return 0; } } void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { switch (Type) { case R_386_8: // R_386_{PC,}{8,16} are not part of the i386 psABI, but they are // being used for some 16-bit programs such as boot loaders, so // we want to support them. checkUInt<8>(Loc, Val, Type); *Loc = Val; break; case R_386_PC8: checkInt<8>(Loc, Val, Type); *Loc = Val; break; case R_386_16: checkUInt<16>(Loc, Val, Type); write16le(Loc, Val); break; case R_386_PC16: // R_386_PC16 is normally used with 16 bit code. In that situation // the PC is 16 bits, just like the addend. This means that it can // point from any 16 bit address to any other if the possibility // of wrapping is included. // The only restriction we have to check then is that the destination // address fits in 16 bits. That is impossible to do here. The problem is // that we are passed the final value, which already had the // current location subtracted from it. // We just check that Val fits in 17 bits. This misses some cases, but // should have no false positives. checkInt<17>(Loc, Val, Type); write16le(Loc, Val); break; case R_386_32: case R_386_GLOB_DAT: case R_386_GOT32: case R_386_GOT32X: case R_386_GOTOFF: case R_386_GOTPC: case R_386_PC32: case R_386_PLT32: case R_386_RELATIVE: case R_386_TLS_DTPMOD32: case R_386_TLS_DTPOFF32: case R_386_TLS_GD: case R_386_TLS_GOTIE: case R_386_TLS_IE: case R_386_TLS_LDM: case R_386_TLS_LDO_32: case R_386_TLS_LE: case R_386_TLS_LE_32: case R_386_TLS_TPOFF: case R_386_TLS_TPOFF32: checkInt<32>(Loc, Val, Type); write32le(Loc, Val); break; default: error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); } } void X86::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { // Convert // leal x@tlsgd(, %ebx, 1), // call __tls_get_addr@plt // to // movl %gs:0,%eax // subl $x@ntpoff,%eax const uint8_t Inst[] = { 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax - 0x81, 0xe8, 0x00, 0x00, 0x00, 0x00 // subl 0(%ebx), %eax + 0x81, 0xe8, 0, 0, 0, 0, // subl Val(%ebx), %eax }; memcpy(Loc - 3, Inst, sizeof(Inst)); write32le(Loc + 5, Val); } void X86::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const { // Convert // leal x@tlsgd(, %ebx, 1), // call __tls_get_addr@plt // to // movl %gs:0, %eax // addl x@gotntpoff(%ebx), %eax const uint8_t Inst[] = { 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax - 0x03, 0x83, 0x00, 0x00, 0x00, 0x00 // addl 0(%ebx), %eax + 0x03, 0x83, 0, 0, 0, 0, // addl Val(%ebx), %eax }; memcpy(Loc - 3, Inst, sizeof(Inst)); write32le(Loc + 5, Val); } // In some conditions, relocations can be optimized to avoid using GOT. // This function does that for Initial Exec to Local Exec case. void X86::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { // Ulrich's document section 6.2 says that @gotntpoff can // be used with MOVL or ADDL instructions. // @indntpoff is similar to @gotntpoff, but for use in // position dependent code. uint8_t Reg = (Loc[-1] >> 3) & 7; if (Type == R_386_TLS_IE) { if (Loc[-1] == 0xa1) { // "movl foo@indntpoff,%eax" -> "movl $foo,%eax" // This case is different from the generic case below because // this is a 5 byte instruction while below is 6 bytes. Loc[-1] = 0xb8; } else if (Loc[-2] == 0x8b) { // "movl foo@indntpoff,%reg" -> "movl $foo,%reg" Loc[-2] = 0xc7; Loc[-1] = 0xc0 | Reg; } else { // "addl foo@indntpoff,%reg" -> "addl $foo,%reg" Loc[-2] = 0x81; Loc[-1] = 0xc0 | Reg; } } else { assert(Type == R_386_TLS_GOTIE); if (Loc[-2] == 0x8b) { // "movl foo@gottpoff(%rip),%reg" -> "movl $foo,%reg" Loc[-2] = 0xc7; Loc[-1] = 0xc0 | Reg; } else { // "addl foo@gotntpoff(%rip),%reg" -> "leal foo(%reg),%reg" Loc[-2] = 0x8d; Loc[-1] = 0x80 | (Reg << 3) | Reg; } } write32le(Loc, Val); } void X86::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { if (Type == R_386_TLS_LDO_32) { write32le(Loc, Val); return; } // Convert // leal foo(%reg),%eax // call ___tls_get_addr // to // movl %gs:0,%eax // nop // leal 0(%esi,1),%esi const uint8_t Inst[] = { 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0,%eax 0x90, // nop - 0x8d, 0x74, 0x26, 0x00 // leal 0(%esi,1),%esi + 0x8d, 0x74, 0x26, 0x00, // leal 0(%esi,1),%esi }; memcpy(Loc - 2, Inst, sizeof(Inst)); } TargetInfo *elf::getX86TargetInfo() { static X86 Target; return &Target; } Index: vendor/lld/dist/ELF/Arch/X86_64.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/X86_64.cpp (revision 327307) +++ vendor/lld/dist/ELF/Arch/X86_64.cpp (revision 327308) @@ -1,471 +1,471 @@ //===- X86_64.cpp ---------------------------------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; using namespace lld; using namespace lld::elf; namespace { template class X86_64 final : public TargetInfo { public: X86_64(); RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const override; bool isPicRel(RelType Type) const override; void writeGotPltHeader(uint8_t *Buf) const override; void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; void writePltHeader(uint8_t *Buf) const override; void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, RelExpr Expr) const override; void relaxGot(uint8_t *Loc, uint64_t Val) const override; void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override; void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; private: void relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op, uint8_t ModRm) const; }; } // namespace template X86_64::X86_64() { GotBaseSymOff = -1; CopyRel = R_X86_64_COPY; GotRel = R_X86_64_GLOB_DAT; PltRel = R_X86_64_JUMP_SLOT; RelativeRel = R_X86_64_RELATIVE; IRelativeRel = R_X86_64_IRELATIVE; TlsGotRel = R_X86_64_TPOFF64; TlsModuleIndexRel = R_X86_64_DTPMOD64; TlsOffsetRel = R_X86_64_DTPOFF64; GotEntrySize = 8; GotPltEntrySize = 8; PltEntrySize = 16; PltHeaderSize = 16; TlsGdRelaxSkip = 2; TrapInstr = 0xcccccccc; // 0xcc = INT3 // Align to the large page size (known as a superpage or huge page). // FreeBSD automatically promotes large, superpage-aligned allocations. DefaultImageBase = 0x200000; } template RelExpr X86_64::getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const { switch (Type) { case R_X86_64_8: case R_X86_64_16: case R_X86_64_32: case R_X86_64_32S: case R_X86_64_64: case R_X86_64_DTPOFF32: case R_X86_64_DTPOFF64: return R_ABS; case R_X86_64_TPOFF32: return R_TLS; case R_X86_64_TLSLD: return R_TLSLD_PC; case R_X86_64_TLSGD: return R_TLSGD_PC; case R_X86_64_SIZE32: case R_X86_64_SIZE64: return R_SIZE; case R_X86_64_PLT32: return R_PLT_PC; case R_X86_64_PC32: case R_X86_64_PC64: return R_PC; case R_X86_64_GOT32: case R_X86_64_GOT64: return R_GOT_FROM_END; case R_X86_64_GOTPCREL: case R_X86_64_GOTPCRELX: case R_X86_64_REX_GOTPCRELX: case R_X86_64_GOTTPOFF: return R_GOT_PC; case R_X86_64_NONE: return R_NONE; default: return R_INVALID; } } template void X86_64::writeGotPltHeader(uint8_t *Buf) const { // The first entry holds the value of _DYNAMIC. It is not clear why that is // required, but it is documented in the psabi and the glibc dynamic linker // seems to use it (note that this is relevant for linking ld.so, not any // other program). write64le(Buf, InX::Dynamic->getVA()); } template void X86_64::writeGotPlt(uint8_t *Buf, const Symbol &S) const { // See comments in X86::writeGotPlt. write32le(Buf, S.getPltVA() + 6); } template void X86_64::writePltHeader(uint8_t *Buf) const { const uint8_t PltData[] = { - 0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // pushq GOTPLT+8(%rip) - 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *GOTPLT+16(%rip) - 0x0f, 0x1f, 0x40, 0x00 // nop + 0xff, 0x35, 0, 0, 0, 0, // pushq GOTPLT+8(%rip) + 0xff, 0x25, 0, 0, 0, 0, // jmp *GOTPLT+16(%rip) + 0x0f, 0x1f, 0x40, 0x00, // nop }; memcpy(Buf, PltData, sizeof(PltData)); uint64_t GotPlt = InX::GotPlt->getVA(); uint64_t Plt = InX::Plt->getVA(); write32le(Buf + 2, GotPlt - Plt + 2); // GOTPLT+8 write32le(Buf + 8, GotPlt - Plt + 4); // GOTPLT+16 } template void X86_64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const { const uint8_t Inst[] = { - 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmpq *got(%rip) - 0x68, 0x00, 0x00, 0x00, 0x00, // pushq - 0xe9, 0x00, 0x00, 0x00, 0x00 // jmpq plt[0] + 0xff, 0x25, 0, 0, 0, 0, // jmpq *got(%rip) + 0x68, 0, 0, 0, 0, // pushq + 0xe9, 0, 0, 0, 0, // jmpq plt[0] }; memcpy(Buf, Inst, sizeof(Inst)); write32le(Buf + 2, GotPltEntryAddr - PltEntryAddr - 6); write32le(Buf + 7, Index); write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16); } template bool X86_64::isPicRel(RelType Type) const { return Type != R_X86_64_PC32 && Type != R_X86_64_32 && Type != R_X86_64_TPOFF32; } template void X86_64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { // Convert // .byte 0x66 // leaq x@tlsgd(%rip), %rdi // .word 0x6666 // rex64 // call __tls_get_addr@plt // to // mov %fs:0x0,%rax // lea x@tpoff,%rax const uint8_t Inst[] = { 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0x0,%rax - 0x48, 0x8d, 0x80, 0x00, 0x00, 0x00, 0x00 // lea x@tpoff,%rax + 0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea x@tpoff,%rax }; memcpy(Loc - 4, Inst, sizeof(Inst)); // The original code used a pc relative relocation and so we have to // compensate for the -4 in had in the addend. write32le(Loc + 8, Val + 4); } template void X86_64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const { // Convert // .byte 0x66 // leaq x@tlsgd(%rip), %rdi // .word 0x6666 // rex64 // call __tls_get_addr@plt // to // mov %fs:0x0,%rax // addq x@tpoff,%rax const uint8_t Inst[] = { 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0x0,%rax - 0x48, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00 // addq x@tpoff,%rax + 0x48, 0x03, 0x05, 0, 0, 0, 0, // addq x@tpoff,%rax }; memcpy(Loc - 4, Inst, sizeof(Inst)); // Both code sequences are PC relatives, but since we are moving the constant // forward by 8 bytes we have to subtract the value by 8. write32le(Loc + 8, Val - 8); } // In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to // R_X86_64_TPOFF32 so that it does not use GOT. template void X86_64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { uint8_t *Inst = Loc - 3; uint8_t Reg = Loc[-1] >> 3; uint8_t *RegSlot = Loc - 1; // Note that ADD with RSP or R12 is converted to ADD instead of LEA // because LEA with these registers needs 4 bytes to encode and thus // wouldn't fit the space. if (memcmp(Inst, "\x48\x03\x25", 3) == 0) { // "addq foo@gottpoff(%rip),%rsp" -> "addq $foo,%rsp" memcpy(Inst, "\x48\x81\xc4", 3); } else if (memcmp(Inst, "\x4c\x03\x25", 3) == 0) { // "addq foo@gottpoff(%rip),%r12" -> "addq $foo,%r12" memcpy(Inst, "\x49\x81\xc4", 3); } else if (memcmp(Inst, "\x4c\x03", 2) == 0) { // "addq foo@gottpoff(%rip),%r[8-15]" -> "leaq foo(%r[8-15]),%r[8-15]" memcpy(Inst, "\x4d\x8d", 2); *RegSlot = 0x80 | (Reg << 3) | Reg; } else if (memcmp(Inst, "\x48\x03", 2) == 0) { // "addq foo@gottpoff(%rip),%reg -> "leaq foo(%reg),%reg" memcpy(Inst, "\x48\x8d", 2); *RegSlot = 0x80 | (Reg << 3) | Reg; } else if (memcmp(Inst, "\x4c\x8b", 2) == 0) { // "movq foo@gottpoff(%rip),%r[8-15]" -> "movq $foo,%r[8-15]" memcpy(Inst, "\x49\xc7", 2); *RegSlot = 0xc0 | Reg; } else if (memcmp(Inst, "\x48\x8b", 2) == 0) { // "movq foo@gottpoff(%rip),%reg" -> "movq $foo,%reg" memcpy(Inst, "\x48\xc7", 2); *RegSlot = 0xc0 | Reg; } else { error(getErrorLocation(Loc - 3) + "R_X86_64_GOTTPOFF must be used in MOVQ or ADDQ instructions only"); } // The original code used a PC relative relocation. // Need to compensate for the -4 it had in the addend. write32le(Loc, Val + 4); } template void X86_64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { // Convert // leaq bar@tlsld(%rip), %rdi // callq __tls_get_addr@PLT // leaq bar@dtpoff(%rax), %rcx // to // .word 0x6666 // .byte 0x66 // mov %fs:0,%rax // leaq bar@tpoff(%rax), %rcx if (Type == R_X86_64_DTPOFF64) { write64le(Loc, Val); return; } if (Type == R_X86_64_DTPOFF32) { write32le(Loc, Val); return; } const uint8_t Inst[] = { - 0x66, 0x66, // .word 0x6666 - 0x66, // .byte 0x66 - 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00 // mov %fs:0,%rax + 0x66, 0x66, // .word 0x6666 + 0x66, // .byte 0x66 + 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0,%rax }; memcpy(Loc - 3, Inst, sizeof(Inst)); } template void X86_64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { switch (Type) { case R_X86_64_8: checkUInt<8>(Loc, Val, Type); *Loc = Val; break; case R_X86_64_16: checkUInt<16>(Loc, Val, Type); write16le(Loc, Val); break; case R_X86_64_32: checkUInt<32>(Loc, Val, Type); write32le(Loc, Val); break; case R_X86_64_32S: case R_X86_64_TPOFF32: case R_X86_64_GOT32: case R_X86_64_GOTPCREL: case R_X86_64_GOTPCRELX: case R_X86_64_REX_GOTPCRELX: case R_X86_64_PC32: case R_X86_64_GOTTPOFF: case R_X86_64_PLT32: case R_X86_64_TLSGD: case R_X86_64_TLSLD: case R_X86_64_DTPOFF32: case R_X86_64_SIZE32: checkInt<32>(Loc, Val, Type); write32le(Loc, Val); break; case R_X86_64_64: case R_X86_64_DTPOFF64: case R_X86_64_GLOB_DAT: case R_X86_64_PC64: case R_X86_64_SIZE64: case R_X86_64_GOT64: write64le(Loc, Val); break; default: error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); } } template RelExpr X86_64::adjustRelaxExpr(RelType Type, const uint8_t *Data, RelExpr RelExpr) const { if (Type != R_X86_64_GOTPCRELX && Type != R_X86_64_REX_GOTPCRELX) return RelExpr; const uint8_t Op = Data[-2]; const uint8_t ModRm = Data[-1]; // FIXME: When PIC is disabled and foo is defined locally in the // lower 32 bit address space, memory operand in mov can be converted into // immediate operand. Otherwise, mov must be changed to lea. We support only // latter relaxation at this moment. if (Op == 0x8b) return R_RELAX_GOT_PC; // Relax call and jmp. if (Op == 0xff && (ModRm == 0x15 || ModRm == 0x25)) return R_RELAX_GOT_PC; // Relaxation of test, adc, add, and, cmp, or, sbb, sub, xor. // If PIC then no relaxation is available. // We also don't relax test/binop instructions without REX byte, // they are 32bit operations and not common to have. assert(Type == R_X86_64_REX_GOTPCRELX); return Config->Pic ? RelExpr : R_RELAX_GOT_PC_NOPIC; } // A subset of relaxations can only be applied for no-PIC. This method // handles such relaxations. Instructions encoding information was taken from: // "Intel 64 and IA-32 Architectures Software Developer's Manual V2" // (http://www.intel.com/content/dam/www/public/us/en/documents/manuals/ // 64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf) template void X86_64::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op, uint8_t ModRm) const { const uint8_t Rex = Loc[-3]; // Convert "test %reg, foo@GOTPCREL(%rip)" to "test $foo, %reg". if (Op == 0x85) { // See "TEST-Logical Compare" (4-428 Vol. 2B), // TEST r/m64, r64 uses "full" ModR / M byte (no opcode extension). // ModR/M byte has form XX YYY ZZZ, where // YYY is MODRM.reg(register 2), ZZZ is MODRM.rm(register 1). // XX has different meanings: // 00: The operand's memory address is in reg1. // 01: The operand's memory address is reg1 + a byte-sized displacement. // 10: The operand's memory address is reg1 + a word-sized displacement. // 11: The operand is reg1 itself. // If an instruction requires only one operand, the unused reg2 field // holds extra opcode bits rather than a register code // 0xC0 == 11 000 000 binary. // 0x38 == 00 111 000 binary. // We transfer reg2 to reg1 here as operand. // See "2.1.3 ModR/M and SIB Bytes" (Vol. 2A 2-3). Loc[-1] = 0xc0 | (ModRm & 0x38) >> 3; // ModR/M byte. // Change opcode from TEST r/m64, r64 to TEST r/m64, imm32 // See "TEST-Logical Compare" (4-428 Vol. 2B). Loc[-2] = 0xf7; // Move R bit to the B bit in REX byte. // REX byte is encoded as 0100WRXB, where // 0100 is 4bit fixed pattern. // REX.W When 1, a 64-bit operand size is used. Otherwise, when 0, the // default operand size is used (which is 32-bit for most but not all // instructions). // REX.R This 1-bit value is an extension to the MODRM.reg field. // REX.X This 1-bit value is an extension to the SIB.index field. // REX.B This 1-bit value is an extension to the MODRM.rm field or the // SIB.base field. // See "2.2.1.2 More on REX Prefix Fields " (2-8 Vol. 2A). Loc[-3] = (Rex & ~0x4) | (Rex & 0x4) >> 2; write32le(Loc, Val); return; } // If we are here then we need to relax the adc, add, and, cmp, or, sbb, sub // or xor operations. // Convert "binop foo@GOTPCREL(%rip), %reg" to "binop $foo, %reg". // Logic is close to one for test instruction above, but we also // write opcode extension here, see below for details. Loc[-1] = 0xc0 | (ModRm & 0x38) >> 3 | (Op & 0x3c); // ModR/M byte. // Primary opcode is 0x81, opcode extension is one of: // 000b = ADD, 001b is OR, 010b is ADC, 011b is SBB, // 100b is AND, 101b is SUB, 110b is XOR, 111b is CMP. // This value was wrote to MODRM.reg in a line above. // See "3.2 INSTRUCTIONS (A-M)" (Vol. 2A 3-15), // "INSTRUCTION SET REFERENCE, N-Z" (Vol. 2B 4-1) for // descriptions about each operation. Loc[-2] = 0x81; Loc[-3] = (Rex & ~0x4) | (Rex & 0x4) >> 2; write32le(Loc, Val); } template void X86_64::relaxGot(uint8_t *Loc, uint64_t Val) const { const uint8_t Op = Loc[-2]; const uint8_t ModRm = Loc[-1]; // Convert "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg". if (Op == 0x8b) { Loc[-2] = 0x8d; write32le(Loc, Val); return; } if (Op != 0xff) { // We are relaxing a rip relative to an absolute, so compensate // for the old -4 addend. assert(!Config->Pic); relaxGotNoPic(Loc, Val + 4, Op, ModRm); return; } // Convert call/jmp instructions. if (ModRm == 0x15) { // ABI says we can convert "call *foo@GOTPCREL(%rip)" to "nop; call foo". // Instead we convert to "addr32 call foo" where addr32 is an instruction // prefix. That makes result expression to be a single instruction. Loc[-2] = 0x67; // addr32 prefix Loc[-1] = 0xe8; // call write32le(Loc, Val); return; } // Convert "jmp *foo@GOTPCREL(%rip)" to "jmp foo; nop". // jmp doesn't return, so it is fine to use nop here, it is just a stub. assert(ModRm == 0x25); Loc[-2] = 0xe9; // jmp Loc[3] = 0x90; // nop write32le(Loc - 1, Val + 1); } TargetInfo *elf::getX32TargetInfo() { static X86_64 Target; return &Target; } TargetInfo *elf::getX86_64TargetInfo() { static X86_64 Target; return &Target; } Index: vendor/lld/dist/ELF/Relocations.cpp =================================================================== --- vendor/lld/dist/ELF/Relocations.cpp (revision 327307) +++ vendor/lld/dist/ELF/Relocations.cpp (revision 327308) @@ -1,1422 +1,1421 @@ //===- Relocations.cpp ----------------------------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file contains platform-independent functions to process relocations. // I'll describe the overview of this file here. // // Simple relocations are easy to handle for the linker. For example, // for R_X86_64_PC64 relocs, the linker just has to fix up locations // with the relative offsets to the target symbols. It would just be // reading records from relocation sections and applying them to output. // // But not all relocations are that easy to handle. For example, for // R_386_GOTOFF relocs, the linker has to create new GOT entries for // symbols if they don't exist, and fix up locations with GOT entry // offsets from the beginning of GOT section. So there is more than // fixing addresses in relocation processing. // // ELF defines a large number of complex relocations. // // The functions in this file analyze relocations and do whatever needs // to be done. It includes, but not limited to, the following. // // - create GOT/PLT entries // - create new relocations in .dynsym to let the dynamic linker resolve // them at runtime (since ELF supports dynamic linking, not all // relocations can be resolved at link-time) // - create COPY relocs and reserve space in .bss // - replace expensive relocs (in terms of runtime cost) with cheap ones // - error out infeasible combinations such as PIC and non-relative relocs // // Note that the functions in this file don't actually apply relocations // because it doesn't know about the output file nor the output file buffer. // It instead stores Relocation objects to InputSection's Relocations // vector to let it apply later in InputSection::writeTo. // //===----------------------------------------------------------------------===// #include "Relocations.h" #include "Config.h" #include "LinkerScript.h" #include "OutputSections.h" #include "Strings.h" #include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "Thunks.h" #include "lld/Common/Memory.h" #include "llvm/Support/Endian.h" #include "llvm/Support/raw_ostream.h" #include using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; using namespace llvm::support::endian; using namespace lld; using namespace lld::elf; // Construct a message in the following format. // // >>> defined in /home/alice/src/foo.o // >>> referenced by bar.c:12 (/home/alice/src/bar.c:12) // >>> /home/alice/src/bar.o:(.text+0x1) static std::string getLocation(InputSectionBase &S, const Symbol &Sym, uint64_t Off) { std::string Msg = "\n>>> defined in " + toString(Sym.File) + "\n>>> referenced by "; std::string Src = S.getSrcMsg(Sym, Off); if (!Src.empty()) Msg += Src + "\n>>> "; return Msg + S.getObjMsg(Off); } // This is a MIPS-specific rule. // // In case of MIPS GP-relative relocations always resolve to a definition // in a regular input file, ignoring the one-definition rule. So we, // for example, should not attempt to create a dynamic relocation even // if the target symbol is preemptible. There are two two MIPS GP-relative // relocations R_MIPS_GPREL16 and R_MIPS_GPREL32. But only R_MIPS_GPREL16 // can be against a preemptible symbol. // // To get MIPS relocation type we apply 0xff mask. In case of O32 ABI all // relocation types occupy eight bit. In case of N64 ABI we extract first // relocation from 3-in-1 packet because only the first relocation can // be against a real symbol. static bool isMipsGprel(RelType Type) { if (Config->EMachine != EM_MIPS) return false; Type &= 0xff; return Type == R_MIPS_GPREL16 || Type == R_MICROMIPS_GPREL16 || Type == R_MICROMIPS_GPREL7_S2; } // This function is similar to the `handleTlsRelocation`. MIPS does not // support any relaxations for TLS relocations so by factoring out MIPS // handling in to the separate function we can simplify the code and do not // pollute other `handleTlsRelocation` by MIPS `ifs` statements. // Mips has a custom MipsGotSection that handles the writing of GOT entries // without dynamic relocations. template static unsigned handleMipsTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C, uint64_t Offset, int64_t Addend, RelExpr Expr) { if (Expr == R_MIPS_TLSLD) { if (InX::MipsGot->addTlsIndex() && Config->Pic) InX::RelaDyn->addReloc({Target->TlsModuleIndexRel, InX::MipsGot, InX::MipsGot->getTlsIndexOff(), false, nullptr, 0}); C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return 1; } if (Expr == R_MIPS_TLSGD) { if (InX::MipsGot->addDynTlsEntry(Sym) && Sym.IsPreemptible) { uint64_t Off = InX::MipsGot->getGlobalDynOffset(Sym); InX::RelaDyn->addReloc( {Target->TlsModuleIndexRel, InX::MipsGot, Off, false, &Sym, 0}); if (Sym.IsPreemptible) InX::RelaDyn->addReloc({Target->TlsOffsetRel, InX::MipsGot, Off + Config->Wordsize, false, &Sym, 0}); } C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return 1; } return 0; } // This function is similar to the `handleMipsTlsRelocation`. ARM also does not // support any relaxations for TLS relocations. ARM is logically similar to Mips // in how it handles TLS, but Mips uses its own custom GOT which handles some // of the cases that ARM uses GOT relocations for. // // We look for TLS global dynamic and local dynamic relocations, these may // require the generation of a pair of GOT entries that have associated // dynamic relocations. When the results of the dynamic relocations can be // resolved at static link time we do so. This is necessary for static linking // as there will be no dynamic loader to resolve them at load-time. // // The pair of GOT entries created are of the form // GOT[e0] Module Index (Used to find pointer to TLS block at run-time) // GOT[e1] Offset of symbol in TLS block template static unsigned handleARMTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C, uint64_t Offset, int64_t Addend, RelExpr Expr) { // The Dynamic TLS Module Index Relocation for a symbol defined in an // executable is always 1. If the target Symbol is not preemptible then // we know the offset into the TLS block at static link time. bool NeedDynId = Sym.IsPreemptible || Config->Shared; bool NeedDynOff = Sym.IsPreemptible; auto AddTlsReloc = [&](uint64_t Off, RelType Type, Symbol *Dest, bool Dyn) { if (Dyn) InX::RelaDyn->addReloc({Type, InX::Got, Off, false, Dest, 0}); else InX::Got->Relocations.push_back({R_ABS, Type, Off, 0, Dest}); }; // Local Dynamic is for access to module local TLS variables, while still // being suitable for being dynamically loaded via dlopen. // GOT[e0] is the module index, with a special value of 0 for the current // module. GOT[e1] is unused. There only needs to be one module index entry. if (Expr == R_TLSLD_PC && InX::Got->addTlsIndex()) { AddTlsReloc(InX::Got->getTlsIndexOff(), Target->TlsModuleIndexRel, NeedDynId ? nullptr : &Sym, NeedDynId); C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return 1; } // Global Dynamic is the most general purpose access model. When we know // the module index and offset of symbol in TLS block we can fill these in // using static GOT relocations. if (Expr == R_TLSGD_PC) { if (InX::Got->addDynTlsEntry(Sym)) { uint64_t Off = InX::Got->getGlobalDynOffset(Sym); AddTlsReloc(Off, Target->TlsModuleIndexRel, &Sym, NeedDynId); AddTlsReloc(Off + Config->Wordsize, Target->TlsOffsetRel, &Sym, NeedDynOff); } C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return 1; } return 0; } // Returns the number of relocations processed. template static unsigned handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C, typename ELFT::uint Offset, int64_t Addend, RelExpr Expr) { if (!(C.Flags & SHF_ALLOC)) return 0; if (!Sym.isTls()) return 0; if (Config->EMachine == EM_ARM) return handleARMTlsRelocation(Type, Sym, C, Offset, Addend, Expr); if (Config->EMachine == EM_MIPS) return handleMipsTlsRelocation(Type, Sym, C, Offset, Addend, Expr); if (isRelExprOneOf(Expr) && Config->Shared) { if (InX::Got->addDynTlsEntry(Sym)) { uint64_t Off = InX::Got->getGlobalDynOffset(Sym); InX::RelaDyn->addReloc( {Target->TlsDescRel, InX::Got, Off, !Sym.IsPreemptible, &Sym, 0}); } if (Expr != R_TLSDESC_CALL) C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return 1; } if (isRelExprOneOf(Expr)) { // Local-Dynamic relocs can be relaxed to Local-Exec. if (!Config->Shared) { C.Relocations.push_back( {R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Sym}); return 2; } if (InX::Got->addTlsIndex()) InX::RelaDyn->addReloc({Target->TlsModuleIndexRel, InX::Got, InX::Got->getTlsIndexOff(), false, nullptr, 0}); C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return 1; } // Local-Dynamic relocs can be relaxed to Local-Exec. if (isRelExprOneOf(Expr) && !Config->Shared) { C.Relocations.push_back({R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Sym}); return 1; } if (isRelExprOneOf(Expr)) { if (Config->Shared) { if (InX::Got->addDynTlsEntry(Sym)) { uint64_t Off = InX::Got->getGlobalDynOffset(Sym); InX::RelaDyn->addReloc( {Target->TlsModuleIndexRel, InX::Got, Off, false, &Sym, 0}); // If the symbol is preemptible we need the dynamic linker to write // the offset too. uint64_t OffsetOff = Off + Config->Wordsize; if (Sym.IsPreemptible) InX::RelaDyn->addReloc( {Target->TlsOffsetRel, InX::Got, OffsetOff, false, &Sym, 0}); else InX::Got->Relocations.push_back( {R_ABS, Target->TlsOffsetRel, OffsetOff, 0, &Sym}); } C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return 1; } // Global-Dynamic relocs can be relaxed to Initial-Exec or Local-Exec // depending on the symbol being locally defined or not. if (Sym.IsPreemptible) { C.Relocations.push_back( {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_IE), Type, Offset, Addend, &Sym}); if (!Sym.isInGot()) { InX::Got->addEntry(Sym); InX::RelaDyn->addReloc( {Target->TlsGotRel, InX::Got, Sym.getGotOffset(), false, &Sym, 0}); } } else { C.Relocations.push_back( {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_LE), Type, Offset, Addend, &Sym}); } return Target->TlsGdRelaxSkip; } // Initial-Exec relocs can be relaxed to Local-Exec if the symbol is locally // defined. if (isRelExprOneOf(Expr) && !Config->Shared && !Sym.IsPreemptible) { C.Relocations.push_back({R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Sym}); return 1; } if (Expr == R_TLSDESC_CALL) return 1; return 0; } static RelType getMipsPairType(RelType Type, bool IsLocal) { switch (Type) { case R_MIPS_HI16: return R_MIPS_LO16; case R_MIPS_GOT16: // In case of global symbol, the R_MIPS_GOT16 relocation does not // have a pair. Each global symbol has a unique entry in the GOT // and a corresponding instruction with help of the R_MIPS_GOT16 // relocation loads an address of the symbol. In case of local // symbol, the R_MIPS_GOT16 relocation creates a GOT entry to hold // the high 16 bits of the symbol's value. A paired R_MIPS_LO16 // relocations handle low 16 bits of the address. That allows // to allocate only one GOT entry for every 64 KBytes of local data. return IsLocal ? R_MIPS_LO16 : R_MIPS_NONE; case R_MICROMIPS_GOT16: return IsLocal ? R_MICROMIPS_LO16 : R_MIPS_NONE; case R_MIPS_PCHI16: return R_MIPS_PCLO16; case R_MICROMIPS_HI16: return R_MICROMIPS_LO16; default: return R_MIPS_NONE; } } // True if non-preemptable symbol always has the same value regardless of where // the DSO is loaded. static bool isAbsolute(const Symbol &Sym) { if (Sym.isUndefWeak()) return true; if (const auto *DR = dyn_cast(&Sym)) return DR->Section == nullptr; // Absolute symbol. return false; } static bool isAbsoluteValue(const Symbol &Sym) { return isAbsolute(Sym) || Sym.isTls(); } // Returns true if Expr refers a PLT entry. static bool needsPlt(RelExpr Expr) { return isRelExprOneOf(Expr); } // Returns true if Expr refers a GOT entry. Note that this function // returns false for TLS variables even though they need GOT, because // TLS variables uses GOT differently than the regular variables. static bool needsGot(RelExpr Expr) { return isRelExprOneOf(Expr); } // True if this expression is of the form Sym - X, where X is a position in the // file (PC, or GOT for example). static bool isRelExpr(RelExpr Expr) { return isRelExprOneOf(Expr); } // Returns true if a given relocation can be computed at link-time. // // For instance, we know the offset from a relocation to its target at // link-time if the relocation is PC-relative and refers a // non-interposable function in the same executable. This function // will return true for such relocation. // // If this function returns false, that means we need to emit a // dynamic relocation so that the relocation will be fixed at load-time. static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym, InputSectionBase &S, uint64_t RelOff) { // These expressions always compute a constant if (isRelExprOneOf(E)) return true; // These never do, except if the entire file is position dependent or if // only the low bits are used. if (E == R_GOT || E == R_PLT || E == R_TLSDESC) return Target->usesOnlyLowPageBits(Type) || !Config->Pic; if (Sym.IsPreemptible) return false; if (!Config->Pic) return true; // For the target and the relocation, we want to know if they are // absolute or relative. bool AbsVal = isAbsoluteValue(Sym); bool RelE = isRelExpr(E); if (AbsVal && !RelE) return true; if (!AbsVal && RelE) return true; if (!AbsVal && !RelE) return Target->usesOnlyLowPageBits(Type); // Relative relocation to an absolute value. This is normally unrepresentable, // but if the relocation refers to a weak undefined symbol, we allow it to // resolve to the image base. This is a little strange, but it allows us to // link function calls to such symbols. Normally such a call will be guarded // with a comparison, which will load a zero from the GOT. // Another special case is MIPS _gp_disp symbol which represents offset // between start of a function and '_gp' value and defined as absolute just // to simplify the code. assert(AbsVal && RelE); if (Sym.isUndefWeak()) return true; error("relocation " + toString(Type) + " cannot refer to absolute symbol: " + toString(Sym) + getLocation(S, Sym, RelOff)); return true; } static RelExpr toPlt(RelExpr Expr) { if (Expr == R_PPC_OPD) return R_PPC_PLT_OPD; if (Expr == R_PC) return R_PLT_PC; if (Expr == R_PAGE_PC) return R_PLT_PAGE_PC; if (Expr == R_ABS) return R_PLT; return Expr; } static RelExpr fromPlt(RelExpr Expr) { // We decided not to use a plt. Optimize a reference to the plt to a // reference to the symbol itself. if (Expr == R_PLT_PC) return R_PC; if (Expr == R_PPC_PLT_OPD) return R_PPC_OPD; if (Expr == R_PLT) return R_ABS; return Expr; } // Returns true if a given shared symbol is in a read-only segment in a DSO. template static bool isReadOnly(SharedSymbol *SS) { typedef typename ELFT::Phdr Elf_Phdr; // Determine if the symbol is read-only by scanning the DSO's program headers. const SharedFile &File = SS->getFile(); for (const Elf_Phdr &Phdr : check(File.getObj().program_headers())) if ((Phdr.p_type == ELF::PT_LOAD || Phdr.p_type == ELF::PT_GNU_RELRO) && !(Phdr.p_flags & ELF::PF_W) && SS->Value >= Phdr.p_vaddr && SS->Value < Phdr.p_vaddr + Phdr.p_memsz) return true; return false; } // Returns symbols at the same offset as a given symbol, including SS itself. // // If two or more symbols are at the same offset, and at least one of // them are copied by a copy relocation, all of them need to be copied. // Otherwise, they would refer different places at runtime. template static std::vector getSymbolsAt(SharedSymbol *SS) { typedef typename ELFT::Sym Elf_Sym; SharedFile &File = SS->getFile(); std::vector Ret; for (const Elf_Sym &S : File.getGlobalELFSyms()) { if (S.st_shndx == SHN_UNDEF || S.st_shndx == SHN_ABS || S.st_value != SS->Value) continue; StringRef Name = check(S.getName(File.getStringTable())); Symbol *Sym = Symtab->find(Name); if (auto *Alias = dyn_cast_or_null(Sym)) Ret.push_back(Alias); } return Ret; } // Reserve space in .bss or .bss.rel.ro for copy relocation. // // The copy relocation is pretty much a hack. If you use a copy relocation // in your program, not only the symbol name but the symbol's size, RW/RO // bit and alignment become part of the ABI. In addition to that, if the // symbol has aliases, the aliases become part of the ABI. That's subtle, // but if you violate that implicit ABI, that can cause very counter- // intuitive consequences. // // So, what is the copy relocation? It's for linking non-position // independent code to DSOs. In an ideal world, all references to data // exported by DSOs should go indirectly through GOT. But if object files // are compiled as non-PIC, all data references are direct. There is no // way for the linker to transform the code to use GOT, as machine // instructions are already set in stone in object files. This is where // the copy relocation takes a role. // // A copy relocation instructs the dynamic linker to copy data from a DSO // to a specified address (which is usually in .bss) at load-time. If the // static linker (that's us) finds a direct data reference to a DSO // symbol, it creates a copy relocation, so that the symbol can be // resolved as if it were in .bss rather than in a DSO. // // As you can see in this function, we create a copy relocation for the // dynamic linker, and the relocation contains not only symbol name but // various other informtion about the symbol. So, such attributes become a // part of the ABI. // // Note for application developers: I can give you a piece of advice if // you are writing a shared library. You probably should export only // functions from your library. You shouldn't export variables. // // As an example what can happen when you export variables without knowing // the semantics of copy relocations, assume that you have an exported // variable of type T. It is an ABI-breaking change to add new members at // end of T even though doing that doesn't change the layout of the // existing members. That's because the space for the new members are not // reserved in .bss unless you recompile the main program. That means they // are likely to overlap with other data that happens to be laid out next // to the variable in .bss. This kind of issue is sometimes very hard to // debug. What's a solution? Instead of exporting a varaible V from a DSO, // define an accessor getV(). template static void addCopyRelSymbol(SharedSymbol *SS) { // Copy relocation against zero-sized symbol doesn't make sense. uint64_t SymSize = SS->getSize(); if (SymSize == 0) fatal("cannot create a copy relocation for symbol " + toString(*SS)); // See if this symbol is in a read-only segment. If so, preserve the symbol's // memory protection by reserving space in the .bss.rel.ro section. bool IsReadOnly = isReadOnly(SS); BssSection *Sec = make(IsReadOnly ? ".bss.rel.ro" : ".bss", SymSize, SS->Alignment); if (IsReadOnly) InX::BssRelRo->getParent()->addSection(Sec); else InX::Bss->getParent()->addSection(Sec); // Look through the DSO's dynamic symbol table for aliases and create a // dynamic symbol for each one. This causes the copy relocation to correctly // interpose any aliases. for (SharedSymbol *Sym : getSymbolsAt(SS)) { Sym->CopyRelSec = Sec; Sym->IsPreemptible = false; Sym->IsUsedInRegularObj = true; Sym->Used = true; } InX::RelaDyn->addReloc({Target->CopyRel, Sec, 0, false, SS, 0}); } static void errorOrWarn(const Twine &Msg) { if (!Config->NoinhibitExec) error(Msg); else warn(Msg); } // Returns PLT relocation expression. // // This handles a non PIC program call to function in a shared library. In // an ideal world, we could just report an error saying the relocation can // overflow at runtime. In the real world with glibc, crt1.o has a // R_X86_64_PC32 pointing to libc.so. // // The general idea on how to handle such cases is to create a PLT entry and // use that as the function value. // // For the static linking part, we just return a plt expr and everything // else will use the the PLT entry as the address. // // The remaining problem is making sure pointer equality still works. We // need the help of the dynamic linker for that. We let it know that we have // a direct reference to a so symbol by creating an undefined symbol with a // non zero st_value. Seeing that, the dynamic linker resolves the symbol to // the value of the symbol we created. This is true even for got entries, so // pointer equality is maintained. To avoid an infinite loop, the only entry // that points to the real function is a dedicated got entry used by the // plt. That is identified by special relocation types (R_X86_64_JUMP_SLOT, // R_386_JMP_SLOT, etc). static RelExpr getPltExpr(Symbol &Sym, RelExpr Expr, bool &IsConstant) { Sym.NeedsPltAddr = true; Sym.IsPreemptible = false; IsConstant = true; return toPlt(Expr); } +// This modifies the expression if we can use a copy relocation or point the +// symbol to the PLT. template static RelExpr adjustExpr(Symbol &Sym, RelExpr Expr, RelType Type, InputSectionBase &S, uint64_t RelOff, bool &IsConstant) { - // We can create any dynamic relocation if a section is simply writable. - if (S.Flags & SHF_WRITE) - return Expr; - - // Or, if we are allowed to create dynamic relocations against - // read-only sections (i.e. when "-z notext" is given), - // we can create a dynamic relocation as we want, too. - if (!Config->ZText) { - // We use PLT for relocations that may overflow in runtime, - // see comment for getPltExpr(). - if (Sym.isFunc() && !Target->isPicRel(Type)) - return getPltExpr(Sym, Expr, IsConstant); - return Expr; - } - // If a relocation can be applied at link-time, we don't need to // create a dynamic relocation in the first place. if (IsConstant) return Expr; - // If we got here we know that this relocation would require the dynamic - // linker to write a value to read only memory. - - // If the relocation is to a weak undef, give up on it and produce a - // non preemptible 0. - if (Sym.isUndefWeak()) { + // If the relocation is to a weak undef, and we are producing + // executable, give up on it and produce a non preemptible 0. + if (!Config->Shared && Sym.isUndefWeak()) { Sym.IsPreemptible = false; IsConstant = true; return Expr; } + // We can create any dynamic relocation supported by the dynamic linker if a + // section is writable or we are passed -z notext. + bool CanWrite = (S.Flags & SHF_WRITE) || !Config->ZText; + if (CanWrite && Target->isPicRel(Type)) + return Expr; + + // If we got here we know that this relocation would require the dynamic + // linker to write a value to read only memory or use an unsupported + // relocation. + // We can hack around it if we are producing an executable and // the refered symbol can be preemepted to refer to the executable. - if (Config->Shared || (Config->Pic && !isRelExpr(Expr))) { + if (!CanWrite && (Config->Shared || (Config->Pic && !isRelExpr(Expr)))) { error( "can't create dynamic relocation " + toString(Type) + " against " + (Sym.getName().empty() ? "local symbol" : "symbol: " + toString(Sym)) + " in readonly segment; recompile object files with -fPIC" + getLocation(S, Sym, RelOff)); return Expr; } + + // Copy relocations are only possible if we are creating an executable and the + // symbol is shared. + if (!Sym.isShared() || Config->Shared) + return Expr; if (Sym.getVisibility() != STV_DEFAULT) { error("cannot preempt symbol: " + toString(Sym) + getLocation(S, Sym, RelOff)); return Expr; } if (Sym.isObject()) { // Produce a copy relocation. auto *B = dyn_cast(&Sym); if (B && !B->CopyRelSec) { if (Config->ZNocopyreloc) error("unresolvable relocation " + toString(Type) + " against symbol '" + toString(*B) + "'; recompile with -fPIC or remove '-z nocopyreloc'" + getLocation(S, Sym, RelOff)); addCopyRelSymbol(B); } IsConstant = true; return Expr; } if (Sym.isFunc()) return getPltExpr(Sym, Expr, IsConstant); errorOrWarn("symbol '" + toString(Sym) + "' defined in " + toString(Sym.File) + " has no type"); return Expr; } // MIPS has an odd notion of "paired" relocations to calculate addends. // For example, if a relocation is of R_MIPS_HI16, there must be a // R_MIPS_LO16 relocation after that, and an addend is calculated using // the two relocations. template static int64_t computeMipsAddend(const RelTy &Rel, const RelTy *End, InputSectionBase &Sec, RelExpr Expr, bool IsLocal) { if (Expr == R_MIPS_GOTREL && IsLocal) return Sec.getFile()->MipsGp0; // The ABI says that the paired relocation is used only for REL. // See p. 4-17 at ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf if (RelTy::IsRela) return 0; RelType Type = Rel.getType(Config->IsMips64EL); uint32_t PairTy = getMipsPairType(Type, IsLocal); if (PairTy == R_MIPS_NONE) return 0; const uint8_t *Buf = Sec.Data.data(); uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL); // To make things worse, paired relocations might not be contiguous in // the relocation table, so we need to do linear search. *sigh* for (const RelTy *RI = &Rel; RI != End; ++RI) if (RI->getType(Config->IsMips64EL) == PairTy && RI->getSymbol(Config->IsMips64EL) == SymIndex) return Target->getImplicitAddend(Buf + RI->r_offset, PairTy); warn("can't find matching " + toString(PairTy) + " relocation for " + toString(Type)); return 0; } // Returns an addend of a given relocation. If it is RELA, an addend // is in a relocation itself. If it is REL, we need to read it from an // input section. template static int64_t computeAddend(const RelTy &Rel, const RelTy *End, InputSectionBase &Sec, RelExpr Expr, bool IsLocal) { int64_t Addend; RelType Type = Rel.getType(Config->IsMips64EL); if (RelTy::IsRela) { Addend = getAddend(Rel); } else { const uint8_t *Buf = Sec.Data.data(); Addend = Target->getImplicitAddend(Buf + Rel.r_offset, Type); } if (Config->EMachine == EM_PPC64 && Config->Pic && Type == R_PPC64_TOC) Addend += getPPC64TocBase(); if (Config->EMachine == EM_MIPS) Addend += computeMipsAddend(Rel, End, Sec, Expr, IsLocal); return Addend; } // Report an undefined symbol if necessary. // Returns true if this function printed out an error message. static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec, uint64_t Offset) { if (Config->UnresolvedSymbols == UnresolvedPolicy::IgnoreAll) return false; if (Sym.isLocal() || !Sym.isUndefined() || Sym.isWeak()) return false; bool CanBeExternal = Sym.computeBinding() != STB_LOCAL && Sym.getVisibility() == STV_DEFAULT; if (Config->UnresolvedSymbols == UnresolvedPolicy::Ignore && CanBeExternal) return false; std::string Msg = "undefined symbol: " + toString(Sym) + "\n>>> referenced by "; std::string Src = Sec.getSrcMsg(Sym, Offset); if (!Src.empty()) Msg += Src + "\n>>> "; Msg += Sec.getObjMsg(Offset); if ((Config->UnresolvedSymbols == UnresolvedPolicy::Warn && CanBeExternal) || Config->NoinhibitExec) { warn(Msg); return false; } error(Msg); return true; } // MIPS N32 ABI treats series of successive relocations with the same offset // as a single relocation. The similar approach used by N64 ABI, but this ABI // packs all relocations into the single relocation record. Here we emulate // this for the N32 ABI. Iterate over relocation with the same offset and put // theirs types into the single bit-set. template static RelType getMipsN32RelType(RelTy *&Rel, RelTy *End) { RelType Type = Rel->getType(Config->IsMips64EL); uint64_t Offset = Rel->r_offset; int N = 0; while (Rel + 1 != End && (Rel + 1)->r_offset == Offset) Type |= (++Rel)->getType(Config->IsMips64EL) << (8 * ++N); return Type; } // .eh_frame sections are mergeable input sections, so their input // offsets are not linearly mapped to output section. For each input // offset, we need to find a section piece containing the offset and // add the piece's base address to the input offset to compute the // output offset. That isn't cheap. // // This class is to speed up the offset computation. When we process // relocations, we access offsets in the monotonically increasing // order. So we can optimize for that access pattern. // // For sections other than .eh_frame, this class doesn't do anything. namespace { class OffsetGetter { public: explicit OffsetGetter(InputSectionBase &Sec) { if (auto *Eh = dyn_cast(&Sec)) Pieces = Eh->Pieces; } // Translates offsets in input sections to offsets in output sections. // Given offset must increase monotonically. We assume that Piece is // sorted by InputOff. uint64_t get(uint64_t Off) { if (Pieces.empty()) return Off; while (I != Pieces.size() && Pieces[I].InputOff + Pieces[I].Size <= Off) ++I; if (I == Pieces.size()) return Off; // Pieces must be contiguous, so there must be no holes in between. assert(Pieces[I].InputOff <= Off && "Relocation not in any piece"); // Offset -1 means that the piece is dead (i.e. garbage collected). if (Pieces[I].OutputOff == -1) return -1; return Pieces[I].OutputOff + Off - Pieces[I].InputOff; } private: ArrayRef Pieces; size_t I = 0; }; } // namespace template static void addPltEntry(PltSection *Plt, GotPltSection *GotPlt, RelocationBaseSection *Rel, RelType Type, Symbol &Sym, bool UseSymVA) { Plt->addEntry(Sym); GotPlt->addEntry(Sym); Rel->addReloc({Type, GotPlt, Sym.getGotPltOffset(), UseSymVA, &Sym, 0}); } template static void addGotEntry(Symbol &Sym, bool Preemptible) { InX::Got->addEntry(Sym); RelExpr Expr = Sym.isTls() ? R_TLS : R_ABS; uint64_t Off = Sym.getGotOffset(); // If a GOT slot value can be calculated at link-time, which is now, // we can just fill that out. // // (We don't actually write a value to a GOT slot right now, but we // add a static relocation to a Relocations vector so that // InputSection::relocate will do the work for us. We may be able // to just write a value now, but it is a TODO.) bool IsLinkTimeConstant = !Preemptible && (!Config->Pic || isAbsolute(Sym)); if (IsLinkTimeConstant) { InX::Got->Relocations.push_back({Expr, Target->GotRel, Off, 0, &Sym}); return; } // Otherwise, we emit a dynamic relocation to .rel[a].dyn so that // the GOT slot will be fixed at load-time. RelType Type; if (Sym.isTls()) Type = Target->TlsGotRel; else if (!Preemptible && Config->Pic && !isAbsolute(Sym)) Type = Target->RelativeRel; else Type = Target->GotRel; InX::RelaDyn->addReloc({Type, InX::Got, Off, !Preemptible, &Sym, 0}); // REL type relocations don't have addend fields unlike RELAs, and // their addends are stored to the section to which they are applied. // So, store addends if we need to. // // This is ugly -- the difference between REL and RELA should be // handled in a better way. It's a TODO. if (!Config->IsRela && !Preemptible) InX::Got->Relocations.push_back({R_ABS, Target->GotRel, Off, 0, &Sym}); } // The reason we have to do this early scan is as follows // * To mmap the output file, we need to know the size // * For that, we need to know how many dynamic relocs we will have. // It might be possible to avoid this by outputting the file with write: // * Write the allocated output sections, computing addresses. // * Apply relocations, recording which ones require a dynamic reloc. // * Write the dynamic relocations. // * Write the rest of the file. // This would have some drawbacks. For example, we would only know if .rela.dyn // is needed after applying relocations. If it is, it will go after rw and rx // sections. Given that it is ro, we will need an extra PT_LOAD. This // complicates things for the dynamic linker and means we would have to reserve // space for the extra PT_LOAD even if we end up not using it. template static void scanRelocs(InputSectionBase &Sec, ArrayRef Rels) { OffsetGetter GetOffset(Sec); // Not all relocations end up in Sec.Relocations, but a lot do. Sec.Relocations.reserve(Rels.size()); for (auto I = Rels.begin(), End = Rels.end(); I != End; ++I) { const RelTy &Rel = *I; Symbol &Sym = Sec.getFile()->getRelocTargetSym(Rel); RelType Type = Rel.getType(Config->IsMips64EL); // Deal with MIPS oddity. if (Config->MipsN32Abi) Type = getMipsN32RelType(I, End); // Get an offset in an output section this relocation is applied to. uint64_t Offset = GetOffset.get(Rel.r_offset); if (Offset == uint64_t(-1)) continue; // Skip if the target symbol is an erroneous undefined symbol. if (maybeReportUndefined(Sym, Sec, Rel.r_offset)) continue; RelExpr Expr = Target->getRelExpr(Type, Sym, Sec.Data.begin() + Rel.r_offset); // Ignore "hint" relocations because they are only markers for relaxation. if (isRelExprOneOf(Expr)) continue; // Handle yet another MIPS-ness. if (isMipsGprel(Type)) { int64_t Addend = computeAddend(Rel, End, Sec, Expr, Sym.isLocal()); Sec.Relocations.push_back({R_MIPS_GOTREL, Type, Offset, Addend, &Sym}); continue; } bool Preemptible = Sym.IsPreemptible; // Strenghten or relax a PLT access. // // GNU ifunc symbols must be accessed via PLT because their addresses // are determined by runtime. // // On the other hand, if we know that a PLT entry will be resolved within // the same ELF module, we can skip PLT access and directly jump to the // destination function. For example, if we are linking a main exectuable, // all dynamic symbols that can be resolved within the executable will // actually be resolved that way at runtime, because the main exectuable // is always at the beginning of a search list. We can leverage that fact. if (Sym.isGnuIFunc()) Expr = toPlt(Expr); else if (!Preemptible && Expr == R_GOT_PC && !isAbsoluteValue(Sym)) Expr = Target->adjustRelaxExpr(Type, Sec.Data.data() + Rel.r_offset, Expr); else if (!Preemptible) Expr = fromPlt(Expr); bool IsConstant = isStaticLinkTimeConstant(Expr, Type, Sym, Sec, Rel.r_offset); Expr = adjustExpr(Sym, Expr, Type, Sec, Rel.r_offset, IsConstant); if (errorCount()) continue; // This relocation does not require got entry, but it is relative to got and // needs it to be created. Here we request for that. if (isRelExprOneOf(Expr)) InX::Got->HasGotOffRel = true; // Read an addend. int64_t Addend = computeAddend(Rel, End, Sec, Expr, Sym.isLocal()); // Process some TLS relocations, including relaxing TLS relocations. // Note that this function does not handle all TLS relocations. if (unsigned Processed = handleTlsRelocation(Type, Sym, Sec, Offset, Addend, Expr)) { I += (Processed - 1); continue; } // If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol. if (needsPlt(Expr) && !Sym.isInPlt()) { if (Sym.isGnuIFunc() && !Preemptible) addPltEntry(InX::Iplt, InX::IgotPlt, InX::RelaIplt, Target->IRelativeRel, Sym, true); else addPltEntry(InX::Plt, InX::GotPlt, InX::RelaPlt, Target->PltRel, Sym, !Preemptible); } // Create a GOT slot if a relocation needs GOT. if (needsGot(Expr)) { if (Config->EMachine == EM_MIPS) { // MIPS ABI has special rules to process GOT entries and doesn't // require relocation entries for them. A special case is TLS // relocations. In that case dynamic loader applies dynamic // relocations to initialize TLS GOT entries. // See "Global Offset Table" in Chapter 5 in the following document // for detailed description: // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf InX::MipsGot->addEntry(Sym, Addend, Expr); if (Sym.isTls() && Sym.IsPreemptible) InX::RelaDyn->addReloc({Target->TlsGotRel, InX::MipsGot, Sym.getGotOffset(), false, &Sym, 0}); } else if (!Sym.isInGot()) { addGotEntry(Sym, Preemptible); } } if (!needsPlt(Expr) && !needsGot(Expr) && Sym.IsPreemptible) { // We don't know anything about the finaly symbol. Just ask the dynamic // linker to handle the relocation for us. if (!Target->isPicRel(Type)) errorOrWarn( "relocation " + toString(Type) + " cannot be used against shared object; recompile with -fPIC" + getLocation(Sec, Sym, Offset)); InX::RelaDyn->addReloc( {Target->getDynRel(Type), &Sec, Offset, false, &Sym, Addend}); // MIPS ABI turns using of GOT and dynamic relocations inside out. // While regular ABI uses dynamic relocations to fill up GOT entries // MIPS ABI requires dynamic linker to fills up GOT entries using // specially sorted dynamic symbol table. This affects even dynamic // relocations against symbols which do not require GOT entries // creation explicitly, i.e. do not have any GOT-relocations. So if // a preemptible symbol has a dynamic relocation we anyway have // to create a GOT entry for it. // If a non-preemptible symbol has a dynamic relocation against it, // dynamic linker takes it st_value, adds offset and writes down // result of the dynamic relocation. In case of preemptible symbol // dynamic linker performs symbol resolution, writes the symbol value // to the GOT entry and reads the GOT entry when it needs to perform // a dynamic relocation. // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf p.4-19 if (Config->EMachine == EM_MIPS) InX::MipsGot->addEntry(Sym, Addend, Expr); continue; } // The size is not going to change, so we fold it in here. if (Expr == R_SIZE) Addend += Sym.getSize(); // If the produced value is a constant, we just remember to write it // when outputting this section. We also have to do it if the format // uses Elf_Rel, since in that case the written value is the addend. if (IsConstant) { Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); continue; } // If the output being produced is position independent, the final value // is still not known. In that case we still need some help from the // dynamic linker. We can however do better than just copying the incoming // relocation. We can process some of it and and just ask the dynamic // linker to add the load address. if (Config->IsRela) { InX::RelaDyn->addReloc( {Target->RelativeRel, &Sec, Offset, true, &Sym, Addend}); } else { // In REL, addends are stored to the target section. InX::RelaDyn->addReloc( {Target->RelativeRel, &Sec, Offset, true, &Sym, 0}); Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); } } } template void elf::scanRelocations(InputSectionBase &S) { if (S.AreRelocsRela) scanRelocs(S, S.relas()); else scanRelocs(S, S.rels()); } // Thunk Implementation // // Thunks (sometimes called stubs, veneers or branch islands) are small pieces // of code that the linker inserts inbetween a caller and a callee. The thunks // are added at link time rather than compile time as the decision on whether // a thunk is needed, such as the caller and callee being out of range, can only // be made at link time. // // It is straightforward to tell given the current state of the program when a // thunk is needed for a particular call. The more difficult part is that // the thunk needs to be placed in the program such that the caller can reach // the thunk and the thunk can reach the callee; furthermore, adding thunks to // the program alters addresses, which can mean more thunks etc. // // In lld we have a synthetic ThunkSection that can hold many Thunks. // The decision to have a ThunkSection act as a container means that we can // more easily handle the most common case of a single block of contiguous // Thunks by inserting just a single ThunkSection. // // The implementation of Thunks in lld is split across these areas // Relocations.cpp : Framework for creating and placing thunks // Thunks.cpp : The code generated for each supported thunk // Target.cpp : Target specific hooks that the framework uses to decide when // a thunk is used // Synthetic.cpp : Implementation of ThunkSection // Writer.cpp : Iteratively call framework until no more Thunks added // // Thunk placement requirements: // Mips LA25 thunks. These must be placed immediately before the callee section // We can assume that the caller is in range of the Thunk. These are modelled // by Thunks that return the section they must precede with // getTargetInputSection(). // // ARM interworking and range extension thunks. These thunks must be placed // within range of the caller. All implemented ARM thunks can always reach the // callee as they use an indirect jump via a register that has no range // restrictions. // // Thunk placement algorithm: // For Mips LA25 ThunkSections; the placement is explicit, it has to be before // getTargetInputSection(). // // For thunks that must be placed within range of the caller there are many // possible choices given that the maximum range from the caller is usually // much larger than the average InputSection size. Desirable properties include: // - Maximize reuse of thunks by multiple callers // - Minimize number of ThunkSections to simplify insertion // - Handle impact of already added Thunks on addresses // - Simple to understand and implement // // In lld for the first pass, we pre-create one or more ThunkSections per // InputSectionDescription at Target specific intervals. A ThunkSection is // placed so that the estimated end of the ThunkSection is within range of the // start of the InputSectionDescription or the previous ThunkSection. For // example: // InputSectionDescription // Section 0 // ... // Section N // ThunkSection 0 // Section N + 1 // ... // Section N + K // Thunk Section 1 // // The intention is that we can add a Thunk to a ThunkSection that is well // spaced enough to service a number of callers without having to do a lot // of work. An important principle is that it is not an error if a Thunk cannot // be placed in a pre-created ThunkSection; when this happens we create a new // ThunkSection placed next to the caller. This allows us to handle the vast // majority of thunks simply, but also handle rare cases where the branch range // is smaller than the target specific spacing. // // The algorithm is expected to create all the thunks that are needed in a // single pass, with a small number of programs needing a second pass due to // the insertion of thunks in the first pass increasing the offset between // callers and callees that were only just in range. // // A consequence of allowing new ThunkSections to be created outside of the // pre-created ThunkSections is that in rare cases calls to Thunks that were in // range in pass K, are out of range in some pass > K due to the insertion of // more Thunks in between the caller and callee. When this happens we retarget // the relocation back to the original target and create another Thunk. // Remove ThunkSections that are empty, this should only be the initial set // precreated on pass 0. // Insert the Thunks for OutputSection OS into their designated place // in the Sections vector, and recalculate the InputSection output section // offsets. // This may invalidate any output section offsets stored outside of InputSection void ThunkCreator::mergeThunks(ArrayRef OutputSections) { forEachInputSectionDescription( OutputSections, [&](OutputSection *OS, InputSectionDescription *ISD) { if (ISD->ThunkSections.empty()) return; // Remove any zero sized precreated Thunks. llvm::erase_if(ISD->ThunkSections, [](const std::pair &TS) { return TS.first->getSize() == 0; }); // ISD->ThunkSections contains all created ThunkSections, including // those inserted in previous passes. Extract the Thunks created this // pass and order them in ascending OutSecOff. std::vector NewThunks; for (const std::pair TS : ISD->ThunkSections) if (TS.second == Pass) NewThunks.push_back(TS.first); std::stable_sort(NewThunks.begin(), NewThunks.end(), [](const ThunkSection *A, const ThunkSection *B) { return A->OutSecOff < B->OutSecOff; }); // Merge sorted vectors of Thunks and InputSections by OutSecOff std::vector Tmp; Tmp.reserve(ISD->Sections.size() + NewThunks.size()); auto MergeCmp = [](const InputSection *A, const InputSection *B) { // std::merge requires a strict weak ordering. if (A->OutSecOff < B->OutSecOff) return true; if (A->OutSecOff == B->OutSecOff) { auto *TA = dyn_cast(A); auto *TB = dyn_cast(B); // Check if Thunk is immediately before any specific Target // InputSection for example Mips LA25 Thunks. if (TA && TA->getTargetInputSection() == B) return true; if (TA && !TB && !TA->getTargetInputSection()) // Place Thunk Sections without specific targets before // non-Thunk Sections. return true; } return false; }; std::merge(ISD->Sections.begin(), ISD->Sections.end(), NewThunks.begin(), NewThunks.end(), std::back_inserter(Tmp), MergeCmp); ISD->Sections = std::move(Tmp); }); } // Find or create a ThunkSection within the InputSectionDescription (ISD) that // is in range of Src. An ISD maps to a range of InputSections described by a // linker script section pattern such as { .text .text.* }. ThunkSection *ThunkCreator::getISDThunkSec(OutputSection *OS, InputSection *IS, InputSectionDescription *ISD, uint32_t Type, uint64_t Src) { for (std::pair TP : ISD->ThunkSections) { ThunkSection *TS = TP.first; uint64_t TSBase = OS->Addr + TS->OutSecOff; uint64_t TSLimit = TSBase + TS->getSize(); if (Target->inBranchRange(Type, Src, (Src > TSLimit) ? TSBase : TSLimit)) return TS; } // No suitable ThunkSection exists. This can happen when there is a branch // with lower range than the ThunkSection spacing or when there are too // many Thunks. Create a new ThunkSection as close to the InputSection as // possible. Error if InputSection is so large we cannot place ThunkSection // anywhere in Range. uint64_t ThunkSecOff = IS->OutSecOff; if (!Target->inBranchRange(Type, Src, OS->Addr + ThunkSecOff)) { ThunkSecOff = IS->OutSecOff + IS->getSize(); if (!Target->inBranchRange(Type, Src, OS->Addr + ThunkSecOff)) fatal("InputSection too large for range extension thunk " + IS->getObjMsg(Src - (OS->Addr + IS->OutSecOff))); } return addThunkSection(OS, ISD, ThunkSecOff); } // Add a Thunk that needs to be placed in a ThunkSection that immediately // precedes its Target. ThunkSection *ThunkCreator::getISThunkSec(InputSection *IS) { ThunkSection *TS = ThunkedSections.lookup(IS); if (TS) return TS; // Find InputSectionRange within Target Output Section (TOS) that the // InputSection (IS) that we need to precede is in. OutputSection *TOS = IS->getParent(); for (BaseCommand *BC : TOS->SectionCommands) if (auto *ISD = dyn_cast(BC)) { if (ISD->Sections.empty()) continue; InputSection *first = ISD->Sections.front(); InputSection *last = ISD->Sections.back(); if (IS->OutSecOff >= first->OutSecOff && IS->OutSecOff <= last->OutSecOff) { TS = addThunkSection(TOS, ISD, IS->OutSecOff); ThunkedSections[IS] = TS; break; } } return TS; } // Create one or more ThunkSections per OS that can be used to place Thunks. // We attempt to place the ThunkSections using the following desirable // properties: // - Within range of the maximum number of callers // - Minimise the number of ThunkSections // // We follow a simple but conservative heuristic to place ThunkSections at // offsets that are multiples of a Target specific branch range. // For an InputSectionRange that is smaller than the range, a single // ThunkSection at the end of the range will do. void ThunkCreator::createInitialThunkSections( ArrayRef OutputSections) { forEachInputSectionDescription( OutputSections, [&](OutputSection *OS, InputSectionDescription *ISD) { if (ISD->Sections.empty()) return; uint32_t ISLimit; uint32_t PrevISLimit = ISD->Sections.front()->OutSecOff; uint32_t ThunkUpperBound = PrevISLimit + Target->ThunkSectionSpacing; for (const InputSection *IS : ISD->Sections) { ISLimit = IS->OutSecOff + IS->getSize(); if (ISLimit > ThunkUpperBound) { addThunkSection(OS, ISD, PrevISLimit); ThunkUpperBound = PrevISLimit + Target->ThunkSectionSpacing; } PrevISLimit = ISLimit; } addThunkSection(OS, ISD, ISLimit); }); } ThunkSection *ThunkCreator::addThunkSection(OutputSection *OS, InputSectionDescription *ISD, uint64_t Off) { auto *TS = make(OS, Off); ISD->ThunkSections.push_back(std::make_pair(TS, Pass)); return TS; } std::pair ThunkCreator::getThunk(Symbol &Sym, RelType Type, uint64_t Src) { auto Res = ThunkedSymbols.insert({&Sym, std::vector()}); if (!Res.second) { // Check existing Thunks for Sym to see if they can be reused for (Thunk *ET : Res.first->second) if (ET->isCompatibleWith(Type) && Target->inBranchRange(Type, Src, ET->ThunkSym->getVA())) return std::make_pair(ET, false); } // No existing compatible Thunk in range, create a new one Thunk *T = addThunk(Type, Sym); Res.first->second.push_back(T); return std::make_pair(T, true); } // Call Fn on every executable InputSection accessed via the linker script // InputSectionDescription::Sections. void ThunkCreator::forEachInputSectionDescription( ArrayRef OutputSections, std::function Fn) { for (OutputSection *OS : OutputSections) { if (!(OS->Flags & SHF_ALLOC) || !(OS->Flags & SHF_EXECINSTR)) continue; for (BaseCommand *BC : OS->SectionCommands) if (auto *ISD = dyn_cast(BC)) Fn(OS, ISD); } } // Return true if the relocation target is an in range Thunk. // Return false if the relocation is not to a Thunk. If the relocation target // was originally to a Thunk, but is no longer in range we revert the // relocation back to its original non-Thunk target. bool ThunkCreator::normalizeExistingThunk(Relocation &Rel, uint64_t Src) { if (Thunk *ET = Thunks.lookup(Rel.Sym)) { if (Target->inBranchRange(Rel.Type, Src, Rel.Sym->getVA())) return true; Rel.Sym = &ET->Destination; if (Rel.Sym->isInPlt()) Rel.Expr = toPlt(Rel.Expr); } return false; } // Process all relocations from the InputSections that have been assigned // to InputSectionDescriptions and redirect through Thunks if needed. The // function should be called iteratively until it returns false. // // PreConditions: // All InputSections that may need a Thunk are reachable from // OutputSectionCommands. // // All OutputSections have an address and all InputSections have an offset // within the OutputSection. // // The offsets between caller (relocation place) and callee // (relocation target) will not be modified outside of createThunks(). // // PostConditions: // If return value is true then ThunkSections have been inserted into // OutputSections. All relocations that needed a Thunk based on the information // available to createThunks() on entry have been redirected to a Thunk. Note // that adding Thunks changes offsets between caller and callee so more Thunks // may be required. // // If return value is false then no more Thunks are needed, and createThunks has // made no changes. If the target requires range extension thunks, currently // ARM, then any future change in offset between caller and callee risks a // relocation out of range error. bool ThunkCreator::createThunks(ArrayRef OutputSections) { bool AddressesChanged = false; if (Pass == 0 && Target->ThunkSectionSpacing) createInitialThunkSections(OutputSections); else if (Pass == 10) // With Thunk Size much smaller than branch range we expect to // converge quickly; if we get to 10 something has gone wrong. fatal("thunk creation not converged"); // Create all the Thunks and insert them into synthetic ThunkSections. The // ThunkSections are later inserted back into InputSectionDescriptions. // We separate the creation of ThunkSections from the insertion of the // ThunkSections as ThunkSections are not always inserted into the same // InputSectionDescription as the caller. forEachInputSectionDescription( OutputSections, [&](OutputSection *OS, InputSectionDescription *ISD) { for (InputSection *IS : ISD->Sections) for (Relocation &Rel : IS->Relocations) { uint64_t Src = OS->Addr + IS->OutSecOff + Rel.Offset; // If we are a relocation to an existing Thunk, check if it is // still in range. If not then Rel will be altered to point to its // original target so another Thunk can be generated. if (Pass > 0 && normalizeExistingThunk(Rel, Src)) continue; if (!Target->needsThunk(Rel.Expr, Rel.Type, IS->File, Src, *Rel.Sym)) continue; Thunk *T; bool IsNew; std::tie(T, IsNew) = getThunk(*Rel.Sym, Rel.Type, Src); if (IsNew) { AddressesChanged = true; // Find or create a ThunkSection for the new Thunk ThunkSection *TS; if (auto *TIS = T->getTargetInputSection()) TS = getISThunkSec(TIS); else TS = getISDThunkSec(OS, IS, ISD, Rel.Type, Src); TS->addThunk(T); Thunks[T->ThunkSym] = T; } // Redirect relocation to Thunk, we never go via the PLT to a Thunk Rel.Sym = T->ThunkSym; Rel.Expr = fromPlt(Rel.Expr); } }); // Merge all created synthetic ThunkSections back into OutputSection mergeThunks(OutputSections); ++Pass; return AddressesChanged; } template void elf::scanRelocations(InputSectionBase &); template void elf::scanRelocations(InputSectionBase &); template void elf::scanRelocations(InputSectionBase &); template void elf::scanRelocations(InputSectionBase &); Index: vendor/lld/dist/ELF/ScriptLexer.cpp =================================================================== --- vendor/lld/dist/ELF/ScriptLexer.cpp (revision 327307) +++ vendor/lld/dist/ELF/ScriptLexer.cpp (revision 327308) @@ -1,285 +1,293 @@ //===- ScriptLexer.cpp ----------------------------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines a lexer for the linker script. // // The linker script's grammar is not complex but ambiguous due to the // lack of the formal specification of the language. What we are trying to // do in this and other files in LLD is to make a "reasonable" linker // script processor. // // Among simplicity, compatibility and efficiency, we put the most // emphasis on simplicity when we wrote this lexer. Compatibility with the // GNU linkers is important, but we did not try to clone every tiny corner // case of their lexers, as even ld.bfd and ld.gold are subtly different // in various corner cases. We do not care much about efficiency because // the time spent in parsing linker scripts is usually negligible. // // Our grammar of the linker script is LL(2), meaning that it needs at // most two-token lookahead to parse. The only place we need two-token // lookahead is labels in version scripts, where we need to parse "local :" // as if "local:". // // Overall, this lexer works fine for most linker scripts. There might // be room for improving compatibility, but that's probably not at the // top of our todo list. // //===----------------------------------------------------------------------===// #include "ScriptLexer.h" #include "lld/Common/ErrorHandler.h" #include "llvm/ADT/Twine.h" using namespace llvm; using namespace lld; using namespace lld::elf; // Returns a whole line containing the current token. StringRef ScriptLexer::getLine() { StringRef S = getCurrentMB().getBuffer(); StringRef Tok = Tokens[Pos - 1]; size_t Pos = S.rfind('\n', Tok.data() - S.data()); if (Pos != StringRef::npos) S = S.substr(Pos + 1); return S.substr(0, S.find_first_of("\r\n")); } // Returns 1-based line number of the current token. size_t ScriptLexer::getLineNumber() { StringRef S = getCurrentMB().getBuffer(); StringRef Tok = Tokens[Pos - 1]; return S.substr(0, Tok.data() - S.data()).count('\n') + 1; } // Returns 0-based column number of the current token. size_t ScriptLexer::getColumnNumber() { StringRef Tok = Tokens[Pos - 1]; return Tok.data() - getLine().data(); } std::string ScriptLexer::getCurrentLocation() { std::string Filename = getCurrentMB().getBufferIdentifier(); if (!Pos) return Filename; return (Filename + ":" + Twine(getLineNumber())).str(); } ScriptLexer::ScriptLexer(MemoryBufferRef MB) { tokenize(MB); } // We don't want to record cascading errors. Keep only the first one. void ScriptLexer::setError(const Twine &Msg) { if (errorCount()) return; std::string S = (getCurrentLocation() + ": " + Msg).str(); if (Pos) S += "\n>>> " + getLine().str() + "\n>>> " + std::string(getColumnNumber(), ' ') + "^"; error(S); } // Split S into linker script tokens. void ScriptLexer::tokenize(MemoryBufferRef MB) { std::vector Vec; MBs.push_back(MB); StringRef S = MB.getBuffer(); StringRef Begin = S; for (;;) { S = skipSpace(S); if (S.empty()) break; // Quoted token. Note that double-quote characters are parts of a token // because, in a glob match context, only unquoted tokens are interpreted // as glob patterns. Double-quoted tokens are literal patterns in that // context. if (S.startswith("\"")) { size_t E = S.find("\"", 1); if (E == StringRef::npos) { StringRef Filename = MB.getBufferIdentifier(); size_t Lineno = Begin.substr(0, S.data() - Begin.data()).count('\n'); error(Filename + ":" + Twine(Lineno + 1) + ": unclosed quote"); return; } Vec.push_back(S.take_front(E + 1)); S = S.substr(E + 1); continue; } + // ">foo" is parsed to ">" and "foo", but ">>" is parsed to ">>". + if (S.startswith("<<") || S.startswith("<=") || S.startswith(">>") || + S.startswith(">=")) { + Vec.push_back(S.substr(0, 2)); + S = S.substr(2); + continue; + } + // Unquoted token. This is more relaxed than tokens in C-like language, // so that you can write "file-name.cpp" as one bare token, for example. size_t Pos = S.find_first_not_of( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - "0123456789_.$/\\~=+[]*?-!<>^:"); + "0123456789_.$/\\~=+[]*?-!^:"); // A character that cannot start a word (which is usually a // punctuation) forms a single character token. if (Pos == 0) Pos = 1; Vec.push_back(S.substr(0, Pos)); S = S.substr(Pos); } Tokens.insert(Tokens.begin() + Pos, Vec.begin(), Vec.end()); } // Skip leading whitespace characters or comments. StringRef ScriptLexer::skipSpace(StringRef S) { for (;;) { if (S.startswith("/*")) { size_t E = S.find("*/", 2); if (E == StringRef::npos) { error("unclosed comment in a linker script"); return ""; } S = S.substr(E + 2); continue; } if (S.startswith("#")) { size_t E = S.find('\n', 1); if (E == StringRef::npos) E = S.size() - 1; S = S.substr(E + 1); continue; } size_t Size = S.size(); S = S.ltrim(); if (S.size() == Size) return S; } } // An erroneous token is handled as if it were the last token before EOF. bool ScriptLexer::atEOF() { return errorCount() || Tokens.size() == Pos; } // Split a given string as an expression. // This function returns "3", "*" and "5" for "3*5" for example. static std::vector tokenizeExpr(StringRef S) { StringRef Ops = "+-*/:!~"; // List of operators // Quoted strings are literal strings, so we don't want to split it. if (S.startswith("\"")) return {S}; // Split S with operators as separators. std::vector Ret; while (!S.empty()) { size_t E = S.find_first_of(Ops); // No need to split if there is no operator. if (E == StringRef::npos) { Ret.push_back(S); break; } // Get a token before the opreator. if (E != 0) Ret.push_back(S.substr(0, E)); // Get the operator as a token. Keep != as one token. if (S.substr(E).startswith("!=")) { Ret.push_back(S.substr(E, 2)); S = S.substr(E + 2); } else { Ret.push_back(S.substr(E, 1)); S = S.substr(E + 1); } } return Ret; } // In contexts where expressions are expected, the lexer should apply // different tokenization rules than the default one. By default, // arithmetic operator characters are regular characters, but in the // expression context, they should be independent tokens. // // For example, "foo*3" should be tokenized to "foo", "*" and "3" only // in the expression context. // // This function may split the current token into multiple tokens. void ScriptLexer::maybeSplitExpr() { if (!InExpr || errorCount() || atEOF()) return; std::vector V = tokenizeExpr(Tokens[Pos]); if (V.size() == 1) return; Tokens.erase(Tokens.begin() + Pos); Tokens.insert(Tokens.begin() + Pos, V.begin(), V.end()); } StringRef ScriptLexer::next() { maybeSplitExpr(); if (errorCount()) return ""; if (atEOF()) { setError("unexpected EOF"); return ""; } return Tokens[Pos++]; } StringRef ScriptLexer::peek() { StringRef Tok = next(); if (errorCount()) return ""; Pos = Pos - 1; return Tok; } bool ScriptLexer::consume(StringRef Tok) { if (peek() == Tok) { skip(); return true; } return false; } // Consumes Tok followed by ":". Space is allowed between Tok and ":". bool ScriptLexer::consumeLabel(StringRef Tok) { if (consume((Tok + ":").str())) return true; if (Tokens.size() >= Pos + 2 && Tokens[Pos] == Tok && Tokens[Pos + 1] == ":") { Pos += 2; return true; } return false; } void ScriptLexer::skip() { (void)next(); } void ScriptLexer::expect(StringRef Expect) { if (errorCount()) return; StringRef Tok = next(); if (Tok != Expect) setError(Expect + " expected, but got " + Tok); } // Returns true if S encloses T. static bool encloses(StringRef S, StringRef T) { return S.bytes_begin() <= T.bytes_begin() && T.bytes_end() <= S.bytes_end(); } MemoryBufferRef ScriptLexer::getCurrentMB() { // Find input buffer containing the current token. assert(!MBs.empty()); if (!Pos) return MBs[0]; for (MemoryBufferRef MB : MBs) if (encloses(MB.getBuffer(), Tokens[Pos - 1])) return MB; llvm_unreachable("getCurrentMB: failed to find a token"); } Index: vendor/lld/dist/ELF/ScriptParser.cpp =================================================================== --- vendor/lld/dist/ELF/ScriptParser.cpp (revision 327307) +++ vendor/lld/dist/ELF/ScriptParser.cpp (revision 327308) @@ -1,1335 +1,1334 @@ //===- ScriptParser.cpp ---------------------------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file contains a recursive-descendent parser for linker scripts. // Parsed results are stored to Config and Script global objects. // //===----------------------------------------------------------------------===// #include "ScriptParser.h" #include "Config.h" #include "Driver.h" #include "InputSection.h" #include "LinkerScript.h" #include "OutputSections.h" #include "ScriptLexer.h" #include "Symbols.h" #include "Target.h" #include "lld/Common/Memory.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include #include #include using namespace llvm; using namespace llvm::ELF; using namespace llvm::support::endian; using namespace lld; using namespace lld::elf; static bool isUnderSysroot(StringRef Path); namespace { class ScriptParser final : ScriptLexer { public: ScriptParser(MemoryBufferRef MB) : ScriptLexer(MB), IsUnderSysroot(isUnderSysroot(MB.getBufferIdentifier())) {} void readLinkerScript(); void readVersionScript(); void readDynamicList(); void readDefsym(StringRef Name); private: void addFile(StringRef Path); void readAsNeeded(); void readEntry(); void readExtern(); void readGroup(); void readInclude(); void readMemory(); void readOutput(); void readOutputArch(); void readOutputFormat(); void readPhdrs(); void readRegionAlias(); void readSearchDir(); void readSections(); void readVersion(); void readVersionScriptCommand(); SymbolAssignment *readAssignment(StringRef Name); ByteCommand *readByteCommand(StringRef Tok); uint32_t readFill(); uint32_t parseFill(StringRef Tok); void readSectionAddressType(OutputSection *Cmd); OutputSection *readOutputSectionDescription(StringRef OutSec); std::vector readOutputSectionPhdrs(); InputSectionDescription *readInputSectionDescription(StringRef Tok); StringMatcher readFilePatterns(); std::vector readInputSectionsList(); InputSectionDescription *readInputSectionRules(StringRef FilePattern); unsigned readPhdrType(); SortSectionPolicy readSortKind(); SymbolAssignment *readProvideHidden(bool Provide, bool Hidden); SymbolAssignment *readProvideOrAssignment(StringRef Tok); void readSort(); AssertCommand *readAssert(); Expr readAssertExpr(); Expr readConstant(); Expr getPageSize(); uint64_t readMemoryAssignment(StringRef, StringRef, StringRef); std::pair readMemoryAttributes(); Expr readExpr(); Expr readExpr1(Expr Lhs, int MinPrec); StringRef readParenLiteral(); Expr readPrimary(); Expr readTernary(Expr Cond); Expr readParenExpr(); // For parsing version script. std::vector readVersionExtern(); void readAnonymousDeclaration(); void readVersionDeclaration(StringRef VerStr); std::pair, std::vector> readSymbols(); // True if a script being read is in a subdirectory specified by -sysroot. bool IsUnderSysroot; // A set to detect an INCLUDE() cycle. StringSet<> Seen; }; } // namespace static StringRef unquote(StringRef S) { if (S.startswith("\"")) return S.substr(1, S.size() - 2); return S; } static bool isUnderSysroot(StringRef Path) { if (Config->Sysroot == "") return false; for (; !Path.empty(); Path = sys::path::parent_path(Path)) if (sys::fs::equivalent(Config->Sysroot, Path)) return true; return false; } // Some operations only support one non absolute value. Move the // absolute one to the right hand side for convenience. static void moveAbsRight(ExprValue &A, ExprValue &B) { if (A.Sec == nullptr || (A.ForceAbsolute && !B.isAbsolute())) std::swap(A, B); if (!B.isAbsolute()) error(A.Loc + ": at least one side of the expression must be absolute"); } static ExprValue add(ExprValue A, ExprValue B) { moveAbsRight(A, B); return {A.Sec, A.ForceAbsolute, A.getSectionOffset() + B.getValue(), A.Loc}; } static ExprValue sub(ExprValue A, ExprValue B) { + // The distance between two symbols in sections is absolute. if (!A.isAbsolute() && !B.isAbsolute()) return A.getValue() - B.getValue(); return {A.Sec, false, A.getSectionOffset() - B.getValue(), A.Loc}; } static ExprValue mul(ExprValue A, ExprValue B) { return A.getValue() * B.getValue(); } static ExprValue div(ExprValue A, ExprValue B) { if (uint64_t BV = B.getValue()) return A.getValue() / BV; error("division by zero"); return 0; } static ExprValue bitAnd(ExprValue A, ExprValue B) { moveAbsRight(A, B); return {A.Sec, A.ForceAbsolute, (A.getValue() & B.getValue()) - A.getSecAddr(), A.Loc}; } static ExprValue bitOr(ExprValue A, ExprValue B) { moveAbsRight(A, B); return {A.Sec, A.ForceAbsolute, (A.getValue() | B.getValue()) - A.getSecAddr(), A.Loc}; } void ScriptParser::readDynamicList() { Config->HasDynamicList = true; expect("{"); std::vector Locals; std::vector Globals; std::tie(Locals, Globals) = readSymbols(); expect(";"); if (!atEOF()) { setError("EOF expected, but got " + next()); return; } if (!Locals.empty()) { setError("\"local:\" scope not supported in --dynamic-list"); return; } for (SymbolVersion V : Globals) Config->DynamicList.push_back(V); } void ScriptParser::readVersionScript() { readVersionScriptCommand(); if (!atEOF()) setError("EOF expected, but got " + next()); } void ScriptParser::readVersionScriptCommand() { if (consume("{")) { readAnonymousDeclaration(); return; } while (!atEOF() && !errorCount() && peek() != "}") { StringRef VerStr = next(); if (VerStr == "{") { setError("anonymous version definition is used in " "combination with other version definitions"); return; } expect("{"); readVersionDeclaration(VerStr); } } void ScriptParser::readVersion() { expect("{"); readVersionScriptCommand(); expect("}"); } void ScriptParser::readLinkerScript() { while (!atEOF()) { StringRef Tok = next(); if (Tok == ";") continue; if (Tok == "ASSERT") { Script->SectionCommands.push_back(readAssert()); } else if (Tok == "ENTRY") { readEntry(); } else if (Tok == "EXTERN") { readExtern(); } else if (Tok == "GROUP" || Tok == "INPUT") { readGroup(); } else if (Tok == "INCLUDE") { readInclude(); } else if (Tok == "MEMORY") { readMemory(); } else if (Tok == "OUTPUT") { readOutput(); } else if (Tok == "OUTPUT_ARCH") { readOutputArch(); } else if (Tok == "OUTPUT_FORMAT") { readOutputFormat(); } else if (Tok == "PHDRS") { readPhdrs(); } else if (Tok == "REGION_ALIAS") { readRegionAlias(); } else if (Tok == "SEARCH_DIR") { readSearchDir(); } else if (Tok == "SECTIONS") { readSections(); } else if (Tok == "VERSION") { readVersion(); } else if (SymbolAssignment *Cmd = readProvideOrAssignment(Tok)) { Script->SectionCommands.push_back(Cmd); } else { setError("unknown directive: " + Tok); } } } void ScriptParser::readDefsym(StringRef Name) { Expr E = readExpr(); if (!atEOF()) setError("EOF expected, but got " + next()); SymbolAssignment *Cmd = make(Name, E, getCurrentLocation()); Script->SectionCommands.push_back(Cmd); } void ScriptParser::addFile(StringRef S) { if (IsUnderSysroot && S.startswith("/")) { SmallString<128> PathData; StringRef Path = (Config->Sysroot + S).toStringRef(PathData); if (sys::fs::exists(Path)) { Driver->addFile(Saver.save(Path), /*WithLOption=*/false); return; } } if (S.startswith("/")) { Driver->addFile(S, /*WithLOption=*/false); } else if (S.startswith("=")) { if (Config->Sysroot.empty()) Driver->addFile(S.substr(1), /*WithLOption=*/false); else Driver->addFile(Saver.save(Config->Sysroot + "/" + S.substr(1)), /*WithLOption=*/false); } else if (S.startswith("-l")) { Driver->addLibrary(S.substr(2)); } else if (sys::fs::exists(S)) { Driver->addFile(S, /*WithLOption=*/false); } else { if (Optional Path = findFromSearchPaths(S)) Driver->addFile(Saver.save(*Path), /*WithLOption=*/true); else setError("unable to find " + S); } } void ScriptParser::readAsNeeded() { expect("("); bool Orig = Config->AsNeeded; Config->AsNeeded = true; while (!errorCount() && !consume(")")) addFile(unquote(next())); Config->AsNeeded = Orig; } void ScriptParser::readEntry() { // -e takes predecence over ENTRY(). expect("("); StringRef Tok = next(); if (Config->Entry.empty()) Config->Entry = Tok; expect(")"); } void ScriptParser::readExtern() { expect("("); while (!errorCount() && !consume(")")) Config->Undefined.push_back(next()); } void ScriptParser::readGroup() { expect("("); while (!errorCount() && !consume(")")) { if (consume("AS_NEEDED")) readAsNeeded(); else addFile(unquote(next())); } } void ScriptParser::readInclude() { StringRef Tok = unquote(next()); if (!Seen.insert(Tok).second) { setError("there is a cycle in linker script INCLUDEs"); return; } if (Optional Path = searchLinkerScript(Tok)) { if (Optional MB = readFile(*Path)) tokenize(*MB); return; } setError("cannot find linker script " + Tok); } void ScriptParser::readOutput() { // -o takes predecence over OUTPUT(). expect("("); StringRef Tok = next(); if (Config->OutputFile.empty()) Config->OutputFile = unquote(Tok); expect(")"); } void ScriptParser::readOutputArch() { // OUTPUT_ARCH is ignored for now. expect("("); while (!errorCount() && !consume(")")) skip(); } void ScriptParser::readOutputFormat() { // Error checking only for now. expect("("); skip(); if (consume(")")) return; expect(","); skip(); expect(","); skip(); expect(")"); } void ScriptParser::readPhdrs() { expect("{"); while (!errorCount() && !consume("}")) { PhdrsCommand Cmd; Cmd.Name = next(); Cmd.Type = readPhdrType(); while (!errorCount() && !consume(";")) { if (consume("FILEHDR")) Cmd.HasFilehdr = true; else if (consume("PHDRS")) Cmd.HasPhdrs = true; else if (consume("AT")) Cmd.LMAExpr = readParenExpr(); else if (consume("FLAGS")) Cmd.Flags = readParenExpr()().getValue(); else setError("unexpected header attribute: " + next()); } Script->PhdrsCommands.push_back(Cmd); } } void ScriptParser::readRegionAlias() { expect("("); StringRef Alias = unquote(next()); expect(","); StringRef Name = next(); expect(")"); if (Script->MemoryRegions.count(Alias)) setError("redefinition of memory region '" + Alias + "'"); if (!Script->MemoryRegions.count(Name)) setError("memory region '" + Name + "' is not defined"); Script->MemoryRegions.insert({Alias, Script->MemoryRegions[Name]}); } void ScriptParser::readSearchDir() { expect("("); StringRef Tok = next(); if (!Config->Nostdlib) Config->SearchPaths.push_back(unquote(Tok)); expect(")"); } void ScriptParser::readSections() { Script->HasSectionsCommand = true; // -no-rosegment is used to avoid placing read only non-executable sections in // their own segment. We do the same if SECTIONS command is present in linker // script. See comment for computeFlags(). Config->SingleRoRx = true; expect("{"); while (!errorCount() && !consume("}")) { StringRef Tok = next(); BaseCommand *Cmd = readProvideOrAssignment(Tok); if (!Cmd) { if (Tok == "ASSERT") Cmd = readAssert(); else Cmd = readOutputSectionDescription(Tok); } Script->SectionCommands.push_back(Cmd); } } static int precedence(StringRef Op) { return StringSwitch(Op) .Cases("*", "/", 5) .Cases("+", "-", 4) .Cases("<<", ">>", 3) .Cases("<", "<=", ">", ">=", "==", "!=", 2) .Cases("&", "|", 1) .Default(-1); } StringMatcher ScriptParser::readFilePatterns() { std::vector V; while (!errorCount() && !consume(")")) V.push_back(next()); return StringMatcher(V); } SortSectionPolicy ScriptParser::readSortKind() { if (consume("SORT") || consume("SORT_BY_NAME")) return SortSectionPolicy::Name; if (consume("SORT_BY_ALIGNMENT")) return SortSectionPolicy::Alignment; if (consume("SORT_BY_INIT_PRIORITY")) return SortSectionPolicy::Priority; if (consume("SORT_NONE")) return SortSectionPolicy::None; return SortSectionPolicy::Default; } // Reads SECTIONS command contents in the following form: // // ::= * // ::= ? // ::= "EXCLUDE_FILE" "(" + ")" // // For example, // // *(.foo EXCLUDE_FILE (a.o) .bar EXCLUDE_FILE (b.o) .baz) // // is parsed as ".foo", ".bar" with "a.o", and ".baz" with "b.o". // The semantics of that is section .foo in any file, section .bar in // any file but a.o, and section .baz in any file but b.o. std::vector ScriptParser::readInputSectionsList() { std::vector Ret; while (!errorCount() && peek() != ")") { StringMatcher ExcludeFilePat; if (consume("EXCLUDE_FILE")) { expect("("); ExcludeFilePat = readFilePatterns(); } std::vector V; while (!errorCount() && peek() != ")" && peek() != "EXCLUDE_FILE") V.push_back(next()); if (!V.empty()) Ret.push_back({std::move(ExcludeFilePat), StringMatcher(V)}); else setError("section pattern is expected"); } return Ret; } // Reads contents of "SECTIONS" directive. That directive contains a // list of glob patterns for input sections. The grammar is as follows. // // ::= // | "(" ")" // | "(" "(" ")" ")" // // ::= "SORT" | "SORT_BY_NAME" | "SORT_BY_ALIGNMENT" // | "SORT_BY_INIT_PRIORITY" | "SORT_NONE" // // is parsed by readInputSectionsList(). InputSectionDescription * ScriptParser::readInputSectionRules(StringRef FilePattern) { auto *Cmd = make(FilePattern); expect("("); while (!errorCount() && !consume(")")) { SortSectionPolicy Outer = readSortKind(); SortSectionPolicy Inner = SortSectionPolicy::Default; std::vector V; if (Outer != SortSectionPolicy::Default) { expect("("); Inner = readSortKind(); if (Inner != SortSectionPolicy::Default) { expect("("); V = readInputSectionsList(); expect(")"); } else { V = readInputSectionsList(); } expect(")"); } else { V = readInputSectionsList(); } for (SectionPattern &Pat : V) { Pat.SortInner = Inner; Pat.SortOuter = Outer; } std::move(V.begin(), V.end(), std::back_inserter(Cmd->SectionPatterns)); } return Cmd; } InputSectionDescription * ScriptParser::readInputSectionDescription(StringRef Tok) { // Input section wildcard can be surrounded by KEEP. // https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep if (Tok == "KEEP") { expect("("); StringRef FilePattern = next(); InputSectionDescription *Cmd = readInputSectionRules(FilePattern); expect(")"); Script->KeptSections.push_back(Cmd); return Cmd; } return readInputSectionRules(Tok); } void ScriptParser::readSort() { expect("("); expect("CONSTRUCTORS"); expect(")"); } AssertCommand *ScriptParser::readAssert() { return make(readAssertExpr()); } Expr ScriptParser::readAssertExpr() { expect("("); Expr E = readExpr(); expect(","); StringRef Msg = unquote(next()); expect(")"); return [=] { if (!E().getValue()) error(Msg); return Script->getDot(); }; } // Reads a FILL(expr) command. We handle the FILL command as an // alias for =fillexp section attribute, which is different from // what GNU linkers do. // https://sourceware.org/binutils/docs/ld/Output-Section-Data.html uint32_t ScriptParser::readFill() { expect("("); uint32_t V = parseFill(next()); expect(")"); return V; } // Reads an expression and/or the special directive "(NOLOAD)" for an // output section definition. // // An output section name can be followed by an address expression // and/or by "(NOLOAD)". This grammar is not LL(1) because "(" can be // interpreted as either the beginning of some expression or "(NOLOAD)". // // https://sourceware.org/binutils/docs/ld/Output-Section-Address.html // https://sourceware.org/binutils/docs/ld/Output-Section-Type.html void ScriptParser::readSectionAddressType(OutputSection *Cmd) { if (consume("(")) { if (consume("NOLOAD")) { expect(")"); Cmd->Noload = true; return; } Cmd->AddrExpr = readExpr(); expect(")"); } else { Cmd->AddrExpr = readExpr(); } if (consume("(")) { expect("NOLOAD"); expect(")"); Cmd->Noload = true; } } static Expr checkAlignment(Expr E, std::string &Loc) { return [=] { uint64_t Alignment = std::max((uint64_t)1, E().getValue()); if (!isPowerOf2_64(Alignment)) { error(Loc + ": alignment must be power of 2"); return (uint64_t)1; // Return a dummy value. } return Alignment; }; } OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) { OutputSection *Cmd = Script->createOutputSection(OutSec, getCurrentLocation()); if (peek() != ":") readSectionAddressType(Cmd); expect(":"); std::string Location = getCurrentLocation(); if (consume("AT")) Cmd->LMAExpr = readParenExpr(); if (consume("ALIGN")) Cmd->AlignExpr = checkAlignment(readParenExpr(), Location); if (consume("SUBALIGN")) Cmd->SubalignExpr = checkAlignment(readParenExpr(), Location); // Parse constraints. if (consume("ONLY_IF_RO")) Cmd->Constraint = ConstraintKind::ReadOnly; if (consume("ONLY_IF_RW")) Cmd->Constraint = ConstraintKind::ReadWrite; expect("{"); while (!errorCount() && !consume("}")) { StringRef Tok = next(); if (Tok == ";") { // Empty commands are allowed. Do nothing here. } else if (SymbolAssignment *Assign = readProvideOrAssignment(Tok)) { Cmd->SectionCommands.push_back(Assign); } else if (ByteCommand *Data = readByteCommand(Tok)) { Cmd->SectionCommands.push_back(Data); } else if (Tok == "ASSERT") { Cmd->SectionCommands.push_back(readAssert()); expect(";"); } else if (Tok == "CONSTRUCTORS") { // CONSTRUCTORS is a keyword to make the linker recognize C++ ctors/dtors // by name. This is for very old file formats such as ECOFF/XCOFF. // For ELF, we should ignore. } else if (Tok == "FILL") { Cmd->Filler = readFill(); } else if (Tok == "SORT") { readSort(); } else if (peek() == "(") { Cmd->SectionCommands.push_back(readInputSectionDescription(Tok)); } else { setError("unknown command " + Tok); } } if (consume(">")) Cmd->MemoryRegionName = next(); - else if (peek().startswith(">")) - Cmd->MemoryRegionName = next().drop_front(); Cmd->Phdrs = readOutputSectionPhdrs(); if (consume("=")) Cmd->Filler = parseFill(next()); else if (peek().startswith("=")) Cmd->Filler = parseFill(next().drop_front()); // Consume optional comma following output section command. consume(","); return Cmd; } // Parses a given string as a octal/decimal/hexadecimal number and // returns it as a big-endian number. Used for `=`. // https://sourceware.org/binutils/docs/ld/Output-Section-Fill.html // // When reading a hexstring, ld.bfd handles it as a blob of arbitrary // size, while ld.gold always handles it as a 32-bit big-endian number. // We are compatible with ld.gold because it's easier to implement. uint32_t ScriptParser::parseFill(StringRef Tok) { uint32_t V = 0; if (!to_integer(Tok, V)) setError("invalid filler expression: " + Tok); uint32_t Buf; write32be(&Buf, V); return Buf; } SymbolAssignment *ScriptParser::readProvideHidden(bool Provide, bool Hidden) { expect("("); SymbolAssignment *Cmd = readAssignment(next()); Cmd->Provide = Provide; Cmd->Hidden = Hidden; expect(")"); expect(";"); return Cmd; } SymbolAssignment *ScriptParser::readProvideOrAssignment(StringRef Tok) { SymbolAssignment *Cmd = nullptr; if (peek() == "=" || peek() == "+=") { Cmd = readAssignment(Tok); expect(";"); } else if (Tok == "PROVIDE") { Cmd = readProvideHidden(true, false); } else if (Tok == "HIDDEN") { Cmd = readProvideHidden(false, true); } else if (Tok == "PROVIDE_HIDDEN") { Cmd = readProvideHidden(true, true); } return Cmd; } SymbolAssignment *ScriptParser::readAssignment(StringRef Name) { StringRef Op = next(); assert(Op == "=" || Op == "+="); Expr E = readExpr(); if (Op == "+=") { std::string Loc = getCurrentLocation(); E = [=] { return add(Script->getSymbolValue(Name, Loc), E()); }; } return make(Name, E, getCurrentLocation()); } // This is an operator-precedence parser to parse a linker // script expression. Expr ScriptParser::readExpr() { // Our lexer is context-aware. Set the in-expression bit so that // they apply different tokenization rules. bool Orig = InExpr; InExpr = true; Expr E = readExpr1(readPrimary(), 0); InExpr = Orig; return E; } static Expr combine(StringRef Op, Expr L, Expr R) { if (Op == "+") return [=] { return add(L(), R()); }; if (Op == "-") return [=] { return sub(L(), R()); }; if (Op == "*") return [=] { return mul(L(), R()); }; if (Op == "/") return [=] { return div(L(), R()); }; if (Op == "<<") return [=] { return L().getValue() << R().getValue(); }; if (Op == ">>") return [=] { return L().getValue() >> R().getValue(); }; if (Op == "<") return [=] { return L().getValue() < R().getValue(); }; if (Op == ">") return [=] { return L().getValue() > R().getValue(); }; if (Op == ">=") return [=] { return L().getValue() >= R().getValue(); }; if (Op == "<=") return [=] { return L().getValue() <= R().getValue(); }; if (Op == "==") return [=] { return L().getValue() == R().getValue(); }; if (Op == "!=") return [=] { return L().getValue() != R().getValue(); }; if (Op == "&") return [=] { return bitAnd(L(), R()); }; if (Op == "|") return [=] { return bitOr(L(), R()); }; llvm_unreachable("invalid operator"); } // This is a part of the operator-precedence parser. This function // assumes that the remaining token stream starts with an operator. Expr ScriptParser::readExpr1(Expr Lhs, int MinPrec) { while (!atEOF() && !errorCount()) { // Read an operator and an expression. if (consume("?")) return readTernary(Lhs); StringRef Op1 = peek(); if (precedence(Op1) < MinPrec) break; skip(); Expr Rhs = readPrimary(); // Evaluate the remaining part of the expression first if the // next operator has greater precedence than the previous one. // For example, if we have read "+" and "3", and if the next // operator is "*", then we'll evaluate 3 * ... part first. while (!atEOF()) { StringRef Op2 = peek(); if (precedence(Op2) <= precedence(Op1)) break; Rhs = readExpr1(Rhs, precedence(Op2)); } Lhs = combine(Op1, Lhs, Rhs); } return Lhs; } Expr ScriptParser::getPageSize() { std::string Location = getCurrentLocation(); return [=]() -> uint64_t { if (Target) return Target->PageSize; error(Location + ": unable to calculate page size"); return 4096; // Return a dummy value. }; } Expr ScriptParser::readConstant() { StringRef S = readParenLiteral(); if (S == "COMMONPAGESIZE") return getPageSize(); if (S == "MAXPAGESIZE") return [] { return Config->MaxPageSize; }; setError("unknown constant: " + S); return {}; } // Parses Tok as an integer. It recognizes hexadecimal (prefixed with // "0x" or suffixed with "H") and decimal numbers. Decimal numbers may // have "K" (Ki) or "M" (Mi) suffixes. static Optional parseInt(StringRef Tok) { // Negative number if (Tok.startswith("-")) { if (Optional Val = parseInt(Tok.substr(1))) return -*Val; return None; } // Hexadecimal uint64_t Val; if (Tok.startswith_lower("0x")) { if (!to_integer(Tok.substr(2), Val, 16)) return None; return Val; } if (Tok.endswith_lower("H")) { if (!to_integer(Tok.drop_back(), Val, 16)) return None; return Val; } // Decimal if (Tok.endswith_lower("K")) { if (!to_integer(Tok.drop_back(), Val, 10)) return None; return Val * 1024; } if (Tok.endswith_lower("M")) { if (!to_integer(Tok.drop_back(), Val, 10)) return None; return Val * 1024 * 1024; } if (!to_integer(Tok, Val, 10)) return None; return Val; } ByteCommand *ScriptParser::readByteCommand(StringRef Tok) { int Size = StringSwitch(Tok) .Case("BYTE", 1) .Case("SHORT", 2) .Case("LONG", 4) .Case("QUAD", 8) .Default(-1); if (Size == -1) return nullptr; return make(readParenExpr(), Size); } StringRef ScriptParser::readParenLiteral() { expect("("); StringRef Tok = next(); expect(")"); return Tok; } static void checkIfExists(OutputSection *Cmd, StringRef Location) { if (Cmd->Location.empty() && Script->ErrorOnMissingSection) error(Location + ": undefined section " + Cmd->Name); } Expr ScriptParser::readPrimary() { if (peek() == "(") return readParenExpr(); if (consume("~")) { Expr E = readPrimary(); return [=] { return ~E().getValue(); }; } if (consume("!")) { Expr E = readPrimary(); return [=] { return !E().getValue(); }; } if (consume("-")) { Expr E = readPrimary(); return [=] { return -E().getValue(); }; } StringRef Tok = next(); std::string Location = getCurrentLocation(); // Built-in functions are parsed here. // https://sourceware.org/binutils/docs/ld/Builtin-Functions.html. if (Tok == "ABSOLUTE") { Expr Inner = readParenExpr(); return [=] { ExprValue I = Inner(); I.ForceAbsolute = true; return I; }; } if (Tok == "ADDR") { StringRef Name = readParenLiteral(); OutputSection *Sec = Script->getOrCreateOutputSection(Name); return [=]() -> ExprValue { checkIfExists(Sec, Location); return {Sec, false, 0, Location}; }; } if (Tok == "ALIGN") { expect("("); Expr E = readExpr(); if (consume(")")) { E = checkAlignment(E, Location); return [=] { return alignTo(Script->getDot(), E().getValue()); }; } expect(","); Expr E2 = checkAlignment(readExpr(), Location); expect(")"); return [=] { ExprValue V = E(); V.Alignment = E2().getValue(); return V; }; } if (Tok == "ALIGNOF") { StringRef Name = readParenLiteral(); OutputSection *Cmd = Script->getOrCreateOutputSection(Name); return [=] { checkIfExists(Cmd, Location); return Cmd->Alignment; }; } if (Tok == "ASSERT") return readAssertExpr(); if (Tok == "CONSTANT") return readConstant(); if (Tok == "DATA_SEGMENT_ALIGN") { expect("("); Expr E = readExpr(); expect(","); readExpr(); expect(")"); return [=] { return alignTo(Script->getDot(), std::max((uint64_t)1, E().getValue())); }; } if (Tok == "DATA_SEGMENT_END") { expect("("); expect("."); expect(")"); return [] { return Script->getDot(); }; } if (Tok == "DATA_SEGMENT_RELRO_END") { // GNU linkers implements more complicated logic to handle // DATA_SEGMENT_RELRO_END. We instead ignore the arguments and // just align to the next page boundary for simplicity. expect("("); readExpr(); expect(","); readExpr(); expect(")"); Expr E = getPageSize(); return [=] { return alignTo(Script->getDot(), E().getValue()); }; } if (Tok == "DEFINED") { StringRef Name = readParenLiteral(); return [=] { return Symtab->find(Name) ? 1 : 0; }; } if (Tok == "LENGTH") { StringRef Name = readParenLiteral(); if (Script->MemoryRegions.count(Name) == 0) setError("memory region not defined: " + Name); return [=] { return Script->MemoryRegions[Name]->Length; }; } if (Tok == "LOADADDR") { StringRef Name = readParenLiteral(); OutputSection *Cmd = Script->getOrCreateOutputSection(Name); return [=] { checkIfExists(Cmd, Location); return Cmd->getLMA(); }; } if (Tok == "ORIGIN") { StringRef Name = readParenLiteral(); if (Script->MemoryRegions.count(Name) == 0) setError("memory region not defined: " + Name); return [=] { return Script->MemoryRegions[Name]->Origin; }; } if (Tok == "SEGMENT_START") { expect("("); skip(); expect(","); Expr E = readExpr(); expect(")"); return [=] { return E(); }; } if (Tok == "SIZEOF") { StringRef Name = readParenLiteral(); OutputSection *Cmd = Script->getOrCreateOutputSection(Name); // Linker script does not create an output section if its content is empty. // We want to allow SIZEOF(.foo) where .foo is a section which happened to // be empty. return [=] { return Cmd->Size; }; } if (Tok == "SIZEOF_HEADERS") return [=] { return elf::getHeaderSize(); }; // Tok is the dot. if (Tok == ".") return [=] { return Script->getSymbolValue(Tok, Location); }; // Tok is a literal number. if (Optional Val = parseInt(Tok)) return [=] { return *Val; }; // Tok is a symbol name. if (!isValidCIdentifier(Tok)) setError("malformed number: " + Tok); Script->ReferencedSymbols.push_back(Tok); return [=] { return Script->getSymbolValue(Tok, Location); }; } Expr ScriptParser::readTernary(Expr Cond) { Expr L = readExpr(); expect(":"); Expr R = readExpr(); return [=] { return Cond().getValue() ? L() : R(); }; } Expr ScriptParser::readParenExpr() { expect("("); Expr E = readExpr(); expect(")"); return E; } std::vector ScriptParser::readOutputSectionPhdrs() { std::vector Phdrs; while (!errorCount() && peek().startswith(":")) { StringRef Tok = next(); Phdrs.push_back((Tok.size() == 1) ? next() : Tok.substr(1)); } return Phdrs; } // Read a program header type name. The next token must be a // name of a program header type or a constant (e.g. "0x3"). unsigned ScriptParser::readPhdrType() { StringRef Tok = next(); if (Optional Val = parseInt(Tok)) return *Val; unsigned Ret = StringSwitch(Tok) .Case("PT_NULL", PT_NULL) .Case("PT_LOAD", PT_LOAD) .Case("PT_DYNAMIC", PT_DYNAMIC) .Case("PT_INTERP", PT_INTERP) .Case("PT_NOTE", PT_NOTE) .Case("PT_SHLIB", PT_SHLIB) .Case("PT_PHDR", PT_PHDR) .Case("PT_TLS", PT_TLS) .Case("PT_GNU_EH_FRAME", PT_GNU_EH_FRAME) .Case("PT_GNU_STACK", PT_GNU_STACK) .Case("PT_GNU_RELRO", PT_GNU_RELRO) .Case("PT_OPENBSD_RANDOMIZE", PT_OPENBSD_RANDOMIZE) .Case("PT_OPENBSD_WXNEEDED", PT_OPENBSD_WXNEEDED) .Case("PT_OPENBSD_BOOTDATA", PT_OPENBSD_BOOTDATA) .Default(-1); if (Ret == (unsigned)-1) { setError("invalid program header type: " + Tok); return PT_NULL; } return Ret; } // Reads an anonymous version declaration. void ScriptParser::readAnonymousDeclaration() { std::vector Locals; std::vector Globals; std::tie(Locals, Globals) = readSymbols(); for (SymbolVersion V : Locals) { if (V.Name == "*") Config->DefaultSymbolVersion = VER_NDX_LOCAL; else Config->VersionScriptLocals.push_back(V); } for (SymbolVersion V : Globals) Config->VersionScriptGlobals.push_back(V); expect(";"); } // Reads a non-anonymous version definition, // e.g. "VerStr { global: foo; bar; local: *; };". void ScriptParser::readVersionDeclaration(StringRef VerStr) { // Read a symbol list. std::vector Locals; std::vector Globals; std::tie(Locals, Globals) = readSymbols(); for (SymbolVersion V : Locals) { if (V.Name == "*") Config->DefaultSymbolVersion = VER_NDX_LOCAL; else Config->VersionScriptLocals.push_back(V); } // Create a new version definition and add that to the global symbols. VersionDefinition Ver; Ver.Name = VerStr; Ver.Globals = Globals; // User-defined version number starts from 2 because 0 and 1 are // reserved for VER_NDX_LOCAL and VER_NDX_GLOBAL, respectively. Ver.Id = Config->VersionDefinitions.size() + 2; Config->VersionDefinitions.push_back(Ver); // Each version may have a parent version. For example, "Ver2" // defined as "Ver2 { global: foo; local: *; } Ver1;" has "Ver1" // as a parent. This version hierarchy is, probably against your // instinct, purely for hint; the runtime doesn't care about it // at all. In LLD, we simply ignore it. if (peek() != ";") skip(); expect(";"); } static bool hasWildcard(StringRef S) { return S.find_first_of("?*[") != StringRef::npos; } // Reads a list of symbols, e.g. "{ global: foo; bar; local: *; };". std::pair, std::vector> ScriptParser::readSymbols() { std::vector Locals; std::vector Globals; std::vector *V = &Globals; while (!errorCount()) { if (consume("}")) break; if (consumeLabel("local")) { V = &Locals; continue; } if (consumeLabel("global")) { V = &Globals; continue; } if (consume("extern")) { std::vector Ext = readVersionExtern(); V->insert(V->end(), Ext.begin(), Ext.end()); } else { StringRef Tok = next(); V->push_back({unquote(Tok), false, hasWildcard(Tok)}); } expect(";"); } return {Locals, Globals}; } // Reads an "extern C++" directive, e.g., // "extern "C++" { ns::*; "f(int, double)"; };" std::vector ScriptParser::readVersionExtern() { StringRef Tok = next(); bool IsCXX = Tok == "\"C++\""; if (!IsCXX && Tok != "\"C\"") setError("Unknown language"); expect("{"); std::vector Ret; while (!errorCount() && peek() != "}") { StringRef Tok = next(); bool HasWildcard = !Tok.startswith("\"") && hasWildcard(Tok); Ret.push_back({unquote(Tok), IsCXX, HasWildcard}); expect(";"); } expect("}"); return Ret; } uint64_t ScriptParser::readMemoryAssignment(StringRef S1, StringRef S2, StringRef S3) { if (!consume(S1) && !consume(S2) && !consume(S3)) { setError("expected one of: " + S1 + ", " + S2 + ", or " + S3); return 0; } expect("="); return readExpr()().getValue(); } // Parse the MEMORY command as specified in: // https://sourceware.org/binutils/docs/ld/MEMORY.html // // MEMORY { name [(attr)] : ORIGIN = origin, LENGTH = len ... } void ScriptParser::readMemory() { expect("{"); while (!errorCount() && !consume("}")) { StringRef Name = next(); uint32_t Flags = 0; uint32_t NegFlags = 0; if (consume("(")) { std::tie(Flags, NegFlags) = readMemoryAttributes(); expect(")"); } expect(":"); uint64_t Origin = readMemoryAssignment("ORIGIN", "org", "o"); expect(","); uint64_t Length = readMemoryAssignment("LENGTH", "len", "l"); // Add the memory region to the region map. if (Script->MemoryRegions.count(Name)) setError("region '" + Name + "' already defined"); MemoryRegion *MR = make(); *MR = {Name, Origin, Length, Flags, NegFlags}; Script->MemoryRegions[Name] = MR; } } // This function parses the attributes used to match against section // flags when placing output sections in a memory region. These flags // are only used when an explicit memory region name is not used. std::pair ScriptParser::readMemoryAttributes() { uint32_t Flags = 0; uint32_t NegFlags = 0; bool Invert = false; for (char C : next().lower()) { uint32_t Flag = 0; if (C == '!') Invert = !Invert; else if (C == 'w') Flag = SHF_WRITE; else if (C == 'x') Flag = SHF_EXECINSTR; else if (C == 'a') Flag = SHF_ALLOC; else if (C != 'r') setError("invalid memory region attribute"); if (Invert) NegFlags |= Flag; else Flags |= Flag; } return {Flags, NegFlags}; } void elf::readLinkerScript(MemoryBufferRef MB) { ScriptParser(MB).readLinkerScript(); } void elf::readVersionScript(MemoryBufferRef MB) { ScriptParser(MB).readVersionScript(); } void elf::readDynamicList(MemoryBufferRef MB) { ScriptParser(MB).readDynamicList(); } void elf::readDefsym(StringRef Name, MemoryBufferRef MB) { ScriptParser(MB).readDefsym(Name); } Index: vendor/lld/dist/test/COFF/ignore4217.yaml =================================================================== --- vendor/lld/dist/test/COFF/ignore4217.yaml (nonexistent) +++ vendor/lld/dist/test/COFF/ignore4217.yaml (revision 327308) @@ -0,0 +1,72 @@ +# Tests that /ignore:4217 suppresses "locally defined symbol imported" warnings. +# RUN: yaml2obj < %s > %t.obj + +# RUN: lld-link -entry:main -out:%t.exe %t.obj 2>&1 \ +# RUN: | FileCheck -check-prefix=WARNINGS %s +# RUN: lld-link -ignore:4217 -entry:main -out:%t.exe %t.obj 2>&1 \ +# RUN: | FileCheck -allow-empty -check-prefix=SUPPRESSED %s + +# WARNINGS: locally defined symbol imported +# SUPPRESSED-NOT: locally defined symbol imported + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: B82A000000C3662E0F1F8400000000004883EC28C744242400000000E800000000904883C428C3 + Relocations: + - VirtualAddress: 29 + SymbolName: __imp_foo + Type: IMAGE_REL_AMD64_REL32 + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: '' +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 39 + NumberOfRelocations: 1 + NumberOfLinenumbers: 0 + CheckSum: 3087210877 + Number: 1 + - Name: .data + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 0 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 2 + - Name: foo + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: main + Value: 16 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: __imp_foo + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... Index: vendor/lld/dist/test/ELF/Inputs/writable-sec-plt-reloc.s =================================================================== --- vendor/lld/dist/test/ELF/Inputs/writable-sec-plt-reloc.s (nonexistent) +++ vendor/lld/dist/test/ELF/Inputs/writable-sec-plt-reloc.s (revision 327308) @@ -0,0 +1,4 @@ + .global foo + .type foo, @function +foo: + retq Property changes on: vendor/lld/dist/test/ELF/Inputs/writable-sec-plt-reloc.s ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/dist/test/ELF/Inputs/znotext-copy-relocations.s =================================================================== --- vendor/lld/dist/test/ELF/Inputs/znotext-copy-relocations.s (nonexistent) +++ vendor/lld/dist/test/ELF/Inputs/znotext-copy-relocations.s (revision 327308) @@ -0,0 +1,5 @@ +.global foo +.type foo,@object +.size foo, 8 +foo: +.quad 42 Property changes on: vendor/lld/dist/test/ELF/Inputs/znotext-copy-relocations.s ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/dist/test/ELF/Inputs/znotext-plt-relocations-protected.s =================================================================== --- vendor/lld/dist/test/ELF/Inputs/znotext-plt-relocations-protected.s (nonexistent) +++ vendor/lld/dist/test/ELF/Inputs/znotext-plt-relocations-protected.s (revision 327308) @@ -0,0 +1,5 @@ +.global foo +.type foo,@function +.protected foo +foo: + nop Property changes on: vendor/lld/dist/test/ELF/Inputs/znotext-plt-relocations-protected.s ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/dist/test/ELF/arm-thumb-no-undefined-thunk.s =================================================================== --- vendor/lld/dist/test/ELF/arm-thumb-no-undefined-thunk.s (revision 327307) +++ vendor/lld/dist/test/ELF/arm-thumb-no-undefined-thunk.s (revision 327308) @@ -1,24 +1,24 @@ // RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t // RUN: ld.lld %t -o %t2 2>&1 // RUN: llvm-objdump -triple=thumbv7a-none-linux-gnueabi -d %t2 | FileCheck %s // REQUIRES: arm // Check that no thunks are created for an undefined weak symbol .syntax unified .weak target .section .text.thumb, "ax", %progbits .thumb .global _start: bl target b target b.w target // CHECK: Disassembly of section .text: // CHECK-NEXT: _start: // 69636 = 0x11004 = next instruction // CHECK: 11000: {{.*}} bl #0 // CHECK-NEXT: 11004: {{.*}} b.w #0 <_start+0x8> -// CHECK-NEXT: 11008: {{.*}} b.w #0 <_start+0xC> +// CHECK-NEXT: 11008: {{.*}} b.w #0 <_start+0xc> Index: vendor/lld/dist/test/ELF/arm-undefined-weak.s =================================================================== --- vendor/lld/dist/test/ELF/arm-undefined-weak.s (revision 327307) +++ vendor/lld/dist/test/ELF/arm-undefined-weak.s (revision 327308) @@ -1,39 +1,39 @@ // RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t // RUN: ld.lld %t -o %t2 2>&1 // RUN: llvm-objdump -triple=armv7a-none-linux-gnueabi -d %t2 | FileCheck %s // REQUIRES: arm // Check that the ARM ABI rules for undefined weak symbols are applied. // Branch instructions are resolved to the next instruction. Undefined // Symbols in relative are resolved to the place so S - P + A = A. .syntax unified .weak target .text .global _start _start: // R_ARM_JUMP24 b target // R_ARM_CALL bl target // R_ARM_CALL with exchange blx target // R_ARM_MOVT_PREL movt r0, :upper16:target - . // R_ARM_MOVW_PREL_NC movw r0, :lower16:target - . // R_ARM_REL32 .word target - . // CHECK: Disassembly of section .text: // 69636 = 0x11004 // CHECK: 11000: {{.*}} b #-4 <_start+0x4> // CHECK-NEXT: 11004: {{.*}} bl #-4 <_start+0x8> // blx is transformed into bl so we don't change state -// CHECK-NEXT: 11008: {{.*}} bl #-4 <_start+0xC> +// CHECK-NEXT: 11008: {{.*}} bl #-4 <_start+0xc> // CHECK-NEXT: 1100c: {{.*}} movt r0, #0 // CHECK-NEXT: 11010: {{.*}} movw r0, #0 // CHECK: 11014: {{.*}} .word 0x00000000 Index: vendor/lld/dist/test/ELF/gnu-ifunc-i386.s =================================================================== --- vendor/lld/dist/test/ELF/gnu-ifunc-i386.s (revision 327307) +++ vendor/lld/dist/test/ELF/gnu-ifunc-i386.s (revision 327308) @@ -1,126 +1,126 @@ // RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t.o // RUN: ld.lld -static %t.o -o %tout // RUN: llvm-objdump -d %tout | FileCheck %s --check-prefix=DISASM // RUN: llvm-readobj -r -symbols -sections %tout | FileCheck %s // REQUIRES: x86 // CHECK: Sections [ // CHECK: Section { // CHECK: Index: 1 // CHECK-NEXT: Name: .rel.plt // CHECK-NEXT: Type: SHT_REL // CHECK-NEXT: Flags [ // CHECK-NEXT: SHF_ALLOC // CHECK-NEXT: ] // CHECK-NEXT: Address: [[RELA:.*]] // CHECK-NEXT: Offset: 0xD4 // CHECK-NEXT: Size: 16 // CHECK-NEXT: Link: 0 // CHECK-NEXT: Info: 0 // CHECK-NEXT: AddressAlignment: 4 // CHECK-NEXT: EntrySize: 8 // CHECK-NEXT: } // CHECK: Relocations [ // CHECK-NEXT: Section ({{.*}}) .rel.plt { // CHECK-NEXT: 0x12000 R_386_IRELATIVE // CHECK-NEXT: 0x12004 R_386_IRELATIVE // CHECK-NEXT: } // CHECK-NEXT: ] // CHECK: Symbols [ // CHECK-NEXT: Symbol { // CHECK-NEXT: Name: // CHECK-NEXT: Value: 0x0 // CHECK-NEXT: Size: 0 // CHECK-NEXT: Binding: Local // CHECK-NEXT: Type: None // CHECK-NEXT: Other: 0 // CHECK-NEXT: Section: Undefined // CHECK-NEXT: } // CHECK-NEXT: Symbol { // CHECK-NEXT: Name: __rel_iplt_end // CHECK-NEXT: Value: 0x100E4 // CHECK-NEXT: Size: 0 // CHECK-NEXT: Binding: Local // CHECK-NEXT: Type: None // CHECK-NEXT: Other [ // CHECK-NEXT: STV_HIDDEN // CHECK-NEXT: ] // CHECK-NEXT: Section: .rel.plt // CHECK-NEXT: } // CHECK-NEXT: Symbol { // CHECK-NEXT: Name: __rel_iplt_start // CHECK-NEXT: Value: [[RELA]] // CHECK-NEXT: Size: 0 // CHECK-NEXT: Binding: Local // CHECK-NEXT: Type: None // CHECK-NEXT: Other [ // CHECK-NEXT: STV_HIDDEN // CHECK-NEXT: ] // CHECK-NEXT: Section: .rel.plt // CHECK-NEXT: } // CHECK-NEXT: Symbol { // CHECK-NEXT: Name: _start // CHECK-NEXT: Value: 0x11002 // CHECK-NEXT: Size: 0 // CHECK-NEXT: Binding: Global // CHECK-NEXT: Type: None // CHECK-NEXT: Other: 0 // CHECK-NEXT: Section: .text // CHECK-NEXT: } // CHECK-NEXT: Symbol { // CHECK-NEXT: Name: bar // CHECK-NEXT: Value: 0x11001 // CHECK-NEXT: Size: 0 // CHECK-NEXT: Binding: Global // CHECK-NEXT: Type: GNU_IFunc // CHECK-NEXT: Other: 0 // CHECK-NEXT: Section: .text // CHECK-NEXT: } // CHECK-NEXT: Symbol { // CHECK-NEXT: Name: foo // CHECK-NEXT: Value: 0x11000 // CHECK-NEXT: Size: 0 // CHECK-NEXT: Binding: Global // CHECK-NEXT: Type: GNU_IFunc // CHECK-NEXT: Other: 0 // CHECK-NEXT: Section: .text // CHECK-NEXT: } // CHECK-NEXT:] // DISASM: Disassembly of section .text: // DISASM-NEXT: foo: // DISASM-NEXT: 11000: c3 retl // DISASM: bar: // DISASM-NEXT: 11001: c3 retl // DISASM: _start: // DISASM-NEXT: 11002: e8 19 00 00 00 calll 25 // DISASM-NEXT: 11007: e8 24 00 00 00 calll 36 // DISASM-NEXT: 1100c: ba d4 00 01 00 movl $65748, %edx // DISASM-NEXT: 11011: ba e4 00 01 00 movl $65764, %edx // DISASM-NEXT: Disassembly of section .plt: // DISASM-NEXT: .plt: // DISASM-NEXT: 11020: ff 25 00 20 01 00 jmpl *73728 // DISASM-NEXT: 11026: 68 10 00 00 00 pushl $16 -// DISASM-NEXT: 1102b: e9 e0 ff ff ff jmp -32 <_start+0xE> +// DISASM-NEXT: 1102b: e9 e0 ff ff ff jmp -32 <_start+0xe> // DISASM-NEXT: 11030: ff 25 04 20 01 00 jmpl *73732 // DISASM-NEXT: 11036: 68 18 00 00 00 pushl $24 -// DISASM-NEXT: 1103b: e9 d0 ff ff ff jmp -48 <_start+0xE> +// DISASM-NEXT: 1103b: e9 d0 ff ff ff jmp -48 <_start+0xe> .text .type foo STT_GNU_IFUNC .globl foo foo: ret .type bar STT_GNU_IFUNC .globl bar bar: ret .globl _start _start: call foo call bar movl $__rel_iplt_start,%edx movl $__rel_iplt_end,%edx Index: vendor/lld/dist/test/ELF/gnu-ifunc.s =================================================================== --- vendor/lld/dist/test/ELF/gnu-ifunc.s (revision 327307) +++ vendor/lld/dist/test/ELF/gnu-ifunc.s (revision 327308) @@ -1,127 +1,127 @@ // RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o // RUN: ld.lld -static %t.o -o %tout // RUN: llvm-objdump -d %tout | FileCheck %s --check-prefix=DISASM // RUN: llvm-readobj -r -symbols -sections %tout | FileCheck %s // REQUIRES: x86 // CHECK: Sections [ // CHECK: Section { // CHECK: Index: 1 // CHECK-NEXT: Name: .rela.plt // CHECK-NEXT: Type: SHT_RELA // CHECK-NEXT: Flags [ // CHECK-NEXT: SHF_ALLOC // CHECK-NEXT: ] // CHECK-NEXT: Address: [[RELA:.*]] // CHECK-NEXT: Offset: 0x158 // CHECK-NEXT: Size: 48 // CHECK-NEXT: Link: 0 // CHECK-NEXT: Info: 0 // CHECK-NEXT: AddressAlignment: 8 // CHECK-NEXT: EntrySize: 24 // CHECK-NEXT: } // CHECK: Relocations [ // CHECK-NEXT: Section ({{.*}}) .rela.plt { // CHECK-NEXT: 0x202000 R_X86_64_IRELATIVE // CHECK-NEXT: 0x202008 R_X86_64_IRELATIVE // CHECK-NEXT: } // CHECK-NEXT: ] // CHECK: Symbols [ // CHECK-NEXT: Symbol { // CHECK-NEXT: Name: // CHECK-NEXT: Value: 0x0 // CHECK-NEXT: Size: 0 // CHECK-NEXT: Binding: Local // CHECK-NEXT: Type: None // CHECK-NEXT: Other: 0 // CHECK-NEXT: Section: Undefined // CHECK-NEXT: } // CHECK-NEXT: Symbol { // CHECK-NEXT: Name: __rela_iplt_end // CHECK-NEXT: Value: 0x200188 // CHECK-NEXT: Size: 0 // CHECK-NEXT: Binding: Local // CHECK-NEXT: Type: None // CHECK-NEXT: Other [ // CHECK-NEXT: STV_HIDDEN // CHECK-NEXT: ] // CHECK-NEXT: Section: .rela.plt // CHECK-NEXT: } // CHECK-NEXT: Symbol { // CHECK-NEXT: Name: __rela_iplt_start // CHECK-NEXT: Value: [[RELA]] // CHECK-NEXT: Size: 0 // CHECK-NEXT: Binding: Local // CHECK-NEXT: Type: None // CHECK-NEXT: Other [ // CHECK-NEXT: STV_HIDDEN // CHECK-NEXT: ] // CHECK-NEXT: Section: .rela.plt // CHECK-NEXT: } // CHECK-NEXT: Symbol { // CHECK-NEXT: Name: _start // CHECK-NEXT: Value: 0x201002 // CHECK-NEXT: Size: 0 // CHECK-NEXT: Binding: Global // CHECK-NEXT: Type: None // CHECK-NEXT: Other: 0 // CHECK-NEXT: Section: .text // CHECK-NEXT: } // CHECK-NEXT: Symbol { // CHECK-NEXT: Name: bar // CHECK-NEXT: Value: 0x201001 // CHECK-NEXT: Size: 0 // CHECK-NEXT: Binding: Global // CHECK-NEXT: Type: GNU_IFunc // CHECK-NEXT: Other: 0 // CHECK-NEXT: Section: .text // CHECK-NEXT: } // CHECK-NEXT: Symbol { // CHECK-NEXT: Name: foo // CHECK-NEXT: Value: 0x201000 // CHECK-NEXT: Size: 0 // CHECK-NEXT: Binding: Global // CHECK-NEXT: Type: GNU_IFunc // CHECK-NEXT: Other: 0 // CHECK-NEXT: Section: .text // CHECK-NEXT: } // CHECK-NEXT: ] // DISASM: Disassembly of section .text: // DISASM-NEXT: foo: // DISASM-NEXT: 201000: {{.*}} retq // DISASM: bar: // DISASM-NEXT: 201001: {{.*}} retq // DISASM: _start: // DISASM-NEXT: 201002: {{.*}} callq 25 // DISASM-NEXT: 201007: {{.*}} callq 36 // DISASM-NEXT: 20100c: {{.*}} movl $2097496, %edx // DISASM-NEXT: 201011: {{.*}} movl $2097544, %edx // DISASM-NEXT: 201016: {{.*}} movl $2097545, %edx // DISASM-NEXT: Disassembly of section .plt: // DISASM-NEXT: .plt: // DISASM-NEXT: 201020: {{.*}} jmpq *4058(%rip) // DISASM-NEXT: 201026: {{.*}} pushq $0 -// DISASM-NEXT: 20102b: {{.*}} jmp -32 <_start+0xE> +// DISASM-NEXT: 20102b: {{.*}} jmp -32 <_start+0xe> // DISASM-NEXT: 201030: {{.*}} jmpq *4050(%rip) // DISASM-NEXT: 201036: {{.*}} pushq $1 -// DISASM-NEXT: 20103b: {{.*}} jmp -48 <_start+0xE> +// DISASM-NEXT: 20103b: {{.*}} jmp -48 <_start+0xe> .text .type foo STT_GNU_IFUNC .globl foo foo: ret .type bar STT_GNU_IFUNC .globl bar bar: ret .globl _start _start: call foo call bar movl $__rela_iplt_start,%edx movl $__rela_iplt_end,%edx movl $__rela_iplt_end + 1,%edx Index: vendor/lld/dist/test/ELF/writable-sec-plt-reloc.s =================================================================== --- vendor/lld/dist/test/ELF/writable-sec-plt-reloc.s (nonexistent) +++ vendor/lld/dist/test/ELF/writable-sec-plt-reloc.s (revision 327308) @@ -0,0 +1,14 @@ +# REQUIRES: x86 +# RUN: llvm-mc %s -o %t.o -filetype=obj -triple=x86_64-pc-linux +# RUN: llvm-mc %p/Inputs/writable-sec-plt-reloc.s -o %t2.o -filetype=obj -triple=x86_64-pc-linux +# RUN: ld.lld %t2.o -o %t2.so -shared +# RUN: ld.lld %t.o %t2.so -o %t +# RUN: llvm-readelf --symbols -r %t | FileCheck %s + +# CHECK: R_X86_64_JUMP_SLOT {{.*}} foo + 0 +# CHECK: 0000000000201010 0 FUNC GLOBAL DEFAULT UND foo + +.section .bar,"awx" +.global _start +_start: + call foo Property changes on: vendor/lld/dist/test/ELF/writable-sec-plt-reloc.s ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/dist/test/ELF/x86-64-dyn-rel-error.s =================================================================== --- vendor/lld/dist/test/ELF/x86-64-dyn-rel-error.s (revision 327307) +++ vendor/lld/dist/test/ELF/x86-64-dyn-rel-error.s (revision 327308) @@ -1,14 +1,14 @@ // REQUIRES: x86 // RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o // RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/shared.s -o %t2.o // RUN: ld.lld %t2.o -shared -o %t2.so -// RUN: not ld.lld %t.o %t2.so -o %t 2>&1 | FileCheck %s +// RUN: not ld.lld -shared %t.o %t2.so -o %t 2>&1 | FileCheck %s .global _start _start: .data - .long bar + .long zed // CHECK: relocation R_X86_64_32 cannot be used against shared object; recompile with -fPIC // RUN: ld.lld --noinhibit-exec %t.o %t2.so -o %t 2>&1 | FileCheck %s Index: vendor/lld/dist/test/ELF/x86-64-dyn-rel-error2.s =================================================================== --- vendor/lld/dist/test/ELF/x86-64-dyn-rel-error2.s (revision 327307) +++ vendor/lld/dist/test/ELF/x86-64-dyn-rel-error2.s (revision 327308) @@ -1,14 +1,14 @@ // REQUIRES: x86 // RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o // RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/shared.s -o %t2.o // RUN: ld.lld %t2.o -shared -o %t2.so -// RUN: not ld.lld %t.o %t2.so -o %t 2>&1 | FileCheck %s +// RUN: not ld.lld -shared %t.o %t2.so -o %t 2>&1 | FileCheck %s // CHECK: relocation R_X86_64_PC32 cannot be used against shared object; recompile with -fPIC // CHECK: >>> defined in {{.*}}.so // CHECK: >>> referenced by {{.*}}.o:(.data+0x0) .global _start _start: .data - .long bar - . + .long zed - . Index: vendor/lld/dist/test/ELF/znotext-copy-relocation.s =================================================================== --- vendor/lld/dist/test/ELF/znotext-copy-relocation.s (nonexistent) +++ vendor/lld/dist/test/ELF/znotext-copy-relocation.s (revision 327308) @@ -0,0 +1,16 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/znotext-copy-relocations.s -o %t2.o +# RUN: ld.lld %t2.o -o %t2.so -shared +# RUN: ld.lld -z notext %t.o %t2.so -o %t +# RUN: llvm-readobj -r %t | FileCheck %s + +# CHECK: Relocations [ +# CHECK-NEXT: Section ({{.*}}) .rela.dyn { +# CHECK-NEXT: R_X86_64_COPY foo 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: ] + +.global _start +_start: +.long foo Property changes on: vendor/lld/dist/test/ELF/znotext-copy-relocation.s ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/dist/test/ELF/znotext-plt-relocations-protected.s =================================================================== --- vendor/lld/dist/test/ELF/znotext-plt-relocations-protected.s (nonexistent) +++ vendor/lld/dist/test/ELF/znotext-plt-relocations-protected.s (revision 327308) @@ -0,0 +1,11 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/znotext-plt-relocations-protected.s -o %t2.o +# RUN: ld.lld %t2.o -o %t2.so -shared +# RUN: not ld.lld -z notext %t.o %t2.so -o %t 2>&1 | FileCheck %s + +# CHECK: error: cannot preempt symbol: foo + +.global _start +_start: +callq foo Property changes on: vendor/lld/dist/test/ELF/znotext-plt-relocations-protected.s ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lld/dist/test/ELF/znotext-weak-undef.s =================================================================== --- vendor/lld/dist/test/ELF/znotext-weak-undef.s (nonexistent) +++ vendor/lld/dist/test/ELF/znotext-weak-undef.s (revision 327308) @@ -0,0 +1,16 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o +# RUN: not ld.lld -z notext -shared %t.o -o %t 2>&1 | FileCheck %s +# CHECK: relocation R_X86_64_32 cannot be used against shared object; recompile with -fPIC + +# RUN: ld.lld -z notext %t.o -o %t +# RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=EXE +# EXE: Relocations [ +# EXE-NEXT: ] + +.text +.global foo +.weak foo + +_start: +mov $foo,%eax Property changes on: vendor/lld/dist/test/ELF/znotext-weak-undef.s ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property