diff options
Diffstat (limited to 'COFF/DriverUtils.cpp')
-rw-r--r-- | COFF/DriverUtils.cpp | 194 |
1 files changed, 119 insertions, 75 deletions
diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp index 5d7dc2bc65aff..14dd004f1c049 100644 --- a/COFF/DriverUtils.cpp +++ b/COFF/DriverUtils.cpp @@ -16,6 +16,7 @@ #include "Config.h" #include "Driver.h" #include "Error.h" +#include "Memory.h" #include "Symbols.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" @@ -43,29 +44,29 @@ namespace { class Executor { public: explicit Executor(StringRef S) : Saver(Alloc), 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 add(StringRef S) { Args.push_back(Saver.save(S).data()); } + void add(std::string &S) { Args.push_back(Saver.save(S).data()); } + void add(Twine S) { Args.push_back(Saver.save(S).data()); } + void add(const char *S) { Args.push_back(Saver.save(S).data()); } void run() { - ErrorOr<std::string> ExeOrErr = llvm::sys::findProgramByName(Prog); + ErrorOr<std::string> ExeOrErr = sys::findProgramByName(Prog); if (auto EC = ExeOrErr.getError()) fatal(EC, "unable to find " + Prog + " in PATH: "); - const char *Exe = Saver.save(*ExeOrErr); + const char *Exe = Saver.save(*ExeOrErr).data(); Args.insert(Args.begin(), Exe); Args.push_back(nullptr); - if (llvm::sys::ExecuteAndWait(Args[0], Args.data()) != 0) { + if (sys::ExecuteAndWait(Args[0], Args.data()) != 0) { for (const char *S : Args) if (S) - llvm::errs() << S << " "; + errs() << S << " "; fatal("ExecuteAndWait failed"); } } private: - llvm::BumpPtrAllocator Alloc; - llvm::StringSaver Saver; + BumpPtrAllocator Alloc; + StringSaver Saver; StringRef Prog; std::vector<const char *> Args; }; @@ -75,10 +76,8 @@ private: // Returns /machine's value. MachineTypes getMachineType(StringRef S) { MachineTypes MT = StringSwitch<MachineTypes>(S.lower()) - .Case("x64", AMD64) - .Case("amd64", AMD64) - .Case("x86", I386) - .Case("i386", I386) + .Cases("x64", "amd64", AMD64) + .Cases("x86", "i386", I386) .Case("arm", ARMNT) .Default(IMAGE_FILE_MACHINE_UNKNOWN); if (MT != IMAGE_FILE_MACHINE_UNKNOWN) @@ -168,8 +167,8 @@ void parseMerge(StringRef S) { if (!Inserted) { StringRef Existing = Pair.first->second; if (Existing != To) - llvm::errs() << "warning: " << S << ": already merged into " - << Existing << "\n"; + errs() << "warning: " << S << ": already merged into " << Existing + << "\n"; } } @@ -279,18 +278,54 @@ static void quoteAndPrint(raw_ostream &Out, StringRef S) { } } +// An RAII temporary file class that automatically removes a temporary file. +namespace { +class TemporaryFile { +public: + TemporaryFile(StringRef Prefix, StringRef Extn) { + SmallString<128> S; + if (auto EC = sys::fs::createTemporaryFile("lld-" + Prefix, Extn, S)) + fatal(EC, "cannot create a temporary file"); + Path = S.str(); + } + + 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<MemoryBuffer> 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; +}; +} + // Create the default manifest file as a temporary file. -static std::string createDefaultXml() { +TemporaryFile createDefaultXml() { // Create a temporary file. - SmallString<128> Path; - if (auto EC = sys::fs::createTemporaryFile("tmp", "manifest", Path)) - fatal(EC, "cannot create a temporary file"); + TemporaryFile File("defaultxml", "manifest"); // Open the temporary file for writing. std::error_code EC; - llvm::raw_fd_ostream OS(Path, EC, sys::fs::F_Text); + raw_fd_ostream OS(File.Path, EC, sys::fs::F_Text); if (EC) - fatal(EC, "failed to open " + Path); + fatal(EC, "failed to open " + File.Path); // Emit the XML. Note that we do *not* verify that the XML attributes are // syntactically correct. This is intentional for link.exe compatibility. @@ -316,56 +351,48 @@ static std::string createDefaultXml() { } OS << "</assembly>\n"; OS.close(); - return StringRef(Path); + return File; } 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(); + return MB->getBuffer(); } static std::string createManifestXml() { // Create the default manifest file. - std::string Path1 = createDefaultXml(); + TemporaryFile File1 = createDefaultXml(); if (Config->ManifestInput.empty()) - return readFile(Path1); + return readFile(File1.Path); // 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); + TemporaryFile File2("user", "manifest"); Executor E("mt.exe"); E.add("/manifest"); - E.add(Path1); + E.add(File1.Path); for (StringRef Filename : Config->ManifestInput) { E.add("/manifest"); E.add(Filename); } E.add("/nologo"); - E.add("/out:" + StringRef(Path2)); + E.add("/out:" + StringRef(File2.Path)); E.run(); - return readFile(Path2); + return readFile(File2.Path); } // 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; - if (auto EC = sys::fs::createTemporaryFile("tmp", "rc", RCPath)) - fatal(EC, "cannot create a temporary file"); - FileRemover RCRemover(RCPath); + TemporaryFile RCFile("manifest", "rc"); // Open the temporary file for writing. std::error_code EC; - llvm::raw_fd_ostream Out(RCPath, EC, sys::fs::F_Text); + raw_fd_ostream Out(RCFile.Path, EC, sys::fs::F_Text); if (EC) - fatal(EC, "failed to open " + RCPath); + fatal(EC, "failed to open " + RCFile.Path); // Write resource script to the RC file. Out << "#define LANG_ENGLISH 9\n" @@ -379,17 +406,15 @@ std::unique_ptr<MemoryBuffer> createManifestRes() { Out.close(); // Create output resource file. - SmallString<128> ResPath; - if (auto EC = sys::fs::createTemporaryFile("tmp", "res", ResPath)) - fatal(EC, "cannot create a temporary file"); + TemporaryFile ResFile("output-resource", "res"); Executor E("rc.exe"); E.add("/fo"); - E.add(ResPath.str()); + E.add(ResFile.Path); E.add("/nologo"); - E.add(RCPath.str()); + E.add(RCFile.Path); E.run(); - return check(MemoryBuffer::getFile(ResPath), "could not open " + ResPath); + return ResFile.getMemoryBuffer(); } void createSideBySideManifest() { @@ -397,7 +422,7 @@ void createSideBySideManifest() { if (Path == "") Path = Config->OutputFile + ".manifest"; std::error_code EC; - llvm::raw_fd_ostream Out(Path, EC, llvm::sys::fs::F_Text); + raw_fd_ostream Out(Path, EC, sys::fs::F_Text); if (EC) fatal(EC, "failed to create manifest"); Out << createManifestXml(); @@ -485,12 +510,14 @@ void fixupExports() { } for (Export &E : Config->Exports) { + SymbolBody *Sym = E.Sym; if (!E.ForwardTo.empty()) { E.SymbolName = E.Name; - } else if (Undefined *U = cast_or_null<Undefined>(E.Sym->WeakAlias)) { - E.SymbolName = U->getName(); } else { - E.SymbolName = E.Sym->getName(); + if (auto *U = dyn_cast<Undefined>(Sym)) + if (U->WeakAlias) + Sym = U->WeakAlias; + E.SymbolName = Sym->getName(); } } @@ -515,7 +542,7 @@ void fixupExports() { Export *Existing = Pair.first->second; if (E == *Existing || E.Name != Existing->Name) continue; - llvm::errs() << "warning: duplicate /export option: " << E.Name << "\n"; + errs() << "warning: duplicate /export option: " << E.Name << "\n"; } Config->Exports = std::move(V); @@ -555,20 +582,39 @@ void checkFailIfMismatch(StringRef Arg) { std::unique_ptr<MemoryBuffer> convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) { // Create an output file path. - SmallString<128> Path; - if (auto EC = llvm::sys::fs::createTemporaryFile("resource", "obj", Path)) - fatal(EC, "could not create temporary file"); + TemporaryFile File("resource-file", "obj"); // Execute cvtres.exe. Executor E("cvtres.exe"); E.add("/machine:" + machineToStr(Config->Machine)); E.add("/readonly"); E.add("/nologo"); - E.add("/out:" + Path); - for (MemoryBufferRef MB : MBs) - E.add(MB.getBufferIdentifier()); + E.add("/out:" + Twine(File.Path)); + + // We must create new files because the memory buffers we have may have no + // underlying file still existing on the disk. + // It happens if it was created from a TemporaryFile, which usually delete + // the file just after creating the MemoryBuffer. + std::vector<TemporaryFile> ResFiles; + ResFiles.reserve(MBs.size()); + for (MemoryBufferRef MB : MBs) { + // We store the temporary file in a vector to avoid deletion + // before running cvtres + ResFiles.emplace_back("resource-file", "res"); + TemporaryFile& ResFile = ResFiles.back(); + // Write the content of the resource in a temporary file + std::error_code EC; + raw_fd_ostream OS(ResFile.Path, EC, sys::fs::F_None); + if (EC) + fatal(EC, "failed to open " + ResFile.Path); + OS << MB.getBuffer(); + OS.close(); + + E.add(ResFile.Path); + } + E.run(); - return check(MemoryBuffer::getFile(Path), "could not open " + Path); + return File.getMemoryBuffer(); } // Create OptTable @@ -595,7 +641,7 @@ public: }; // Parses a given list of options. -llvm::opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) { +opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) { // First, replace respnose files (@<file>-style options). std::vector<const char *> Argv = replaceResponseFiles(ArgsArr); @@ -603,28 +649,28 @@ llvm::opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) { COFFOptTable Table; unsigned MissingIndex; unsigned MissingCount; - llvm::opt::InputArgList Args = - Table.ParseArgs(Argv, MissingIndex, MissingCount); + opt::InputArgList Args = Table.ParseArgs(Argv, MissingIndex, MissingCount); // Print the real command line if response files are expanded. if (Args.hasArg(OPT_verbose) && ArgsArr.size() != Argv.size()) { - llvm::outs() << "Command line:"; + outs() << "Command line:"; for (const char *S : Argv) - llvm::outs() << " " << S; - llvm::outs() << "\n"; + outs() << " " << S; + outs() << "\n"; } if (MissingCount) - fatal("missing arg value for \"" + Twine(Args.getArgString(MissingIndex)) + - "\", expected " + Twine(MissingCount) + - (MissingCount == 1 ? " argument." : " arguments.")); + fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument"); for (auto *Arg : Args.filtered(OPT_UNKNOWN)) - llvm::errs() << "ignoring unknown argument: " << Arg->getSpelling() << "\n"; + errs() << "ignoring unknown argument: " << Arg->getSpelling() << "\n"; return Args; } -llvm::opt::InputArgList ArgParser::parseLINK(ArrayRef<const char *> Args) { - // Concatenate LINK env and given arguments and parse them. +// link.exe has an interesting feature. If LINK environment exists, +// its contents are handled as a command line string. So you can pass +// extra arguments using the environment variable. +opt::InputArgList ArgParser::parseLINK(ArrayRef<const char *> Args) { + // Concatenate LINK env and command line arguments, and then parse them. Optional<std::string> Env = Process::GetEnv("LINK"); if (!Env) return parse(Args); @@ -635,8 +681,7 @@ llvm::opt::InputArgList ArgParser::parseLINK(ArrayRef<const char *> Args) { std::vector<const char *> ArgParser::tokenize(StringRef S) { SmallVector<const char *, 16> Tokens; - StringSaver Saver(AllocAux); - llvm::cl::TokenizeWindowsCommandLine(S, Saver, Tokens); + cl::TokenizeWindowsCommandLine(S, Saver, Tokens); return std::vector<const char *>(Tokens.begin(), Tokens.end()); } @@ -645,14 +690,13 @@ std::vector<const char *> ArgParser::tokenize(StringRef S) { std::vector<const char *> ArgParser::replaceResponseFiles(std::vector<const char *> Argv) { SmallVector<const char *, 256> Tokens(Argv.data(), Argv.data() + Argv.size()); - StringSaver Saver(AllocAux); ExpandResponseFiles(Saver, TokenizeWindowsCommandLine, Tokens); return std::vector<const char *>(Tokens.begin(), Tokens.end()); } void printHelp(const char *Argv0) { COFFOptTable Table; - Table.PrintHelp(llvm::outs(), Argv0, "LLVM Linker", false); + Table.PrintHelp(outs(), Argv0, "LLVM Linker", false); } } // namespace coff |