[lld] r193298 - [PECOFF] Support embedding resource file into executable.
Reid Kleckner
rnk at google.com
Thu Oct 24 10:18:16 PDT 2013
Cool! Some thoughts below.
On Wed, Oct 23, 2013 at 6:39 PM, Rui Ueyama <ruiu at google.com> wrote:
> +// 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");
> + }
> +}
>
Micro-optimization nit: use push_back over append for single character
things because it won't contain a loop.
> +
> +// 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))
>
If this is the only use, then quoteXml should probably take out as a
parameter and stream to it, rather than building up a temporary std::string.
> + << "}\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());
>
Are you sure this will be null-terminated? Seems like you do:
+ SmallString<128> rcFileSmallString;
+ if (llvm::sys::fs::createTemporaryFile("tmp", "rc", rcFileSmallString)) {
...
+ StringRef rcFile(rcFileSmallString.str());
...
+ args.push_back(rcFile.data());
I was able to prove that the temp file comes back null-terminated due to
internal implementation details of the Windows UTF16ToUTF8 helper function,
but it seems like you should null terminate here.
> + args.push_back(nullptr);
> +
> + if (llvm::sys::ExecuteAndWait(programPath.c_str(), &args[0]) != 0) {
> + llvm::errs() << program << " failed\n";
>
llvm::errs() or diagnostics?
> + 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);
> }
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20131024/98959418/attachment.html>
More information about the llvm-commits
mailing list