[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