summaryrefslogtreecommitdiff
path: root/COFF/DriverUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'COFF/DriverUtils.cpp')
-rw-r--r--COFF/DriverUtils.cpp194
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