diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:48:50 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:48:50 +0000 |
commit | 1c98619801a5705c688e683be3ef9d70169a0686 (patch) | |
tree | 8422105cd1a94c368315f2db16b9ac746cf7c000 /COFF | |
parent | f4f3ce4613680903220815690ad79fc7ba0a2e26 (diff) | |
download | src-1c98619801a5705c688e683be3ef9d70169a0686.tar.gz src-1c98619801a5705c688e683be3ef9d70169a0686.zip |
Vendor import of lld release_39 branch r276489:vendor/lld/lld-release_39-r276489
Notes
Notes:
svn path=/vendor/lld/dist/; revision=303239
svn path=/vendor/lld/lld-release_39-r276489/; revision=303240; tag=vendor/lld/lld-release_39-r276489
Diffstat (limited to 'COFF')
-rw-r--r-- | COFF/CMakeLists.txt | 3 | ||||
-rw-r--r-- | COFF/Chunks.cpp | 13 | ||||
-rw-r--r-- | COFF/Chunks.h | 10 | ||||
-rw-r--r-- | COFF/Config.h | 4 | ||||
-rw-r--r-- | COFF/DLL.cpp | 2 | ||||
-rw-r--r-- | COFF/Driver.cpp | 55 | ||||
-rw-r--r-- | COFF/Driver.h | 5 | ||||
-rw-r--r-- | COFF/DriverUtils.cpp | 332 | ||||
-rw-r--r-- | COFF/Error.cpp | 13 | ||||
-rw-r--r-- | COFF/Error.h | 18 | ||||
-rw-r--r-- | COFF/ICF.cpp | 6 | ||||
-rw-r--r-- | COFF/InputFiles.cpp | 119 | ||||
-rw-r--r-- | COFF/InputFiles.h | 9 | ||||
-rw-r--r-- | COFF/Librarian.cpp | 489 | ||||
-rw-r--r-- | COFF/ModuleDef.cpp | 14 | ||||
-rw-r--r-- | COFF/Options.td | 2 | ||||
-rw-r--r-- | COFF/PDB.cpp | 3 | ||||
-rw-r--r-- | COFF/README.md | 266 | ||||
-rw-r--r-- | COFF/SymbolTable.cpp | 33 | ||||
-rw-r--r-- | COFF/SymbolTable.h | 2 | ||||
-rw-r--r-- | COFF/Symbols.cpp | 28 | ||||
-rw-r--r-- | COFF/Symbols.h | 4 | ||||
-rw-r--r-- | COFF/Writer.cpp | 72 |
23 files changed, 844 insertions, 658 deletions
diff --git a/COFF/CMakeLists.txt b/COFF/CMakeLists.txt index 3f31ba9ba1fb..3319f392efe1 100644 --- a/COFF/CMakeLists.txt +++ b/COFF/CMakeLists.txt @@ -10,6 +10,7 @@ add_lld_library(lldCOFF Error.cpp ICF.cpp InputFiles.cpp + Librarian.cpp MarkLive.cpp ModuleDef.cpp PDB.cpp @@ -28,6 +29,8 @@ add_lld_library(lldCOFF Target Option Support + + LINK_LIBS ${PTHREAD_LIB} ) add_dependencies(lldCOFF COFFOptionsTableGen) diff --git a/COFF/Chunks.cpp b/COFF/Chunks.cpp index f9f768d69866..1c1b18176aa2 100644 --- a/COFF/Chunks.cpp +++ b/COFF/Chunks.cpp @@ -34,10 +34,7 @@ SectionChunk::SectionChunk(ObjectFile *F, const coff_section *H) // Initialize SectionName. File->getCOFFObj()->getSectionName(Header, SectionName); - // Bit [20:24] contains section alignment. Both 0 and 1 mean alignment 1. - unsigned Shift = (Header->Characteristics >> 20) & 0xF; - if (Shift > 0) - Align = uint32_t(1) << (Shift - 1); + Align = Header->getAlignment(); // Only COMDAT sections are subject of dead-stripping. Live = !isCOMDAT(); @@ -64,7 +61,7 @@ void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, Defined *Sym, case IMAGE_REL_AMD64_SECTION: add16(Off, Sym->getSectionIndex()); break; case IMAGE_REL_AMD64_SECREL: add32(Off, Sym->getSecrel()); break; default: - error("Unsupported relocation type"); + fatal("unsupported relocation type"); } } @@ -79,7 +76,7 @@ void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, Defined *Sym, case IMAGE_REL_I386_SECTION: add16(Off, Sym->getSectionIndex()); break; case IMAGE_REL_I386_SECREL: add32(Off, Sym->getSecrel()); break; default: - error("Unsupported relocation type"); + fatal("unsupported relocation type"); } } @@ -123,7 +120,7 @@ void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, Defined *Sym, case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(Off, S - P - 4); break; case IMAGE_REL_ARM_BLX23T: applyBranch24T(Off, S - P - 4); break; default: - error("Unsupported relocation type"); + fatal("unsupported relocation type"); } } @@ -310,7 +307,7 @@ void SEHTableChunk::writeTo(uint8_t *Buf) const { BaserelChunk::BaserelChunk(uint32_t Page, Baserel *Begin, Baserel *End) { // Block header consists of 4 byte page RVA and 4 byte block size. // Each entry is 2 byte. Last entry may be padding. - Data.resize(align((End - Begin) * 2 + 8, 4)); + Data.resize(alignTo((End - Begin) * 2 + 8, 4)); uint8_t *P = Data.data(); write32le(P, Page); write32le(P + 4, Data.size()); diff --git a/COFF/Chunks.h b/COFF/Chunks.h index 274135516eb9..cd0e2e69ef5d 100644 --- a/COFF/Chunks.h +++ b/COFF/Chunks.h @@ -18,6 +18,7 @@ #include "llvm/ADT/iterator_range.h" #include "llvm/Object/COFF.h" #include <atomic> +#include <utility> #include <vector> namespace lld { @@ -138,6 +139,7 @@ public: SectionChunk(ObjectFile *File, const coff_section *Header); static bool classof(const Chunk *C) { return C->kind() == SectionKind; } size_t getSize() const override { return Header->SizeOfRawData; } + ArrayRef<uint8_t> getContents() const; void writeTo(uint8_t *Buf) const override; bool hasData() const override; uint32_t getPermissions() const override; @@ -186,8 +188,6 @@ public: uint32_t Checksum = 0; private: - ArrayRef<uint8_t> getContents() const; - // A file this chunk was created from. ObjectFile *File; @@ -295,7 +295,7 @@ private: // functions. x86-only. class SEHTableChunk : public Chunk { public: - explicit SEHTableChunk(std::set<Defined *> S) : Syms(S) {} + explicit SEHTableChunk(std::set<Defined *> S) : Syms(std::move(S)) {} size_t getSize() const override { return Syms.size() * 4; } void writeTo(uint8_t *Buf) const override; @@ -326,10 +326,6 @@ public: uint8_t Type; }; -inline uint64_t align(uint64_t Value, uint64_t Align) { - return llvm::RoundUpToAlignment(Value, Align); -} - } // namespace coff } // namespace lld diff --git a/COFF/Config.h b/COFF/Config.h index 9cfccadba5fa..a5472e937fa1 100644 --- a/COFF/Config.h +++ b/COFF/Config.h @@ -106,11 +106,15 @@ struct Configuration { // Used for /merge:from=to (e.g. /merge:.rdata=.text) std::map<StringRef, StringRef> Merge; + // Used for /section=.name,{DEKPRSW} to set section attributes. + std::map<StringRef, uint32_t> Section; + // Options for manifest files. ManifestKind Manifest = SideBySide; int ManifestID = 1; StringRef ManifestDependency; bool ManifestUAC = true; + std::vector<std::string> ManifestInput; StringRef ManifestLevel = "'asInvoker'"; StringRef ManifestUIAccess = "'false'"; StringRef ManifestFile; diff --git a/COFF/DLL.cpp b/COFF/DLL.cpp index 8f3383d75c7b..9ac370c11d59 100644 --- a/COFF/DLL.cpp +++ b/COFF/DLL.cpp @@ -45,7 +45,7 @@ public: size_t getSize() const override { // Starts with 2 byte Hint field, followed by a null-terminated string, // ends with 0 or 1 byte padding. - return align(Name.size() + 3, 2); + return alignTo(Name.size() + 3, 2); } void writeTo(uint8_t *Buf) const override { diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp index 4cacf0ff552a..bb6a60e4fc4c 100644 --- a/COFF/Driver.cpp +++ b/COFF/Driver.cpp @@ -14,6 +14,7 @@ #include "SymbolTable.h" #include "Symbols.h" #include "Writer.h" +#include "lld/Driver/Driver.h" #include "llvm/ADT/Optional.h" #include "llvm/LibDriver/LibDriver.h" #include "llvm/Option/Arg.h" @@ -40,27 +41,28 @@ namespace coff { Configuration *Config; LinkerDriver *Driver; -void link(llvm::ArrayRef<const char *> Args) { +bool link(llvm::ArrayRef<const char *> Args) { Configuration C; LinkerDriver D; Config = &C; Driver = &D; - return Driver->link(Args); + Driver->link(Args); + return true; } -// Drop directory components and replace extension with ".exe". +// 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); - return (S.substr(0, S.rfind('.')) + ".exe").str(); + const char* E = Config->DLL ? ".dll" : ".exe"; + return (S.substr(0, S.rfind('.')) + E).str(); } // Opens a file. Path has to be resolved already. // Newly created memory buffers are owned by this driver. MemoryBufferRef LinkerDriver::openFile(StringRef Path) { - auto MBOrErr = MemoryBuffer::getFile(Path); - error(MBOrErr, Twine("Could not open ") + Path); - std::unique_ptr<MemoryBuffer> &MB = *MBOrErr; + std::unique_ptr<MemoryBuffer> MB = + check(MemoryBuffer::getFile(Path), "could not open " + Path); MemoryBufferRef MBRef = MB->getMemBufferRef(); OwningMBs.push_back(std::move(MB)); // take ownership return MBRef; @@ -116,12 +118,16 @@ void LinkerDriver::parseDirectives(StringRef S) { case OPT_nodefaultlib: Config->NoDefaultLibs.insert(doFindLib(Arg->getValue())); break; + case OPT_section: + parseSection(Arg->getValue()); + break; case OPT_editandcontinue: + case OPT_fastfail: case OPT_guardsym: case OPT_throwingnew: break; default: - error(Twine(Arg->getSpelling()) + " is not allowed in .drectve"); + fatal(Arg->getSpelling() + " is not allowed in .drectve"); } } } @@ -246,7 +252,7 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) { // 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) - error("lib failed"); + fatal("lib failed"); return; } @@ -268,7 +274,7 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) { } if (Args.filtered_begin(OPT_INPUT) == Args.filtered_end()) - error("no input files."); + fatal("no input files"); // Construct search path list. SearchPaths.push_back(""); @@ -295,7 +301,7 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) { // Handle /noentry if (Args.hasArg(OPT_noentry)) { if (!Args.hasArg(OPT_dll)) - error("/noentry must be specified with /dll"); + fatal("/noentry must be specified with /dll"); Config->NoEntry = true; } @@ -308,7 +314,7 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) { // Handle /fixed if (Args.hasArg(OPT_fixed)) { if (Args.hasArg(OPT_dynamicbase)) - error("/fixed must not be specified with /dynamicbase"); + fatal("/fixed must not be specified with /dynamicbase"); Config->Relocatable = false; Config->DynamicBase = false; } @@ -382,17 +388,17 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) { StringRef OptLevel = StringRef(S).substr(7); if (OptLevel.getAsInteger(10, Config->LTOOptLevel) || Config->LTOOptLevel > 3) - error("/opt:lldlto: invalid optimization level: " + OptLevel); + fatal("/opt:lldlto: invalid optimization level: " + OptLevel); continue; } if (StringRef(S).startswith("lldltojobs=")) { StringRef Jobs = StringRef(S).substr(11); if (Jobs.getAsInteger(10, Config->LTOJobs) || Config->LTOJobs == 0) - error("/opt:lldltojobs: invalid job count: " + Jobs); + fatal("/opt:lldltojobs: invalid job count: " + Jobs); continue; } if (S != "ref" && S != "lbr" && S != "nolbr") - error(Twine("/opt: unknown option: ") + S); + fatal("/opt: unknown option: " + S); } } @@ -404,6 +410,10 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) { for (auto *Arg : Args.filtered(OPT_merge)) parseMerge(Arg->getValue()); + // Handle /section + for (auto *Arg : Args.filtered(OPT_section)) + parseSection(Arg->getValue()); + // Handle /manifest if (auto *Arg = Args.getLastArg(OPT_manifest_colon)) parseManifest(Arg->getValue()); @@ -420,6 +430,10 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) { 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()); + // Handle miscellaneous boolean flags. if (Args.hasArg(OPT_allowbind_no)) Config->AllowBind = false; @@ -485,7 +499,7 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) { continue; } if (Config->Machine != MT) - error(Twine(File->getShortName()) + ": machine type " + machineToStr(MT) + + fatal(File->getShortName() + ": machine type " + machineToStr(MT) + " conflicts with " + machineToStr(Config->Machine)); } if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) { @@ -520,7 +534,7 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) { // infer that from user-defined entry name. StringRef S = findDefaultEntry(); if (S.empty()) - error("entry point must be defined"); + fatal("entry point must be defined"); Config->Entry = addUndefined(S); if (Config->Verbose) llvm::outs() << "Entry name inferred: " << S << "\n"; @@ -627,14 +641,14 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) { if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) { Config->Subsystem = inferSubsystem(); if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) - error("subsystem must be defined"); + fatal("subsystem must be defined"); } // Handle /safeseh. if (Args.hasArg(OPT_safeseh)) for (ObjectFile *File : Symtab.ObjectFiles) if (!File->SEHCompat) - error("/safeseh: " + File->getName() + " is not compatible with SEH"); + fatal("/safeseh: " + File->getName() + " is not compatible with SEH"); // Windows specific -- when we are creating a .dll file, we also // need to create a .lib file. @@ -668,7 +682,8 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) { if (auto *Arg = Args.getLastArg(OPT_lldmap)) { std::error_code EC; llvm::raw_fd_ostream Out(Arg->getValue(), EC, OpenFlags::F_Text); - error(EC, "Could not create the symbol map"); + if (EC) + fatal(EC, "could not create the symbol map"); Symtab.printMap(Out); } // Call exit to avoid calling destructors. diff --git a/COFF/Driver.h b/COFF/Driver.h index e50da20cbb04..23969ee802fb 100644 --- a/COFF/Driver.h +++ b/COFF/Driver.h @@ -34,9 +34,6 @@ using llvm::COFF::WindowsSubsystem; using llvm::Optional; class InputFile; -// Entry point of the COFF linker. -void link(llvm::ArrayRef<const char *> Args); - // Implemented in MarkLive.cpp. void markLive(const std::vector<Chunk *> &Chunks); @@ -136,6 +133,7 @@ void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major, void parseAlternateName(StringRef); void parseMerge(StringRef); +void parseSection(StringRef); // Parses a string in the form of "EMBED[,=<integer>]|NO". void parseManifest(StringRef Arg); @@ -163,7 +161,6 @@ void checkFailIfMismatch(StringRef Arg); std::unique_ptr<MemoryBuffer> convertResToCOFF(const std::vector<MemoryBufferRef> &MBs); -void touchFile(StringRef Path); void createPDB(StringRef Path); // Create enum with OPT_xxx values for each option in Options.td diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp index 014fee7fefd7..5d7dc2bc65af 100644 --- a/COFF/DriverUtils.cpp +++ b/COFF/DriverUtils.cpp @@ -19,15 +19,12 @@ #include "Symbols.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" -#include "llvm/Object/Archive.h" -#include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/COFF.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/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" @@ -53,7 +50,8 @@ public: void run() { ErrorOr<std::string> ExeOrErr = llvm::sys::findProgramByName(Prog); - error(ExeOrErr, Twine("unable to find ") + Prog + " in PATH: "); + if (auto EC = ExeOrErr.getError()) + fatal(EC, "unable to find " + Prog + " in PATH: "); const char *Exe = Saver.save(*ExeOrErr); Args.insert(Args.begin(), Exe); Args.push_back(nullptr); @@ -61,7 +59,7 @@ public: for (const char *S : Args) if (S) llvm::errs() << S << " "; - error("failed"); + fatal("ExecuteAndWait failed"); } } @@ -85,7 +83,7 @@ MachineTypes getMachineType(StringRef S) { .Default(IMAGE_FILE_MACHINE_UNKNOWN); if (MT != IMAGE_FILE_MACHINE_UNKNOWN) return MT; - error(Twine("unknown /machine argument: ") + S); + fatal("unknown /machine argument: " + S); } StringRef machineToStr(MachineTypes MT) { @@ -106,9 +104,9 @@ void parseNumbers(StringRef Arg, uint64_t *Addr, uint64_t *Size) { StringRef S1, S2; std::tie(S1, S2) = Arg.split(','); if (S1.getAsInteger(0, *Addr)) - error(Twine("invalid number: ") + S1); + fatal("invalid number: " + S1); if (Size && !S2.empty() && S2.getAsInteger(0, *Size)) - error(Twine("invalid number: ") + S2); + fatal("invalid number: " + S2); } // Parses a string in the form of "<integer>[.<integer>]". @@ -117,10 +115,10 @@ void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor) { StringRef S1, S2; std::tie(S1, S2) = Arg.split('.'); if (S1.getAsInteger(0, *Major)) - error(Twine("invalid number: ") + S1); + fatal("invalid number: " + S1); *Minor = 0; if (!S2.empty() && S2.getAsInteger(0, *Minor)) - error(Twine("invalid number: ") + S2); + fatal("invalid number: " + S2); } // Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]". @@ -140,7 +138,7 @@ void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major, .Case("windows", IMAGE_SUBSYSTEM_WINDOWS_GUI) .Default(IMAGE_SUBSYSTEM_UNKNOWN); if (*Sys == IMAGE_SUBSYSTEM_UNKNOWN) - error(Twine("unknown subsystem: ") + SysStr); + fatal("unknown subsystem: " + SysStr); if (!Ver.empty()) parseVersion(Ver, Major, Minor); } @@ -151,10 +149,10 @@ void parseAlternateName(StringRef S) { StringRef From, To; std::tie(From, To) = S.split('='); if (From.empty() || To.empty()) - error(Twine("/alternatename: invalid argument: ") + S); + fatal("/alternatename: invalid argument: " + S); auto It = Config->AlternateNames.find(From); if (It != Config->AlternateNames.end() && It->second != To) - error(Twine("/alternatename: conflicts: ") + S); + fatal("/alternatename: conflicts: " + S); Config->AlternateNames.insert(It, std::make_pair(From, To)); } @@ -164,7 +162,7 @@ void parseMerge(StringRef S) { StringRef From, To; std::tie(From, To) = S.split('='); if (From.empty() || To.empty()) - error(Twine("/merge: invalid argument: ") + S); + fatal("/merge: invalid argument: " + S); auto Pair = Config->Merge.insert(std::make_pair(From, To)); bool Inserted = Pair.second; if (!Inserted) { @@ -175,6 +173,47 @@ void parseMerge(StringRef S) { } } +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 a string in the form of "EMBED[,=<integer>]|NO". // Results are directly written to Config. void parseManifest(StringRef Arg) { @@ -183,16 +222,16 @@ void parseManifest(StringRef Arg) { return; } if (!Arg.startswith_lower("embed")) - error(Twine("Invalid option ") + Arg); + fatal("invalid option " + Arg); Config->Manifest = Configuration::Embed; Arg = Arg.substr(strlen("embed")); if (Arg.empty()) return; if (!Arg.startswith_lower(",id=")) - error(Twine("Invalid option ") + Arg); + fatal("invalid option " + Arg); Arg = Arg.substr(strlen(",id=")); if (Arg.getAsInteger(0, Config->ManifestID)) - error(Twine("Invalid option ") + Arg); + fatal("invalid option " + Arg); } // Parses a string in the form of "level=<string>|uiAccess=<string>|NO". @@ -216,7 +255,7 @@ void parseManifestUAC(StringRef Arg) { std::tie(Config->ManifestUIAccess, Arg) = Arg.split(" "); continue; } - error(Twine("Invalid option ") + Arg); + fatal("invalid option " + Arg); } } @@ -240,10 +279,19 @@ static void quoteAndPrint(raw_ostream &Out, StringRef S) { } } -// Create a manifest file contents. -static std::string createManifestXml() { - std::string S; - llvm::raw_string_ostream OS(S); +// Create the default manifest file as a temporary file. +static std::string createDefaultXml() { + // Create a temporary file. + SmallString<128> Path; + if (auto EC = sys::fs::createTemporaryFile("tmp", "manifest", Path)) + fatal(EC, "cannot create a temporary file"); + + // Open the temporary file for writing. + std::error_code EC; + llvm::raw_fd_ostream OS(Path, EC, sys::fs::F_Text); + if (EC) + fatal(EC, "failed to open " + Path); + // Emit the XML. Note that we do *not* verify that the XML attributes are // syntactically correct. This is intentional for link.exe compatibility. OS << "<?xml version=\"1.0\" standalone=\"yes\"?>\n" @@ -267,21 +315,57 @@ static std::string createManifestXml() { } } OS << "</assembly>\n"; - OS.flush(); - return S; + OS.close(); + return StringRef(Path); +} + +static std::string readFile(StringRef Path) { + std::unique_ptr<MemoryBuffer> MB = + check(MemoryBuffer::getFile(Path), "could not open " + Path); + std::unique_ptr<MemoryBuffer> Buf(std::move(MB)); + return Buf->getBuffer(); +} + +static std::string createManifestXml() { + // Create the default manifest file. + std::string Path1 = createDefaultXml(); + if (Config->ManifestInput.empty()) + return readFile(Path1); + + // If manifest files are supplied by the user using /MANIFESTINPUT + // option, we need to merge them with the default manifest. + SmallString<128> Path2; + if (auto EC = sys::fs::createTemporaryFile("tmp", "manifest", Path2)) + fatal(EC, "cannot create a temporary file"); + FileRemover Remover1(Path1); + FileRemover Remover2(Path2); + + Executor E("mt.exe"); + E.add("/manifest"); + E.add(Path1); + for (StringRef Filename : Config->ManifestInput) { + E.add("/manifest"); + E.add(Filename); + } + E.add("/nologo"); + E.add("/out:" + StringRef(Path2)); + E.run(); + return readFile(Path2); } // Create a resource file containing a manifest XML. std::unique_ptr<MemoryBuffer> createManifestRes() { // Create a temporary file for the resource script file. SmallString<128> RCPath; - std::error_code EC = sys::fs::createTemporaryFile("tmp", "rc", RCPath); - error(EC, "cannot create a temporary file"); + if (auto EC = sys::fs::createTemporaryFile("tmp", "rc", RCPath)) + fatal(EC, "cannot create a temporary file"); FileRemover RCRemover(RCPath); // Open the temporary file for writing. + std::error_code EC; llvm::raw_fd_ostream Out(RCPath, EC, sys::fs::F_Text); - error(EC, Twine("failed to open ") + RCPath); + if (EC) + fatal(EC, "failed to open " + RCPath); // Write resource script to the RC file. Out << "#define LANG_ENGLISH 9\n" @@ -296,8 +380,8 @@ std::unique_ptr<MemoryBuffer> createManifestRes() { // Create output resource file. SmallString<128> ResPath; - EC = sys::fs::createTemporaryFile("tmp", "res", ResPath); - error(EC, "cannot create a temporary file"); + if (auto EC = sys::fs::createTemporaryFile("tmp", "res", ResPath)) + fatal(EC, "cannot create a temporary file"); Executor E("rc.exe"); E.add("/fo"); @@ -305,18 +389,17 @@ std::unique_ptr<MemoryBuffer> createManifestRes() { E.add("/nologo"); E.add(RCPath.str()); E.run(); - ErrorOr<std::unique_ptr<MemoryBuffer>> Ret = MemoryBuffer::getFile(ResPath); - error(Ret, Twine("Could not open ") + ResPath); - return std::move(*Ret); + return check(MemoryBuffer::getFile(ResPath), "could not open " + ResPath); } void createSideBySideManifest() { std::string Path = Config->ManifestFile; if (Path == "") - Path = (Twine(Config->OutputFile) + ".manifest").str(); + Path = Config->OutputFile + ".manifest"; std::error_code EC; llvm::raw_fd_ostream Out(Path, EC, llvm::sys::fs::F_Text); - error(EC, "failed to create manifest"); + if (EC) + fatal(EC, "failed to create manifest"); Out << createManifestXml(); } @@ -380,7 +463,7 @@ Export parseExport(StringRef Arg) { return E; err: - error(Twine("invalid /export: ") + Arg); + fatal("invalid /export: " + Arg); } static StringRef undecorate(StringRef Sym) { @@ -398,7 +481,7 @@ void fixupExports() { if (E.Ordinal == 0) continue; if (!Ords.insert(E.Ordinal).second) - error("duplicate export ordinal: " + E.Name); + fatal("duplicate export ordinal: " + E.Name); } for (Export &E : Config->Exports) { @@ -459,11 +542,11 @@ void checkFailIfMismatch(StringRef Arg) { StringRef K, V; std::tie(K, V) = Arg.split('='); if (K.empty() || V.empty()) - error(Twine("/failifmismatch: invalid argument: ") + Arg); + fatal("/failifmismatch: invalid argument: " + Arg); StringRef Existing = Config->MustMatch[K]; if (!Existing.empty() && V != Existing) - error(Twine("/failifmismatch: mismatch detected: ") + Existing + " and " + - V + " for key " + K); + fatal("/failifmismatch: mismatch detected: " + Existing + " and " + V + + " for key " + K); Config->MustMatch[K] = V; } @@ -473,8 +556,8 @@ std::unique_ptr<MemoryBuffer> convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) { // Create an output file path. SmallString<128> Path; - if (llvm::sys::fs::createTemporaryFile("resource", "obj", Path)) - error("Could not create temporary file"); + if (auto EC = llvm::sys::fs::createTemporaryFile("resource", "obj", Path)) + fatal(EC, "could not create temporary file"); // Execute cvtres.exe. Executor E("cvtres.exe"); @@ -485,170 +568,7 @@ convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) { for (MemoryBufferRef MB : MBs) E.add(MB.getBufferIdentifier()); E.run(); - ErrorOr<std::unique_ptr<MemoryBuffer>> Ret = MemoryBuffer::getFile(Path); - error(Ret, Twine("Could not open ") + Path); - return std::move(*Ret); -} - -static std::string writeToTempFile(StringRef Contents) { - SmallString<128> Path; - int FD; - if (llvm::sys::fs::createTemporaryFile("tmp", "def", FD, Path)) { - llvm::errs() << "failed to create a temporary file\n"; - return ""; - } - llvm::raw_fd_ostream OS(FD, /*shouldClose*/ true); - OS << Contents; - return Path.str(); -} - -void touchFile(StringRef Path) { - int FD; - std::error_code EC = sys::fs::openFileForWrite(Path, FD, sys::fs::F_Append); - error(EC, "failed to create a file"); - sys::Process::SafelyCloseFileDescriptor(FD); -} - -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(); -} - -static std::unique_ptr<MemoryBuffer> createEmptyImportLibrary() { - std::string S = (Twine("LIBRARY \"") + - llvm::sys::path::filename(Config->OutputFile) + "\"\n") - .str(); - std::string Path1 = writeToTempFile(S); - std::string Path2 = getImplibPath(); - llvm::FileRemover Remover1(Path1); - llvm::FileRemover Remover2(Path2); - - Executor E("lib.exe"); - E.add("/nologo"); - E.add("/machine:" + machineToStr(Config->Machine)); - E.add(Twine("/def:") + Path1); - E.add(Twine("/out:") + Path2); - E.run(); - - ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = - MemoryBuffer::getFile(Path2, -1, false); - error(BufOrErr, Twine("Failed to open ") + Path2); - return MemoryBuffer::getMemBufferCopy((*BufOrErr)->getBuffer()); -} - -static std::vector<NewArchiveIterator> -readMembers(const object::Archive &Archive) { - std::vector<NewArchiveIterator> V; - for (const auto &ChildOrErr : Archive.children()) { - error(ChildOrErr, "Archive::Child::getName failed"); - const object::Archive::Child C(*ChildOrErr); - ErrorOr<StringRef> NameOrErr = C.getName(); - error(NameOrErr, "Archive::Child::getName failed"); - V.emplace_back(C, *NameOrErr); - } - return V; -} - -// This class creates short import files which is described in -// PE/COFF spec 7. Import Library Format. -class ShortImportCreator { -public: - ShortImportCreator(object::Archive *A, StringRef S) : Parent(A), DLLName(S) {} - - NewArchiveIterator create(StringRef Sym, uint16_t Ordinal, - ImportNameType NameType, bool isData) { - size_t ImpSize = DLLName.size() + Sym.size() + 2; // +2 for NULs - size_t Size = sizeof(object::ArchiveMemberHeader) + - sizeof(coff_import_header) + ImpSize; - char *Buf = Alloc.Allocate<char>(Size); - memset(Buf, 0, Size); - char *P = Buf; - - // Write archive member header - auto *Hdr = reinterpret_cast<object::ArchiveMemberHeader *>(P); - P += sizeof(*Hdr); - sprintf(Hdr->Name, "%-12s", "dummy"); - sprintf(Hdr->LastModified, "%-12d", 0); - sprintf(Hdr->UID, "%-6d", 0); - sprintf(Hdr->GID, "%-6d", 0); - sprintf(Hdr->AccessMode, "%-8d", 0644); - sprintf(Hdr->Size, "%-10d", int(sizeof(coff_import_header) + ImpSize)); - - // Write short import library. - auto *Imp = reinterpret_cast<coff_import_header *>(P); - P += sizeof(*Imp); - Imp->Sig2 = 0xFFFF; - Imp->Machine = Config->Machine; - Imp->SizeOfData = ImpSize; - if (Ordinal > 0) - Imp->OrdinalHint = Ordinal; - Imp->TypeInfo = (isData ? IMPORT_DATA : IMPORT_CODE); - Imp->TypeInfo |= NameType << 2; - - // Write symbol name and DLL name. - memcpy(P, Sym.data(), Sym.size()); - P += Sym.size() + 1; - memcpy(P, DLLName.data(), DLLName.size()); - - std::error_code EC; - object::Archive::Child C(Parent, Buf, &EC); - assert(!EC && "We created an invalid buffer"); - return NewArchiveIterator(C, DLLName); - } - -private: - BumpPtrAllocator Alloc; - object::Archive *Parent; - StringRef DLLName; -}; - -static ImportNameType getNameType(StringRef Sym, StringRef ExtName) { - if (Sym != ExtName) - return IMPORT_NAME_UNDECORATE; - if (Config->Machine == I386 && Sym.startswith("_")) - return IMPORT_NAME_NOPREFIX; - return IMPORT_NAME; -} - -static std::string replace(StringRef S, StringRef From, StringRef To) { - size_t Pos = S.find(From); - assert(Pos != StringRef::npos); - return (Twine(S.substr(0, Pos)) + To + S.substr(Pos + From.size())).str(); -} - -// Creates an import library for a DLL. In this function, we first -// create an empty import library using lib.exe and then adds short -// import files to that file. -void writeImportLibrary() { - std::unique_ptr<MemoryBuffer> Buf = createEmptyImportLibrary(); - std::error_code EC; - object::Archive Archive(Buf->getMemBufferRef(), EC); - error(EC, "Error reading an empty import file"); - std::vector<NewArchiveIterator> Members = readMembers(Archive); - - std::string DLLName = llvm::sys::path::filename(Config->OutputFile); - ShortImportCreator ShortImport(&Archive, DLLName); - for (Export &E : Config->Exports) { - if (E.Private) - continue; - if (E.ExtName.empty()) { - Members.push_back(ShortImport.create( - E.SymbolName, E.Ordinal, getNameType(E.SymbolName, E.Name), E.Data)); - } else { - Members.push_back(ShortImport.create( - replace(E.SymbolName, E.Name, E.ExtName), E.Ordinal, - getNameType(E.SymbolName, E.Name), E.Data)); - } - } - - std::string Path = getImplibPath(); - std::pair<StringRef, std::error_code> Result = - writeArchive(Path, Members, /*WriteSymtab*/ true, object::Archive::K_GNU, - /*Deterministic*/ true, /*Thin*/ false); - error(Result.second, Twine("Failed to write ") + Path); + return check(MemoryBuffer::getFile(Path), "could not open " + Path); } // Create OptTable @@ -695,7 +615,7 @@ llvm::opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) { } if (MissingCount) - error(Twine("missing arg value for \"") + Args.getArgString(MissingIndex) + + fatal("missing arg value for \"" + Twine(Args.getArgString(MissingIndex)) + "\", expected " + Twine(MissingCount) + (MissingCount == 1 ? " argument." : " arguments.")); for (auto *Arg : Args.filtered(OPT_UNKNOWN)) diff --git a/COFF/Error.cpp b/COFF/Error.cpp index 255d9bbad9d8..602a8544ce2b 100644 --- a/COFF/Error.cpp +++ b/COFF/Error.cpp @@ -10,20 +10,23 @@ #include "Error.h" #include "llvm/ADT/Twine.h" +#include "llvm/Support/Error.h" #include "llvm/Support/raw_ostream.h" namespace lld { namespace coff { -void error(const Twine &Msg) { +void fatal(const Twine &Msg) { llvm::errs() << Msg << "\n"; exit(1); } -void error(std::error_code EC, const Twine &Prefix) { - if (!EC) - return; - error(Prefix + ": " + EC.message()); +void fatal(std::error_code EC, const Twine &Msg) { + fatal(Msg + ": " + EC.message()); +} + +void fatal(llvm::Error &Err, const Twine &Msg) { + fatal(errorToErrorCode(std::move(Err)), Msg); } } // namespace coff diff --git a/COFF/Error.h b/COFF/Error.h index cb0a185f0917..c9f64c662580 100644 --- a/COFF/Error.h +++ b/COFF/Error.h @@ -11,15 +11,25 @@ #define LLD_COFF_ERROR_H #include "lld/Core/LLVM.h" +#include "llvm/Support/Error.h" namespace lld { namespace coff { -LLVM_ATTRIBUTE_NORETURN void error(const Twine &Msg); -void error(std::error_code EC, const Twine &Prefix); +LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg); +LLVM_ATTRIBUTE_NORETURN void fatal(std::error_code EC, const Twine &Prefix); +LLVM_ATTRIBUTE_NORETURN void fatal(llvm::Error &Err, const Twine &Prefix); -template <typename T> void error(const ErrorOr<T> &V, const Twine &Prefix) { - error(V.getError(), Prefix); +template <class T> T check(ErrorOr<T> &&V, const Twine &Prefix) { + if (auto EC = V.getError()) + fatal(EC, Prefix); + return std::move(*V); +} + +template <class T> T check(Expected<T> E, const Twine &Prefix) { + if (llvm::Error Err = E.takeError()) + fatal(Err, Prefix); + return std::move(*E); } } // namespace coff diff --git a/COFF/ICF.cpp b/COFF/ICF.cpp index f99b41624a84..a2c5a90334d0 100644 --- a/COFF/ICF.cpp +++ b/COFF/ICF.cpp @@ -70,7 +70,7 @@ private: static bool equalsConstant(const SectionChunk *A, const SectionChunk *B); static bool equalsVariable(const SectionChunk *A, const SectionChunk *B); bool forEachGroup(std::vector<SectionChunk *> &Chunks, Comparator Eq); - bool partition(ChunkIterator Begin, ChunkIterator End, Comparator Eq); + bool segregate(ChunkIterator Begin, ChunkIterator End, Comparator Eq); std::atomic<uint64_t> NextID = { 1 }; }; @@ -148,7 +148,7 @@ bool ICF::equalsVariable(const SectionChunk *A, const SectionChunk *B) { return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq); } -bool ICF::partition(ChunkIterator Begin, ChunkIterator End, Comparator Eq) { +bool ICF::segregate(ChunkIterator Begin, ChunkIterator End, Comparator Eq) { bool R = false; for (auto It = Begin;;) { SectionChunk *Head = *It; @@ -171,7 +171,7 @@ bool ICF::forEachGroup(std::vector<SectionChunk *> &Chunks, Comparator Eq) { auto Bound = std::find_if(It + 1, End, [&](SectionChunk *SC) { return SC->GroupID != Head->GroupID; }); - if (partition(It, Bound, Eq)) + if (segregate(It, Bound, Eq)) R = true; It = Bound; } diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp index 23af1e89c34d..ff26826371fa 100644 --- a/COFF/InputFiles.cpp +++ b/COFF/InputFiles.cpp @@ -8,30 +8,41 @@ //===----------------------------------------------------------------------===// #include "Chunks.h" +#include "Config.h" #include "Error.h" #include "InputFiles.h" #include "Symbols.h" -#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Triple.h" +#include "llvm/ADT/Twine.h" #include "llvm/IR/LLVMContext.h" -#include "llvm/LTO/LTOModule.h" +#include "llvm/LTO/legacy/LTOModule.h" +#include "llvm/Object/Binary.h" #include "llvm/Object/COFF.h" #include "llvm/Support/COFF.h" -#include "llvm/Support/Debug.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/Endian.h" -#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Target/TargetOptions.h" +#include "llvm-c/lto.h" +#include <cstring> +#include <system_error> +#include <utility> using namespace llvm::COFF; using namespace llvm::object; using namespace llvm::support::endian; + using llvm::Triple; using llvm::support::ulittle32_t; -using llvm::sys::fs::file_magic; -using llvm::sys::fs::identify_magic; namespace lld { namespace coff { int InputFile::NextIndex = 0; +llvm::LLVMContext BitcodeFile::Context; // Returns the last element of a path, which is supposed to be a filename. static StringRef getBasename(StringRef Path) { @@ -52,9 +63,7 @@ std::string InputFile::getShortName() { void ArchiveFile::parse() { // Parse a MemoryBufferRef as an archive file. - auto ArchiveOrErr = Archive::create(MB); - error(ArchiveOrErr, "Failed to parse static library"); - File = std::move(*ArchiveOrErr); + File = check(Archive::create(MB), "failed to parse static library"); // Allocate a buffer for Lazy objects. size_t NumSyms = File->getNumberOfSymbols(); @@ -67,40 +76,38 @@ void ArchiveFile::parse() { // Seen is a map from member files to boolean values. Initially // all members are mapped to false, which indicates all these files // are not read yet. - for (auto &ChildOrErr : File->children()) { - error(ChildOrErr, "Failed to parse static library"); - const Archive::Child &Child = *ChildOrErr; + Error Err; + for (auto &Child : File->children(Err)) Seen[Child.getChildOffset()].clear(); - } + if (Err) + fatal(Err, "failed to parse static library"); } // Returns a buffer pointing to a member file containing a given symbol. // This function is thread-safe. MemoryBufferRef ArchiveFile::getMember(const Archive::Symbol *Sym) { - auto COrErr = Sym->getMember(); - error(COrErr, Twine("Could not get the member for symbol ") + Sym->getName()); - const Archive::Child &C = *COrErr; + const Archive::Child &C = + check(Sym->getMember(), + "could not get the member for symbol " + Sym->getName()); // Return an empty buffer if we have already returned the same buffer. if (Seen[C.getChildOffset()].test_and_set()) return MemoryBufferRef(); - ErrorOr<MemoryBufferRef> Ret = C.getMemoryBufferRef(); - error(Ret, Twine("Could not get the buffer for the member defining symbol ") + - Sym->getName()); - return *Ret; + return check(C.getMemoryBufferRef(), + "could not get the buffer for the member defining symbol " + + Sym->getName()); } void ObjectFile::parse() { // Parse a memory buffer as a COFF file. - auto BinOrErr = createBinary(MB); - error(BinOrErr, "Failed to parse object file"); - std::unique_ptr<Binary> Bin = std::move(*BinOrErr); + std::unique_ptr<Binary> Bin = + check(createBinary(MB), "failed to parse object file"); if (auto *Obj = dyn_cast<COFFObjectFile>(Bin.get())) { Bin.release(); COFFObj.reset(Obj); } else { - error(Twine(getName()) + " is not a COFF file."); + fatal(getName() + " is not a COFF file"); } // Read section and symbol tables. @@ -116,10 +123,10 @@ void ObjectFile::initializeChunks() { for (uint32_t I = 1; I < NumSections + 1; ++I) { const coff_section *Sec; StringRef Name; - std::error_code EC = COFFObj->getSection(I, Sec); - error(EC, Twine("getSection failed: #") + Twine(I)); - EC = COFFObj->getSectionName(Sec, Name); - error(EC, Twine("getSectionName failed: #") + Twine(I)); + if (auto EC = COFFObj->getSection(I, Sec)) + fatal(EC, "getSection failed: #" + Twine(I)); + if (auto EC = COFFObj->getSectionName(Sec, Name)) + fatal(EC, "getSectionName failed: #" + Twine(I)); if (Name == ".sxdata") { SXData = Sec; continue; @@ -149,14 +156,12 @@ void ObjectFile::initializeSymbols() { uint32_t NumSymbols = COFFObj->getNumberOfSymbols(); SymbolBodies.reserve(NumSymbols); SparseSymbolBodies.resize(NumSymbols); - llvm::SmallVector<Undefined *, 8> WeakAliases; + llvm::SmallVector<std::pair<Undefined *, uint32_t>, 8> WeakAliases; int32_t LastSectionNumber = 0; for (uint32_t I = 0; I < NumSymbols; ++I) { // Get a COFFSymbolRef object. - auto SymOrErr = COFFObj->getSymbol(I); - error(SymOrErr, Twine("broken object file: ") + getName()); - - COFFSymbolRef Sym = *SymOrErr; + COFFSymbolRef Sym = + check(COFFObj->getSymbol(I), "broken object file: " + getName()); const void *AuxP = nullptr; if (Sym.getNumberOfAuxSymbols()) @@ -167,8 +172,10 @@ void ObjectFile::initializeSymbols() { if (Sym.isUndefined()) { Body = createUndefined(Sym); } else if (Sym.isWeakExternal()) { - Body = createWeakExternal(Sym, AuxP); - WeakAliases.push_back((Undefined *)Body); + Body = createUndefined(Sym); + uint32_t TagIndex = + static_cast<const coff_aux_weak_external *>(AuxP)->TagIndex; + WeakAliases.emplace_back((Undefined *)Body, TagIndex); } else { Body = createDefined(Sym, AuxP, IsFirst); } @@ -179,8 +186,8 @@ void ObjectFile::initializeSymbols() { I += Sym.getNumberOfAuxSymbols(); LastSectionNumber = Sym.getSectionNumber(); } - for (Undefined *U : WeakAliases) - U->WeakAlias = SparseSymbolBodies[(uintptr_t)U->WeakAlias]; + for (auto WeakAlias : WeakAliases) + WeakAlias.first->WeakAlias = SparseSymbolBodies[WeakAlias.second]; } Undefined *ObjectFile::createUndefined(COFFSymbolRef Sym) { @@ -189,15 +196,6 @@ Undefined *ObjectFile::createUndefined(COFFSymbolRef Sym) { return new (Alloc) Undefined(Name); } -Undefined *ObjectFile::createWeakExternal(COFFSymbolRef Sym, const void *AuxP) { - StringRef Name; - COFFObj->getSymbolName(Sym, Name); - auto *U = new (Alloc) Undefined(Name); - auto *Aux = (const coff_aux_weak_external *)AuxP; - U->WeakAlias = (Undefined *)(uintptr_t)Aux->TagIndex; - return U; -} - Defined *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP, bool IsFirst) { StringRef Name; @@ -219,11 +217,21 @@ Defined *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP, } return new (Alloc) DefinedAbsolute(Name, Sym); } - if (Sym.getSectionNumber() == llvm::COFF::IMAGE_SYM_DEBUG) + int32_t SectionNumber = Sym.getSectionNumber(); + if (SectionNumber == llvm::COFF::IMAGE_SYM_DEBUG) return nullptr; + // Reserved sections numbers don't have contents. + if (llvm::COFF::isReservedSectionNumber(SectionNumber)) + fatal("broken object file: " + getName()); + + // This symbol references a section which is not present in the section + // header. + if ((uint32_t)SectionNumber >= SparseChunks.size()) + fatal("broken object file: " + getName()); + // Nothing else to do without a section chunk. - auto *SC = cast_or_null<SectionChunk>(SparseChunks[Sym.getSectionNumber()]); + auto *SC = cast_or_null<SectionChunk>(SparseChunks[SectionNumber]); if (!SC) return nullptr; @@ -250,7 +258,7 @@ void ObjectFile::initializeSEH() { ArrayRef<uint8_t> A; COFFObj->getSectionContents(SXData, A); if (A.size() % 4 != 0) - error(".sxdata must be an array of symbol table indices"); + fatal(".sxdata must be an array of symbol table indices"); auto *I = reinterpret_cast<const ulittle32_t *>(A.data()); auto *E = reinterpret_cast<const ulittle32_t *>(A.data() + A.size()); for (; I != E; ++I) @@ -276,11 +284,11 @@ void ImportFile::parse() { // Check if the total size is valid. if ((size_t)(End - Buf) != (sizeof(*Hdr) + Hdr->SizeOfData)) - error("broken import library"); + fatal("broken import library"); // Read names and create an __imp_ symbol. StringRef Name = StringAlloc.save(StringRef(Buf + sizeof(*Hdr))); - StringRef ImpName = StringAlloc.save(Twine("__imp_") + Name); + StringRef ImpName = StringAlloc.save("__imp_" + Name); const char *NameStart = Buf + sizeof(coff_import_header) + Name.size() + 1; DLLName = StringRef(NameStart); StringRef ExtName; @@ -315,11 +323,10 @@ void BitcodeFile::parse() { // Usually parse() is thread-safe, but bitcode file is an exception. std::lock_guard<std::mutex> Lock(Mu); - ErrorOr<std::unique_ptr<LTOModule>> ModOrErr = - LTOModule::createFromBuffer(llvm::getGlobalContext(), MB.getBufferStart(), - MB.getBufferSize(), llvm::TargetOptions()); - error(ModOrErr, "Could not create lto module"); - M = std::move(*ModOrErr); + Context.enableDebugTypeODRUniquing(); + ErrorOr<std::unique_ptr<LTOModule>> ModOrErr = LTOModule::createFromBuffer( + Context, MB.getBufferStart(), MB.getBufferSize(), llvm::TargetOptions()); + M = check(std::move(ModOrErr), "could not create LTO module"); llvm::StringSaver Saver(Alloc); for (unsigned I = 0, E = M->getSymbolCount(); I != E; ++I) { diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h index 6a263fbaddf6..0ec01b5075f9 100644 --- a/COFF/InputFiles.h +++ b/COFF/InputFiles.h @@ -12,7 +12,8 @@ #include "lld/Core/LLVM.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/LTO/LTOModule.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/LTO/legacy/LTOModule.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" #include "llvm/Support/StringSaver.h" @@ -103,7 +104,7 @@ public: // All symbols returned by ArchiveFiles are of Lazy type. std::vector<SymbolBody *> &getSymbols() override { - llvm_unreachable("internal error"); + llvm_unreachable("internal fatal"); } private: @@ -147,7 +148,6 @@ private: Defined *createDefined(COFFSymbolRef Sym, const void *Aux, bool IsFirst); Undefined *createUndefined(COFFSymbolRef Sym); - Undefined *createWeakExternal(COFFSymbolRef Sym, const void *Aux); std::unique_ptr<COFFObjectFile> COFFObj; llvm::BumpPtrAllocator Alloc; @@ -204,9 +204,10 @@ public: static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; } std::vector<SymbolBody *> &getSymbols() override { return SymbolBodies; } MachineTypes getMachineType() override; - std::unique_ptr<LTOModule> takeModule() { return std::move(M); } + static llvm::LLVMContext Context; + private: void parse() override; diff --git a/COFF/Librarian.cpp b/COFF/Librarian.cpp new file mode 100644 index 000000000000..25fb4a87b3eb --- /dev/null +++ b/COFF/Librarian.cpp @@ -0,0 +1,489 @@ +//===- Librarian.cpp ------------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains functions for the Librarian. The librarian creates and +// manages libraries of the Common Object File Format (COFF) object files. It +// primarily is used for creating static libraries and import libraries. +// +//===----------------------------------------------------------------------===// + +#include "Config.h" +#include "Driver.h" +#include "Error.h" +#include "Symbols.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/ArchiveWriter.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Path.h" + +#include <vector> + +using namespace lld::coff; +using namespace llvm::COFF; +using namespace llvm::object; +using namespace llvm; + +static bool is32bit() { + switch (Config->Machine) { + default: + llvm_unreachable("unsupported machine"); + case IMAGE_FILE_MACHINE_AMD64: + return false; + case IMAGE_FILE_MACHINE_ARMNT: + case IMAGE_FILE_MACHINE_I386: + return true; + } +} + +static uint16_t getImgRelRelocation() { + switch (Config->Machine) { + default: + llvm_unreachable("unsupported machine"); + case IMAGE_FILE_MACHINE_AMD64: + return IMAGE_REL_AMD64_ADDR32NB; + case IMAGE_FILE_MACHINE_ARMNT: + return IMAGE_REL_ARM_ADDR32NB; + case IMAGE_FILE_MACHINE_I386: + return IMAGE_REL_I386_DIR32NB; + } +} + +template <class T> void append(std::vector<uint8_t> &B, const T &Data) { + size_t S = B.size(); + B.resize(S + sizeof(T)); + memcpy(&B[S], &Data, sizeof(T)); +} + +static void writeStringTable(std::vector<uint8_t> &B, + ArrayRef<const std::string> Strings) { + // The COFF string table consists of a 4-byte value which is the size of the + // table, including the length field itself. This value is followed by the + // string content itself, which is an array of null-terminated C-style + // strings. The termination is important as they are referenced to by offset + // by the symbol entity in the file format. + + std::vector<uint8_t>::size_type Pos = B.size(); + std::vector<uint8_t>::size_type Offset = B.size(); + + // Skip over the length field, we will fill it in later as we will have + // computed the length while emitting the string content itself. + Pos += sizeof(uint32_t); + + for (const auto &S : Strings) { + B.resize(Pos + S.length() + 1); + strcpy(reinterpret_cast<char *>(&B[Pos]), S.c_str()); + Pos += S.length() + 1; + } + + // Backfill the length of the table now that it has been computed. + support::ulittle32_t Length(B.size() - Offset); + memcpy(&B[Offset], &Length, sizeof(Length)); +} + +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(); +} + +static ImportNameType getNameType(StringRef Sym, StringRef ExtName) { + if (Sym != ExtName) + return IMPORT_NAME_UNDECORATE; + if (Config->Machine == I386 && Sym.startswith("_")) + return IMPORT_NAME_NOPREFIX; + return IMPORT_NAME; +} + +static std::string replace(StringRef S, StringRef From, StringRef To) { + size_t Pos = S.find(From); + assert(Pos != StringRef::npos); + return (Twine(S.substr(0, Pos)) + To + S.substr(Pos + From.size())).str(); +} + +static const std::string NullImportDescriptorSymbolName = + "__NULL_IMPORT_DESCRIPTOR"; + +namespace { +// This class constructs various small object files necessary to support linking +// symbols imported from a DLL. The contents are pretty strictly defined and +// nearly entirely static. The details of the structures files are defined in +// WINNT.h and the PE/COFF specification. +class ObjectFactory { + using u16 = support::ulittle16_t; + using u32 = support::ulittle32_t; + + BumpPtrAllocator Alloc; + StringRef DLLName; + StringRef Library; + std::string ImportDescriptorSymbolName; + std::string NullThunkSymbolName; + +public: + ObjectFactory(StringRef S) + : DLLName(S), Library(S.drop_back(4)), + ImportDescriptorSymbolName(("__IMPORT_DESCRIPTOR_" + Library).str()), + NullThunkSymbolName(("\x7f" + Library + "_NULL_THUNK_DATA").str()) {} + + // Creates an Import Descriptor. This is a small object file which contains a + // reference to the terminators and contains the library name (entry) for the + // import name table. It will force the linker to construct the necessary + // structure to import symbols from the DLL. + NewArchiveMember createImportDescriptor(std::vector<uint8_t> &Buffer); + + // Creates a NULL import descriptor. This is a small object file whcih + // contains a NULL import descriptor. It is used to terminate the imports + // from a specific DLL. + NewArchiveMember createNullImportDescriptor(std::vector<uint8_t> &Buffer); + + // Create a NULL Thunk Entry. This is a small object file which contains a + // NULL Import Address Table entry and a NULL Import Lookup Table Entry. It + // is used to terminate the IAT and ILT. + NewArchiveMember createNullThunk(std::vector<uint8_t> &Buffer); + + // Create a short import file which is described in PE/COFF spec 7. Import + // Library Format. + NewArchiveMember createShortImport(StringRef Sym, uint16_t Ordinal, + ImportNameType NameType, bool isData); +}; +} + +NewArchiveMember +ObjectFactory::createImportDescriptor(std::vector<uint8_t> &Buffer) { + static const uint32_t NumberOfSections = 2; + static const uint32_t NumberOfSymbols = 7; + static const uint32_t NumberOfRelocations = 3; + + // COFF Header + coff_file_header Header{ + u16(Config->Machine), u16(NumberOfSections), u32(0), + u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + + // .idata$2 + sizeof(coff_import_directory_table_entry) + + NumberOfRelocations * sizeof(coff_relocation) + + // .idata$4 + (DLLName.size() + 1)), + u32(NumberOfSymbols), u16(0), + u16(is32bit() ? IMAGE_FILE_32BIT_MACHINE : 0), + }; + append(Buffer, Header); + + // Section Header Table + static const coff_section SectionTable[NumberOfSections] = { + {{'.', 'i', 'd', 'a', 't', 'a', '$', '2'}, + u32(0), + u32(0), + u32(sizeof(coff_import_directory_table_entry)), + u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)), + u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + + sizeof(coff_import_directory_table_entry)), + u32(0), + u16(NumberOfRelocations), + u16(0), + u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, + {{'.', 'i', 'd', 'a', 't', 'a', '$', '6'}, + u32(0), + u32(0), + u32(DLLName.size() + 1), + u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + + sizeof(coff_import_directory_table_entry) + + NumberOfRelocations * sizeof(coff_relocation)), + u32(0), + u32(0), + u16(0), + u16(0), + u32(IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, + }; + append(Buffer, SectionTable); + + // .idata$2 + static const coff_import_directory_table_entry ImportDescriptor{ + u32(0), u32(0), u32(0), u32(0), u32(0), + }; + append(Buffer, ImportDescriptor); + + static const coff_relocation RelocationTable[NumberOfRelocations] = { + {u32(offsetof(coff_import_directory_table_entry, NameRVA)), u32(2), + u16(getImgRelRelocation())}, + {u32(offsetof(coff_import_directory_table_entry, ImportLookupTableRVA)), + u32(3), u16(getImgRelRelocation())}, + {u32(offsetof(coff_import_directory_table_entry, ImportAddressTableRVA)), + u32(4), u16(getImgRelRelocation())}, + }; + append(Buffer, RelocationTable); + + // .idata$6 + auto S = Buffer.size(); + Buffer.resize(S + DLLName.size() + 1); + memcpy(&Buffer[S], DLLName.data(), DLLName.size()); + Buffer[S + DLLName.size()] = '\0'; + + // Symbol Table + coff_symbol16 SymbolTable[NumberOfSymbols] = { + {{{0, 0, 0, 0, 0, 0, 0, 0}}, + u32(0), + u16(1), + u16(0), + IMAGE_SYM_CLASS_EXTERNAL, + 0}, + {{{'.', 'i', 'd', 'a', 't', 'a', '$', '2'}}, + u32(0), + u16(1), + u16(0), + IMAGE_SYM_CLASS_SECTION, + 0}, + {{{'.', 'i', 'd', 'a', 't', 'a', '$', '6'}}, + u32(0), + u16(2), + u16(0), + IMAGE_SYM_CLASS_STATIC, + 0}, + {{{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}}, + u32(0), + u16(0), + u16(0), + IMAGE_SYM_CLASS_SECTION, + 0}, + {{{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}}, + u32(0), + u16(0), + u16(0), + IMAGE_SYM_CLASS_SECTION, + 0}, + {{{0, 0, 0, 0, 0, 0, 0, 0}}, + u32(0), + u16(0), + u16(0), + IMAGE_SYM_CLASS_EXTERNAL, + 0}, + {{{0, 0, 0, 0, 0, 0, 0, 0}}, + u32(0), + u16(0), + u16(0), + IMAGE_SYM_CLASS_EXTERNAL, + 0}, + }; + reinterpret_cast<StringTableOffset &>(SymbolTable[0].Name).Offset = + sizeof(uint32_t); + reinterpret_cast<StringTableOffset &>(SymbolTable[5].Name).Offset = + sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1; + reinterpret_cast<StringTableOffset &>(SymbolTable[6].Name).Offset = + sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1 + + NullImportDescriptorSymbolName.length() + 1; + append(Buffer, SymbolTable); + + // String Table + writeStringTable(Buffer, + {ImportDescriptorSymbolName, NullImportDescriptorSymbolName, + NullThunkSymbolName}); + + StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; + return {MemoryBufferRef(F, DLLName)}; +} + +NewArchiveMember +ObjectFactory::createNullImportDescriptor(std::vector<uint8_t> &Buffer) { + static const uint32_t NumberOfSections = 1; + static const uint32_t NumberOfSymbols = 1; + + // COFF Header + coff_file_header Header{ + u16(Config->Machine), u16(NumberOfSections), u32(0), + u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + + // .idata$3 + sizeof(coff_import_directory_table_entry)), + u32(NumberOfSymbols), u16(0), + u16(is32bit() ? IMAGE_FILE_32BIT_MACHINE : 0), + }; + append(Buffer, Header); + + // Section Header Table + static const coff_section SectionTable[NumberOfSections] = { + {{'.', 'i', 'd', 'a', 't', 'a', '$', '3'}, + u32(0), + u32(0), + u32(sizeof(coff_import_directory_table_entry)), + u32(sizeof(coff_file_header) + + (NumberOfSections * sizeof(coff_section))), + u32(0), + u32(0), + u16(0), + u16(0), + u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, + }; + append(Buffer, SectionTable); + + // .idata$3 + static const coff_import_directory_table_entry ImportDescriptor{ + u32(0), u32(0), u32(0), u32(0), u32(0), + }; + append(Buffer, ImportDescriptor); + + // Symbol Table + coff_symbol16 SymbolTable[NumberOfSymbols] = { + {{{0, 0, 0, 0, 0, 0, 0, 0}}, + u32(0), + u16(1), + u16(0), + IMAGE_SYM_CLASS_EXTERNAL, + 0}, + }; + reinterpret_cast<StringTableOffset &>(SymbolTable[0].Name).Offset = + sizeof(uint32_t); + append(Buffer, SymbolTable); + + // String Table + writeStringTable(Buffer, {NullImportDescriptorSymbolName}); + + StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; + return {MemoryBufferRef(F, DLLName)}; +} + +NewArchiveMember ObjectFactory::createNullThunk(std::vector<uint8_t> &Buffer) { + static const uint32_t NumberOfSections = 2; + static const uint32_t NumberOfSymbols = 1; + + // COFF Header + coff_file_header Header{ + u16(Config->Machine), u16(NumberOfSections), u32(0), + u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + + // .idata$5 + sizeof(export_address_table_entry) + + // .idata$4 + sizeof(export_address_table_entry)), + u32(NumberOfSymbols), u16(0), + u16(is32bit() ? IMAGE_FILE_32BIT_MACHINE : 0), + }; + append(Buffer, Header); + + // Section Header Table + static const coff_section SectionTable[NumberOfSections] = { + {{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}, + u32(0), + u32(0), + u32(sizeof(export_address_table_entry)), + u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)), + u32(0), + u32(0), + u16(0), + u16(0), + u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, + {{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}, + u32(0), + u32(0), + u32(sizeof(export_address_table_entry)), + u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + + sizeof(export_address_table_entry)), + u32(0), + u32(0), + u16(0), + u16(0), + u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, + }; + append(Buffer, SectionTable); + + // .idata$5 + static const export_address_table_entry ILT{u32(0)}; + append(Buffer, ILT); + + // .idata$4 + static const export_address_table_entry IAT{u32(0)}; + append(Buffer, IAT); + + // Symbol Table + coff_symbol16 SymbolTable[NumberOfSymbols] = { + {{{0, 0, 0, 0, 0, 0, 0, 0}}, + u32(0), + u16(1), + u16(0), + IMAGE_SYM_CLASS_EXTERNAL, + 0}, + }; + reinterpret_cast<StringTableOffset &>(SymbolTable[0].Name).Offset = + sizeof(uint32_t); + append(Buffer, SymbolTable); + + // String Table + writeStringTable(Buffer, {NullThunkSymbolName}); + + StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; + return {MemoryBufferRef{F, DLLName}}; +} + +NewArchiveMember ObjectFactory::createShortImport(StringRef Sym, + uint16_t Ordinal, + ImportNameType NameType, + bool isData) { + size_t ImpSize = DLLName.size() + Sym.size() + 2; // +2 for NULs + size_t Size = sizeof(coff_import_header) + ImpSize; + char *Buf = Alloc.Allocate<char>(Size); + memset(Buf, 0, Size); + char *P = Buf; + + // Write short import library. + auto *Imp = reinterpret_cast<coff_import_header *>(P); + P += sizeof(*Imp); + Imp->Sig2 = 0xFFFF; + Imp->Machine = Config->Machine; + Imp->SizeOfData = ImpSize; + if (Ordinal > 0) + Imp->OrdinalHint = Ordinal; + Imp->TypeInfo = (isData ? IMPORT_DATA : IMPORT_CODE); + Imp->TypeInfo |= NameType << 2; + + // Write symbol name and DLL name. + memcpy(P, Sym.data(), Sym.size()); + P += Sym.size() + 1; + memcpy(P, DLLName.data(), DLLName.size()); + + return {MemoryBufferRef(StringRef(Buf, Size), DLLName)}; +} + +// Creates an import library for a DLL. In this function, we first +// create an empty import library using lib.exe and then adds short +// import files to that file. +void lld::coff::writeImportLibrary() { + std::vector<NewArchiveMember> Members; + + std::string Path = getImplibPath(); + std::string DLLName = llvm::sys::path::filename(Config->OutputFile); + ObjectFactory OF(DLLName); + + std::vector<uint8_t> ImportDescriptor; + Members.push_back(OF.createImportDescriptor(ImportDescriptor)); + + std::vector<uint8_t> NullImportDescriptor; + Members.push_back(OF.createNullImportDescriptor(NullImportDescriptor)); + + std::vector<uint8_t> NullThunk; + Members.push_back(OF.createNullThunk(NullThunk)); + + for (Export &E : Config->Exports) { + if (E.Private) + continue; + + ImportNameType Type = getNameType(E.SymbolName, E.Name); + std::string Name = E.ExtName.empty() + ? std::string(E.SymbolName) + : replace(E.SymbolName, E.Name, E.ExtName); + Members.push_back(OF.createShortImport(Name, E.Ordinal, Type, E.Data)); + } + + std::pair<StringRef, std::error_code> Result = + writeArchive(Path, Members, /*WriteSymtab*/ true, object::Archive::K_GNU, + /*Deterministic*/ true, /*Thin*/ false); + if (auto EC = Result.second) + fatal(EC, "failed to write " + Path); +} diff --git a/COFF/ModuleDef.cpp b/COFF/ModuleDef.cpp index d117e961f89a..5e393f45d184 100644 --- a/COFF/ModuleDef.cpp +++ b/COFF/ModuleDef.cpp @@ -134,13 +134,13 @@ private: void readAsInt(uint64_t *I) { read(); if (Tok.K != Identifier || Tok.Value.getAsInteger(10, *I)) - error("integer expected"); + fatal("integer expected"); } void expect(Kind Expected, StringRef Msg) { read(); if (Tok.K != Expected) - error(Msg); + fatal(Msg); } void unget() { Stack.push_back(Tok); } @@ -177,7 +177,7 @@ private: parseVersion(&Config->MajorImageVersion, &Config->MinorImageVersion); return; default: - error(Twine("unknown directive: ") + Tok.Value); + fatal("unknown directive: " + Tok.Value); } } @@ -188,7 +188,7 @@ private: if (Tok.K == Equal) { read(); if (Tok.K != Identifier) - error(Twine("identifier expected, but got ") + Tok.Value); + fatal("identifier expected, but got " + Tok.Value); E.ExtName = E.Name; E.Name = Tok.Value; } else { @@ -264,15 +264,15 @@ private: void parseVersion(uint32_t *Major, uint32_t *Minor) { read(); if (Tok.K != Identifier) - error(Twine("identifier expected, but got ") + Tok.Value); + fatal("identifier expected, but got " + Tok.Value); StringRef V1, V2; std::tie(V1, V2) = Tok.Value.split('.'); if (V1.getAsInteger(10, *Major)) - error(Twine("integer expected, but got ") + Tok.Value); + fatal("integer expected, but got " + Tok.Value); if (V2.empty()) *Minor = 0; else if (V2.getAsInteger(10, *Minor)) - error(Twine("integer expected, but got ") + Tok.Value); + fatal("integer expected, but got " + Tok.Value); } Lexer Lex; diff --git a/COFF/Options.td b/COFF/Options.td index a21b8de76afb..e5c9c5b4635b 100644 --- a/COFF/Options.td +++ b/COFF/Options.td @@ -48,6 +48,7 @@ def manifestuac : P<"manifestuac", "User access control">; def manifestfile : P<"manifestfile", "Manifest file path">; def manifestdependency : P<"manifestdependency", "Attributes for <dependency> 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 @@ -110,6 +111,7 @@ 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">; diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp index 786d28798bab..7606ccc680d3 100644 --- a/COFF/PDB.cpp +++ b/COFF/PDB.cpp @@ -38,7 +38,8 @@ void lld::coff::createPDB(StringRef Path) { size_t FileSize = PageSize * 3; ErrorOr<std::unique_ptr<FileOutputBuffer>> BufferOrErr = FileOutputBuffer::create(Path, FileSize); - error(BufferOrErr, Twine("failed to open ") + Path); + if (auto EC = BufferOrErr.getError()) + fatal(EC, "failed to open " + Path); std::unique_ptr<FileOutputBuffer> Buffer = std::move(*BufferOrErr); // Write the file header. diff --git a/COFF/README.md b/COFF/README.md index c1be560f4405..f1bfc9c15263 100644 --- a/COFF/README.md +++ b/COFF/README.md @@ -1,265 +1 @@ -The PE/COFF Linker -================== - -This directory contains a linker for Windows operating system. -Because the fundamental design of this port is different from -the other ports of LLD, this port is separated to this directory. - -The linker is command-line compatible with MSVC linker and is -generally 2x faster than that. It can be used to link real-world -programs such as LLD itself or Clang, or even web browsers which -are probably the largest open-source programs for Windows. - -This document is also applicable to ELF linker because the linker -shares the same design as this COFF linker. - -Overall Design --------------- - -This is a list of important data types in this linker. - -* SymbolBody - - SymbolBody is a class for symbols. They may be created for symbols - in object files or in archive file headers. The linker may create - them out of nothing. - - There are mainly three types of SymbolBodies: Defined, Undefined, or - Lazy. Defined symbols are for all symbols that are considered as - "resolved", including real defined symbols, COMDAT symbols, common - symbols, absolute symbols, linker-created symbols, etc. Undefined - symbols are for undefined symbols, which need to be replaced by - Defined symbols by the resolver. Lazy symbols represent symbols we - found in archive file headers -- which can turn into Defined symbols - if we read archieve members, but we haven't done that yet. - -* Symbol - - Symbol is a pointer to a SymbolBody. There's only one Symbol for - each unique symbol name (this uniqueness is guaranteed by the symbol - table). Because SymbolBodies are created for each file - independently, there can be many SymbolBodies for the same - name. Thus, the relationship between Symbols and SymbolBodies is 1:N. - - The resolver keeps the Symbol's pointer to always point to the "best" - SymbolBody. Pointer mutation is the resolve operation in this - linker. - - SymbolBodies have pointers to their Symbols. That means you can - always find the best SymbolBody from any SymbolBody by following - pointers twice. This structure makes it very easy to find - replacements for symbols. For example, if you have an Undefined - SymbolBody, you can find a Defined SymbolBody for that symbol just - by going to its Symbol and then to SymbolBody, assuming the resolver - have successfully resolved all undefined symbols. - -* Chunk - - Chunk represents a chunk of data that will occupy space in an - output. Each regular section becomes a chunk. - Chunks created for common or BSS symbols are not backed by sections. - The linker may create chunks out of nothing to append additional - data to an output. - - Chunks know about their size, how to copy their data to mmap'ed - outputs, and how to apply relocations to them. Specifically, - section-based chunks know how to read relocation tables and how to - apply them. - -* SymbolTable - - SymbolTable is basically a hash table from strings to Symbols, with - a logic to resolve symbol conflicts. It resolves conflicts by symbol - type. For example, if we add Undefined and Defined symbols, the - symbol table will keep the latter. If we add Defined and Lazy - symbols, it will keep the former. If we add Lazy and Undefined, it - will keep the former, but it will also trigger the Lazy symbol to - load the archive member to actually resolve the symbol. - -* OutputSection - - OutputSection is a container of Chunks. A Chunk belongs to at most - one OutputSection. - -There are mainly three actors in this linker. - -* InputFile - - InputFile is a superclass of file readers. We have a different - subclass for each input file type, such as regular object file, - archive file, etc. They are responsible for creating and owning - SymbolBodies and Chunks. - -* Writer - - The writer is responsible for writing file headers and Chunks to a - file. It creates OutputSections, put all Chunks into them, assign - unique, non-overlapping addresses and file offsets to them, and then - write them down to a file. - -* Driver - - The linking process is drived by the driver. The driver - - - processes command line options, - - creates a symbol table, - - creates an InputFile for each input file and put all symbols in it - into the symbol table, - - checks if there's no remaining undefined symbols, - - creates a writer, - - and passes the symbol table to the writer to write the result to a - file. - -Performance ------------ - -It's generally 2x faster than MSVC link.exe. It takes 3.5 seconds to -self-host on my Xeon 2580 machine. MSVC linker takes 7.0 seconds to -link the same executable. The resulting output is 65MB. -The old LLD is buggy that it produces 120MB executable for some reason, -and it takes 30 seconds to do that. - -We believe the performance difference comes from simplification and -optimizations we made to the new port. Notable differences are listed -below. - -* Reduced number of relocation table reads - - In the old design, relocation tables are read from beginning to - construct graphs because they consist of graph edges. In the new - design, they are not read until we actually apply relocations. - - This simplification has two benefits. One is that we don't create - additional objects for relocations but instead consume relocation - tables directly. The other is that it reduces number of relocation - entries we have to read, because we won't read relocations for - dead-stripped COMDAT sections. Large C++ programs tend to consist of - lots of COMDAT sections. In the old design, the time to process - relocation table is linear to size of input. In this new model, it's - linear to size of output. - -* Reduced number of symbol table lookup - - Symbol table lookup can be a heavy operation because number of - symbols can be very large and each symbol name can be very long - (think of C++ mangled symbols -- time to compute a hash value for a - string is linear to the length.) - - We look up the symbol table exactly only once for each symbol in the - new design. This is I believe the minimum possible number. This is - achieved by the separation of Symbol and SymbolBody. Once you get a - pointer to a Symbol by looking up the symbol table, you can always - get the latest symbol resolution result by just dereferencing a - pointer. (I'm not sure if the idea is new to the linker. At least, - all other linkers I've investigated so far seem to look up hash - tables or sets more than once for each new symbol, but I may be - wrong.) - -* Reduced number of file visits - - The symbol table implements the Windows linker semantics. We treat - the symbol table as a bucket of all known symbols, including symbols - in archive file headers. We put all symbols into one bucket as we - visit new files. That means we visit each file only once. - - This is different from the Unix linker semantics, in which we only - keep undefined symbols and visit each file one by one until we - resolve all undefined symbols. In the Unix model, we have to visit - archive files many times if there are circular dependencies between - archives. - -* Avoiding creating additional objects or copying data - - The data structures described in the previous section are all thin - wrappers for classes that LLVM libObject provides. We avoid copying - data from libObject's objects to our objects. We read much less data - than before. For example, we don't read symbol values until we apply - relocations because these values are not relevant to symbol - resolution. Again, COMDAT symbols may be discarded during symbol - resolution, so reading their attributes too early could result in a - waste. We use underlying objects directly where doing so makes - sense. - -Parallelism ------------ - -The abovementioned data structures are also chosen with -multi-threading in mind. It should relatively be easy to make the -symbol table a concurrent hash map, so that we let multiple workers -work on symbol table concurrently. Symbol resolution in this design is -a single pointer mutation, which allows the resolver work concurrently -in a lock-free manner using atomic pointer compare-and-swap. - -It should also be easy to apply relocations and write chunks concurrently. - -We created an experimental multi-threaded linker using the Microsoft -ConcRT concurrency library, and it was able to link itself in 0.5 -seconds, so we think the design is promising. - -Link-Time Optimization ----------------------- - -LTO is implemented by handling LLVM bitcode files as object files. -The linker resolves symbols in bitcode files normally. If all symbols -are successfully resolved, it then calls an LLVM libLTO function -with all bitcode files to convert them to one big regular COFF file. -Finally, the linker replaces bitcode symbols with COFF symbols, -so that we can link the input files as if they were in the native -format from the beginning. - -The details are described in this document. -http://llvm.org/docs/LinkTimeOptimization.html - -Glossary --------- - -* RVA - - Short for Relative Virtual Address. - - Windows executables or DLLs are not position-independent; they are - linked against a fixed address called an image base. RVAs are - offsets from an image base. - - Default image bases are 0x140000000 for executables and 0x18000000 - for DLLs. For example, when we are creating an executable, we assume - that the executable will be loaded at address 0x140000000 by the - loader, so we apply relocations accordingly. Result texts and data - will contain raw absolute addresses. - -* VA - - Short for Virtual Address. Equivalent to RVA + image base. It is - rarely used. We almost always use RVAs instead. - -* Base relocations - - Relocation information for the loader. If the loader decides to map - an executable or a DLL to a different address than their image - bases, it fixes up binaries using information contained in the base - relocation table. A base relocation table consists of a list of - locations containing addresses. The loader adds a difference between - RVA and actual load address to all locations listed there. - - Note that this run-time relocation mechanism is much simpler than ELF. - There's no PLT or GOT. Images are relocated as a whole just - by shifting entire images in memory by some offsets. Although doing - this breaks text sharing, I think this mechanism is not actually bad - on today's computers. - -* ICF - - Short for Identical COMDAT Folding. - - ICF is an optimization to reduce output size by merging COMDAT sections - by not only their names but by their contents. If two COMDAT sections - happen to have the same metadata, actual contents and relocations, - they are merged by ICF. It is known as an effective technique, - and it usually reduces C++ program's size by a few percent or more. - - Note that this is not entirely sound optimization. C/C++ require - different functions have different addresses. If a program depends on - that property, it would fail at runtime. However, that's not really an - issue on Windows because MSVC link.exe enabled the optimization by - default. As long as your program works with the linker's default - settings, your program should be safe with ICF. +See docs/NewLLD.rst diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp index 5b7b89cd360a..df9da4c36650 100644 --- a/COFF/SymbolTable.cpp +++ b/COFF/SymbolTable.cpp @@ -14,7 +14,7 @@ #include "Symbols.h" #include "lld/Core/Parallel.h" #include "llvm/IR/LLVMContext.h" -#include "llvm/LTO/LTOCodeGenerator.h" +#include "llvm/LTO/legacy/LTOCodeGenerator.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include <utility> @@ -164,7 +164,7 @@ void SymbolTable::reportRemainingUndefines(bool Resolve) { llvm::errs() << File->getShortName() << ": undefined symbol: " << Sym->getName() << "\n"; if (!Config->Force) - error("Link failed"); + fatal("link failed"); } void SymbolTable::addLazy(Lazy *New, std::vector<Symbol *> *Accum) { @@ -211,7 +211,7 @@ void SymbolTable::addSymbol(SymbolBody *New) { // equivalent (conflicting), or more preferable, respectively. int Comp = Existing->compare(New); if (Comp == 0) - error(Twine("duplicate symbol: ") + Existing->getDebugName() + " and " + + fatal("duplicate symbol: " + Existing->getDebugName() + " and " + New->getDebugName()); if (Comp < 0) Sym->Body = New; @@ -338,21 +338,25 @@ void SymbolTable::addCombinedLTOObject(ObjectFile *Obj) { // diagnose them later in reportRemainingUndefines(). StringRef Name = Body->getName(); Symbol *Sym = insert(Body); + SymbolBody *Existing = Sym->Body; + + if (Existing == Body) + continue; - if (isa<DefinedBitcode>(Sym->Body)) { + if (isa<DefinedBitcode>(Existing)) { Sym->Body = Body; continue; } - if (auto *L = dyn_cast<Lazy>(Sym->Body)) { + if (auto *L = dyn_cast<Lazy>(Existing)) { // We may see new references to runtime library symbols such as __chkstk // here. These symbols must be wholly defined in non-bitcode files. addMemberFile(L); continue; } - SymbolBody *Existing = Sym->Body; + int Comp = Existing->compare(Body); if (Comp == 0) - error(Twine("LTO: unexpected duplicate symbol: ") + Name); + fatal("LTO: unexpected duplicate symbol: " + Name); if (Comp < 0) Sym->Body = Body; } @@ -369,7 +373,7 @@ void SymbolTable::addCombinedLTOObjects() { // Create an object file and add it to the symbol table by replacing any // DefinedBitcode symbols with the definitions in the object file. - LTOCodeGenerator CG(getGlobalContext()); + LTOCodeGenerator CG(BitcodeFile::Context); CG.setOptLevel(Config->LTOOptLevel); std::vector<ObjectFile *> Objs = createLTOObjects(&CG); @@ -379,7 +383,7 @@ void SymbolTable::addCombinedLTOObjects() { size_t NumBitcodeFiles = BitcodeFiles.size(); run(); if (BitcodeFiles.size() != NumBitcodeFiles) - error("LTO: late loaded symbol created new bitcode reference"); + fatal("LTO: late loaded symbol created new bitcode reference"); } // Combine and compile bitcode files and then return the result @@ -414,24 +418,23 @@ std::vector<ObjectFile *> SymbolTable::createLTOObjects(LTOCodeGenerator *CG) { DisableVerify = false; #endif if (!CG->optimize(DisableVerify, false, false, false)) - error(""); // optimize() should have emitted any error message. + fatal(""); // optimize() should have emitted any error message. Objs.resize(Config->LTOJobs); // Use std::list to avoid invalidation of pointers in OSPtrs. std::list<raw_svector_ostream> OSs; std::vector<raw_pwrite_stream *> OSPtrs; - for (SmallVector<char, 0> &Obj : Objs) { + for (SmallString<0> &Obj : Objs) { OSs.emplace_back(Obj); OSPtrs.push_back(&OSs.back()); } if (!CG->compileOptimized(OSPtrs)) - error(""); // compileOptimized() should have emitted any error message. + fatal(""); // compileOptimized() should have emitted any error message. std::vector<ObjectFile *> ObjFiles; - for (SmallVector<char, 0> &Obj : Objs) { - auto *ObjFile = new ObjectFile( - MemoryBufferRef(StringRef(Obj.data(), Obj.size()), "<LTO object>")); + for (SmallString<0> &Obj : Objs) { + auto *ObjFile = new ObjectFile(MemoryBufferRef(Obj, "<LTO object>")); Files.emplace_back(ObjFile); ObjectFiles.push_back(ObjFile); ObjFile->parse(); diff --git a/COFF/SymbolTable.h b/COFF/SymbolTable.h index ce305bfa8743..8bf4387cdfff 100644 --- a/COFF/SymbolTable.h +++ b/COFF/SymbolTable.h @@ -115,7 +115,7 @@ private: std::vector<std::future<InputFile *>> ObjectQueue; std::vector<BitcodeFile *> BitcodeFiles; - std::vector<SmallVector<char, 0>> Objs; + std::vector<SmallString<0>> Objs; llvm::BumpPtrAllocator Alloc; }; diff --git a/COFF/Symbols.cpp b/COFF/Symbols.cpp index d732d76cfb06..6e2db6631ce7 100644 --- a/COFF/Symbols.cpp +++ b/COFF/Symbols.cpp @@ -162,32 +162,6 @@ std::string SymbolBody::getDebugName() { return N; } -uint64_t Defined::getFileOff() { - switch (kind()) { - case DefinedImportDataKind: - return cast<DefinedImportData>(this)->getFileOff(); - case DefinedImportThunkKind: - return cast<DefinedImportThunk>(this)->getFileOff(); - case DefinedLocalImportKind: - return cast<DefinedLocalImport>(this)->getFileOff(); - case DefinedCommonKind: - return cast<DefinedCommon>(this)->getFileOff(); - case DefinedRegularKind: - return cast<DefinedRegular>(this)->getFileOff(); - - case DefinedBitcodeKind: - llvm_unreachable("There is no file offset for a bitcode symbol."); - case DefinedAbsoluteKind: - llvm_unreachable("Cannot get a file offset for an absolute symbol."); - case DefinedRelativeKind: - llvm_unreachable("Cannot get a file offset for a relative symbol."); - case LazyKind: - case UndefinedKind: - llvm_unreachable("Cannot get a file offset for an undefined symbol."); - } - llvm_unreachable("unknown symbol kind"); -} - COFFSymbolRef DefinedCOFF::getCOFFSymbol() { size_t SymSize = File->getCOFFObj()->getSymbolTableEntrySize(); if (SymSize == sizeof(coff_symbol16)) @@ -225,7 +199,7 @@ std::unique_ptr<InputFile> Lazy::getMember() { else if (Magic == file_magic::bitcode) Obj.reset(new BitcodeFile(MBRef)); else - error(Twine(File->getName()) + ": unknown file type"); + fatal("unknown file type: " + File->getName()); Obj->setParentName(File->getName()); return Obj; diff --git a/COFF/Symbols.h b/COFF/Symbols.h index 7059fbc8bb15..f96c1fb3cc1d 100644 --- a/COFF/Symbols.h +++ b/COFF/Symbols.h @@ -125,10 +125,6 @@ public: // writer sets and uses RVAs. uint64_t getRVA(); - // Returns the file offset of this symbol in the final executable. - // The writer uses this information to apply relocations. - uint64_t getFileOff(); - // Returns the RVA relative to the beginning of the output section. // Used to implement SECREL relocation type. uint64_t getSecrel(); diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp index 5575c8d6b320..d8077df95701 100644 --- a/COFF/Writer.cpp +++ b/COFF/Writer.cpp @@ -59,6 +59,7 @@ private: void openFile(StringRef OutputPath); template <typename PEHeaderTy> void writeHeader(); void fixSafeSEHSymbols(); + void setSectionPermissions(); void writeSections(); void sortExceptionTable(); void applyRelocations(); @@ -114,6 +115,7 @@ public: StringRef getName() { return Name; } std::vector<Chunk *> &getChunks() { return Chunks; } void addPermissions(uint32_t C); + void setPermissions(uint32_t C); uint32_t getPermissions() { return Header.Characteristics & PermMask; } uint32_t getCharacteristics() { return Header.Characteristics; } uint64_t getRVA() { return Header.VirtualAddress; } @@ -163,19 +165,23 @@ void OutputSection::addChunk(Chunk *C) { Chunks.push_back(C); C->setOutputSection(this); uint64_t Off = Header.VirtualSize; - Off = align(Off, C->getAlign()); + Off = alignTo(Off, C->getAlign()); C->setRVA(Off); C->setOutputSectionOff(Off); Off += C->getSize(); Header.VirtualSize = Off; if (C->hasData()) - Header.SizeOfRawData = align(Off, SectorSize); + Header.SizeOfRawData = alignTo(Off, SectorSize); } void OutputSection::addPermissions(uint32_t C) { Header.Characteristics |= C & PermMask; } +void OutputSection::setPermissions(uint32_t C) { + Header.Characteristics = C & PermMask; +} + // Write the section header to a given buffer. void OutputSection::writeHeaderTo(uint8_t *Buf) { auto *Hdr = reinterpret_cast<coff_section *>(Buf); @@ -193,13 +199,13 @@ void OutputSection::writeHeaderTo(uint8_t *Buf) { uint64_t Defined::getSecrel() { if (auto *D = dyn_cast<DefinedRegular>(this)) return getRVA() - D->getChunk()->getOutputSection()->getRVA(); - error("SECREL relocation points to a non-regular symbol"); + fatal("SECREL relocation points to a non-regular symbol"); } uint64_t Defined::getSectionIndex() { if (auto *D = dyn_cast<DefinedRegular>(this)) return D->getChunk()->getOutputSection()->SectionIndex; - error("SECTION relocation points to a non-regular symbol"); + fatal("SECTION relocation points to a non-regular symbol"); } bool Defined::isExecutable() { @@ -222,6 +228,7 @@ void Writer::run() { createSection(".reloc"); assignAddresses(); removeEmptySections(); + setSectionPermissions(); createSymbolAndStringTable(); openFile(Config->OutputFile); if (Config->is64()) { @@ -232,7 +239,8 @@ void Writer::run() { fixSafeSEHSymbols(); writeSections(); sortExceptionTable(); - error(Buffer->commit(), "Failed to write the output file"); + if (auto EC = Buffer->commit()) + fatal(EC, "failed to write the output file"); } static StringRef getOutputSection(StringRef Name) { @@ -447,15 +455,15 @@ void Writer::createSymbolAndStringTable() { OutputSection *LastSection = OutputSections.back(); // We position the symbol table to be adjacent to the end of the last section. - uint64_t FileOff = - LastSection->getFileOff() + align(LastSection->getRawSize(), SectorSize); + uint64_t FileOff = LastSection->getFileOff() + + alignTo(LastSection->getRawSize(), SectorSize); if (!OutputSymtab.empty()) { PointerToSymbolTable = FileOff; FileOff += OutputSymtab.size() * sizeof(coff_symbol16); } if (!Strtab.empty()) FileOff += Strtab.size() + 4; - FileSize = align(FileOff, SectorSize); + FileSize = alignTo(FileOff, SectorSize); } // Visits all sections to assign incremental, non-overlapping RVAs and @@ -466,7 +474,7 @@ void Writer::assignAddresses() { sizeof(coff_section) * OutputSections.size(); SizeOfHeaders += Config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header); - SizeOfHeaders = align(SizeOfHeaders, SectorSize); + SizeOfHeaders = alignTo(SizeOfHeaders, SectorSize); uint64_t RVA = 0x1000; // The first page is kept unmapped. FileSize = SizeOfHeaders; // Move DISCARDABLE (or non-memory-mapped) sections to the end of file because @@ -480,10 +488,10 @@ void Writer::assignAddresses() { addBaserels(Sec); Sec->setRVA(RVA); Sec->setFileOffset(FileSize); - RVA += align(Sec->getVirtualSize(), PageSize); - FileSize += align(Sec->getRawSize(), SectorSize); + RVA += alignTo(Sec->getVirtualSize(), PageSize); + FileSize += alignTo(Sec->getRawSize(), SectorSize); } - SizeOfImage = SizeOfHeaders + align(RVA - 0x1000, PageSize); + SizeOfImage = SizeOfHeaders + alignTo(RVA - 0x1000, PageSize); } template <typename PEHeaderTy> void Writer::writeHeader() { @@ -596,13 +604,26 @@ template <typename PEHeaderTy> void Writer::writeHeader() { if (Symbol *Sym = Symtab->findUnderscore("_tls_used")) { if (Defined *B = dyn_cast<Defined>(Sym->Body)) { Dir[TLS_TABLE].RelativeVirtualAddress = B->getRVA(); - Dir[TLS_TABLE].Size = 40; + Dir[TLS_TABLE].Size = Config->is64() + ? sizeof(object::coff_tls_directory64) + : sizeof(object::coff_tls_directory32); } } if (Symbol *Sym = Symtab->findUnderscore("_load_config_used")) { - if (Defined *B = dyn_cast<Defined>(Sym->Body)) { + if (auto *B = dyn_cast<DefinedRegular>(Sym->Body)) { + SectionChunk *SC = B->getChunk(); + assert(B->getRVA() >= SC->getRVA()); + uint64_t OffsetInChunk = B->getRVA() - SC->getRVA(); + if (!SC->hasData() || OffsetInChunk + 4 > SC->getSize()) + fatal("_load_config_used is malformed"); + + ArrayRef<uint8_t> SecContents = SC->getContents(); + uint32_t LoadConfigSize = + *reinterpret_cast<const ulittle32_t *>(&SecContents[OffsetInChunk]); + if (OffsetInChunk + LoadConfigSize > SC->getSize()) + fatal("_load_config_used is too large"); Dir[LOAD_CONFIG_TABLE].RelativeVirtualAddress = B->getRVA(); - Dir[LOAD_CONFIG_TABLE].Size = Config->is64() ? 112 : 64; + Dir[LOAD_CONFIG_TABLE].Size = LoadConfigSize; } } @@ -626,14 +647,14 @@ template <typename PEHeaderTy> void Writer::writeHeader() { // The first 4 bytes is length including itself. Buf = reinterpret_cast<uint8_t *>(&SymbolTable[NumberOfSymbols]); write32le(Buf, Strtab.size() + 4); - memcpy(Buf + 4, Strtab.data(), Strtab.size()); + if (!Strtab.empty()) + memcpy(Buf + 4, Strtab.data(), Strtab.size()); } void Writer::openFile(StringRef Path) { - ErrorOr<std::unique_ptr<FileOutputBuffer>> BufferOrErr = - FileOutputBuffer::create(Path, FileSize, FileOutputBuffer::F_executable); - error(BufferOrErr, Twine("failed to open ") + Path); - Buffer = std::move(*BufferOrErr); + Buffer = check( + FileOutputBuffer::create(Path, FileSize, FileOutputBuffer::F_executable), + "failed to open " + Path); } void Writer::fixSafeSEHSymbols() { @@ -643,6 +664,17 @@ void Writer::fixSafeSEHSymbols() { Config->SEHCount->setVA(SEHTable->getSize() / 4); } +// Handles /section options to allow users to overwrite +// section attributes. +void Writer::setSectionPermissions() { + for (auto &P : Config->Section) { + StringRef Name = P.first; + uint32_t Perm = P.second; + if (auto *Sec = findSection(Name)) + Sec->setPermissions(Perm); + } +} + // Write section contents to a mmap'ed file. void Writer::writeSections() { uint8_t *Buf = Buffer->getBufferStart(); |