[lld] r193298 - [PECOFF] Support embedding resource file into executable.
Rui Ueyama
ruiu at google.com
Wed Oct 23 18:39:43 PDT 2013
Author: ruiu
Date: Wed Oct 23 20:39:43 2013
New Revision: 193298
URL: http://llvm.org/viewvc/llvm-project?rev=193298&view=rev
Log:
[PECOFF] Support embedding resource file into executable.
Instead of making the linker to create a manifest XML file in the same
directory as the resulting binary, you can embed the XML as a part of
resource into the executable.
In order to do that, the linker first creates a resource script file containing
the XML file, compile it into a binary resource file with RC.EXE, and then
convert it to a COFF file with CVTRES.EXE.
Modified:
lld/trunk/include/lld/ReaderWriter/PECOFFLinkingContext.h
lld/trunk/lib/Driver/WinLinkDriver.cpp
Modified: lld/trunk/include/lld/ReaderWriter/PECOFFLinkingContext.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/include/lld/ReaderWriter/PECOFFLinkingContext.h?rev=193298&r1=193297&r2=193298&view=diff
==============================================================================
--- lld/trunk/include/lld/ReaderWriter/PECOFFLinkingContext.h (original)
+++ lld/trunk/include/lld/ReaderWriter/PECOFFLinkingContext.h Wed Oct 23 20:39:43 2013
@@ -20,6 +20,7 @@
#include "llvm/Support/Allocator.h"
#include "llvm/Support/COFF.h"
#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FileUtilities.h"
using llvm::COFF::MachineTypes;
using llvm::COFF::WindowsSubsystem;
@@ -76,6 +77,12 @@ public:
return _inputSearchPaths;
}
+ void registerTemporaryFile(StringRef path) {
+ std::unique_ptr<llvm::FileRemover> fileRemover(
+ new llvm::FileRemover(Twine(allocateString(path))));
+ _tempFiles.push_back(std::move(fileRemover));
+ }
+
StringRef searchLibraryFile(StringRef path) const;
/// Returns the decorated name of the given symbol name. On 32-bit x86, it
@@ -247,6 +254,9 @@ private:
std::vector<StringRef> _inputSearchPaths;
std::unique_ptr<Reader> _reader;
std::unique_ptr<Writer> _writer;
+
+ // List of files that will be removed on destruction.
+ std::vector<std::unique_ptr<llvm::FileRemover> > _tempFiles;
};
} // end namespace lld
Modified: lld/trunk/lib/Driver/WinLinkDriver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/Driver/WinLinkDriver.cpp?rev=193298&r1=193297&r2=193298&view=diff
==============================================================================
--- lld/trunk/lib/Driver/WinLinkDriver.cpp (original)
+++ lld/trunk/lib/Driver/WinLinkDriver.cpp Wed Oct 23 20:39:43 2013
@@ -27,12 +27,17 @@
#include "llvm/Option/Option.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
+#include "llvm/Support/Program.h"
#include "llvm/Support/raw_ostream.h"
namespace lld {
namespace {
+//
+// Option definitions
+//
+
// Create enum with OPT_xxx values for each option in WinLinkOptions.td
enum {
OPT_INVALID = 0,
@@ -68,6 +73,10 @@ public:
/* ignoreCase */ true) {}
};
+//
+// Functions to parse each command line option
+//
+
// Split the given string with spaces.
std::vector<std::string> splitArgList(const std::string &str) {
std::stringstream stream(str);
@@ -205,23 +214,12 @@ StringRef replaceExtension(PECOFFLinking
return ctx.allocateString(val.str());
}
-// Create a side-by-side manifest file. The manifest file will convey some
-// information to the linker, such as whether the binary needs to run as
-// Administrator or not. Instead of being placed in the PE/COFF header, it's in
-// XML format for some reason -- I guess it's probably because it's invented in
-// the early dot-com era.
-//
-// Instead of having the linker emit a manifest file as a separate file, you
-// could have the linker embed the contents of XML into the resource section of
-// the executable. The feature is not implemented in LLD yet, though.
-bool createManifestFile(PECOFFLinkingContext &ctx, raw_ostream &diagnostics) {
- std::string errorInfo;
- llvm::raw_fd_ostream out(ctx.getManifestOutputPath().data(), errorInfo);
- if (!errorInfo.empty()) {
- diagnostics << "Failed to open " << ctx.getManifestOutputPath() << ": "
- << errorInfo << "\n";
- return false;
- }
+// Create a manifest file contents.
+std::string createManifestXml(PECOFFLinkingContext &ctx) {
+ std::string ret;
+ llvm::raw_string_ostream out(ret);
+ // Emit the XML. Note that we do *not* verify that the XML attributes are
+ // syntactically correct. This is intentional for link.exe compatibility.
out << "<?xml version=\"1.0\" standalone=\"yes\"?>\n"
<< "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\"\n"
<< " manifestVersion=\"1.0\">\n"
@@ -242,9 +240,148 @@ bool createManifestFile(PECOFFLinkingCon
<< " </dependency>\n";
}
out << "</assembly>\n";
+ out.flush();
+ return std::move(ret);
+}
+
+// Quote double quotes and backslashes in the given string, so that we can embed
+// the string into a resource script file.
+std::string quoteXml(StringRef str) {
+ std::string ret;
+ ret.reserve(str.size() * 2);
+ StringRef line;
+ for (;;) {
+ if (str.empty())
+ return std::move(ret);
+ llvm::tie(line, str) = str.split("\n");
+ if (!line.empty())
+ continue;
+ ret.append("\"");
+ const char *p = line.data();
+ for (int i = 0, size = line.size(); i < size; ++i) {
+ switch (p[i]) {
+ case '\"':
+ case '\\':
+ ret.append("\\");
+ // fallthrough
+ default:
+ ret.append(1, p[i]);
+ }
+ }
+ ret.append("\"\n");
+ }
+}
+
+// Create a resource file (.res file) containing the manifest XML. This is done
+// in two steps:
+//
+// 1. Create a resource script file containing the XML as a literal string.
+// 2. Run RC.EXE command to compile the script file to a resource file.
+//
+// The temporary file created in step 1 will be deleted on exit from this
+// function. The file created in step 2 will have the same lifetime as the
+// PECOFFLinkingContext.
+bool createManifestResourceFile(PECOFFLinkingContext &ctx,
+ raw_ostream &diagnostics,
+ std::string &resFile) {
+ // Create a temporary file for the resource script file.
+ SmallString<128> rcFileSmallString;
+ if (llvm::sys::fs::createTemporaryFile("tmp", "rc", rcFileSmallString)) {
+ diagnostics << "Cannot create a temporary file\n";
+ return false;
+ }
+ StringRef rcFile(rcFileSmallString.str());
+ llvm::FileRemover rcFileRemover((Twine(rcFile)));
+
+ // Open the temporary file for writing.
+ std::string errorInfo;
+ llvm::raw_fd_ostream out(rcFile.data(), errorInfo);
+ if (!errorInfo.empty()) {
+ diagnostics << "Failed to open " << ctx.getManifestOutputPath() << ": "
+ << errorInfo << "\n";
+ return false;
+ }
+
+ // Write resource script to the RC file.
+ out << "#define LANG_ENGLISH 9\n"
+ << "#define SUBLANG_DEFAULT 1\n"
+ << "#define APP_MANIFEST " << ctx.getManifestId() << "\n"
+ << "#define RT_MANIFEST 24\n"
+ << "LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT\n"
+ << "APP_MANIFEST RT_MANIFEST {\n"
+ << quoteXml(createManifestXml(ctx))
+ << "}\n";
+ out.close();
+
+ // Create output resource file.
+ SmallString<128> resFileSmallString;
+ if (llvm::sys::fs::createTemporaryFile("tmp", "res", resFileSmallString)) {
+ diagnostics << "Cannot create a temporary file";
+ return false;
+ }
+ resFile = resFileSmallString.str();
+
+ // Register the resource file path so that the file will be deleted when the
+ // context's destructor is called.
+ ctx.registerTemporaryFile(resFile);
+
+ // Run RC.EXE /fo tmp.res tmp.rc
+ std::string program = "rc.exe";
+ std::string programPath = llvm::sys::FindProgramByName(program);
+ if (programPath.empty()) {
+ diagnostics << "Unable to find " << program << " in PATH\n";
+ return false;
+ }
+ std::vector<const char *> args;
+ args.push_back(programPath.c_str());
+ args.push_back("/fo");
+ args.push_back(resFile.c_str());
+ args.push_back(rcFile.data());
+ args.push_back(nullptr);
+
+ if (llvm::sys::ExecuteAndWait(programPath.c_str(), &args[0]) != 0) {
+ llvm::errs() << program << " failed\n";
+ return false;
+ }
+ return true;
+}
+
+// Create a side-by-side manifest file. The side-by-side manifest file is a
+// separate XML file having ".manifest" extension. It will be created in the
+// same directory as the resulting executable.
+bool createSideBySideManifestFile(PECOFFLinkingContext &ctx,
+ raw_ostream &diagnostics) {
+ std::string errorInfo;
+ llvm::raw_fd_ostream out(ctx.getManifestOutputPath().data(), errorInfo);
+ if (!errorInfo.empty()) {
+ diagnostics << "Failed to open " << ctx.getManifestOutputPath() << ": "
+ << errorInfo << "\n";
+ return false;
+ }
+ out << createManifestXml(ctx);
return true;
}
+// Create the a side-by-side manifest file, or create a resource file for the
+// manifest file and add it to the input graph.
+//
+// The manifest file will convey some information to the linker, such as whether
+// the binary needs to run as Administrator or not. Instead of being placed in
+// the PE/COFF header, it's in XML format for some reason -- I guess it's
+// probably because it's invented in the early dot-com era.
+bool createManifest(PECOFFLinkingContext &ctx, raw_ostream &diagnostics) {
+ if (ctx.getEmbedManifest()) {
+ std::string resourceFilePath;
+ if (!createManifestResourceFile(ctx, diagnostics, resourceFilePath))
+ return false;
+ std::unique_ptr<InputElement> inputElement(
+ new PECOFFFileNode(ctx, resourceFilePath));
+ ctx.inputGraph().addInputElement(std::move(inputElement));
+ return true;
+ }
+ return createSideBySideManifestFile(ctx, diagnostics);
+}
+
// Handle /failifmismatch option.
bool handleFailIfMismatchOption(StringRef option,
std::map<StringRef, StringRef> &mustMatch,
@@ -265,6 +402,10 @@ bool handleFailIfMismatchOption(StringRe
return false;
}
+//
+// Environment variable
+//
+
// Process "LINK" environment variable. If defined, the value of the variable
// should be processed as command line arguments.
std::vector<const char *> processLinkEnv(PECOFFLinkingContext &context,
@@ -344,6 +485,10 @@ parseArgs(int argc, const char *argv[],
} // namespace
+//
+// Main driver
+//
+
ErrorOr<StringRef> PECOFFFileNode::getPath(const LinkingContext &) const {
if (_path.endswith(".lib"))
return _ctx.searchLibraryFile(_path);
@@ -365,6 +510,12 @@ bool WinLinkDriver::linkPECOFF(int argc,
processLibEnv(context);
if (!parse(newargv.size() - 1, &newargv[0], context, diagnostics))
return false;
+
+ // Create the file if needed.
+ if (context.getCreateManifest())
+ if (!createManifest(context, diagnostics))
+ return false;
+
return link(context, diagnostics);
}
@@ -714,11 +865,6 @@ WinLinkDriver::parse(int argc, const cha
for (auto &e : inputElements)
ctx.inputGraph().addInputElement(std::move(e));
- // Create the side-by-side manifest file if needed.
- if (!isReadingDirectiveSection && ctx.getCreateManifest())
- if (!createManifestFile(ctx, diagnostics))
- return false;
-
// Validate the combination of options used.
return ctx.validate(diagnostics);
}
More information about the llvm-commits
mailing list