[lld] r239978 - COFF: Support /manifest{, uac, dependency, file} options.

Rui Ueyama ruiu at google.com
Wed Jun 17 17:12:43 PDT 2015


Author: ruiu
Date: Wed Jun 17 19:12:42 2015
New Revision: 239978

URL: http://llvm.org/viewvc/llvm-project?rev=239978&view=rev
Log:
COFF: Support /manifest{,uac,dependency,file} options.

The linker has to create an XML file for each executable.
This patch supports that feature.

You can optionally embed an XML file to an executable as .rsrc
section. If you choose to do that (by passing /manifest:embed
option), the linker has to create a textual resource file
containing an XML file, compile that using rc.exe to a binary
resource file, conver that resource file to a COFF file using
cvtres.exe, and then link that COFF file. This patch implements
that feature too.

Added:
    lld/trunk/test/COFF/manifest.test
Modified:
    lld/trunk/COFF/Config.h
    lld/trunk/COFF/Driver.cpp
    lld/trunk/COFF/Driver.h
    lld/trunk/COFF/DriverUtils.cpp

Modified: lld/trunk/COFF/Config.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Config.h?rev=239978&r1=239977&r2=239978&view=diff
==============================================================================
--- lld/trunk/COFF/Config.h (original)
+++ lld/trunk/COFF/Config.h Wed Jun 17 19:12:42 2015
@@ -37,6 +37,8 @@ struct Export {
 
 // Global configuration.
 struct Configuration {
+  enum ManifestKind { SideBySide, Embed, No };
+
   llvm::COFF::MachineTypes MachineType = llvm::COFF::IMAGE_FILE_MACHINE_AMD64;
   bool Verbose = false;
   WindowsSubsystem Subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN;
@@ -55,6 +57,15 @@ struct Configuration {
   bool DLL = false;
   std::vector<Export> Exports;
 
+  // Options for manifest files.
+  ManifestKind Manifest = SideBySide;
+  int ManifestID = 1;
+  StringRef ManifestDependency;
+  bool ManifestUAC = true;
+  StringRef ManifestLevel = "'asInvoker'";
+  StringRef ManifestUIAccess = "'false'";
+  StringRef ManifestFile;
+
   // Used by /failifmismatch option.
   std::map<StringRef, StringRef> MustMatch;
 

Modified: lld/trunk/COFF/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Driver.cpp?rev=239978&r1=239977&r2=239978&view=diff
==============================================================================
--- lld/trunk/COFF/Driver.cpp (original)
+++ lld/trunk/COFF/Driver.cpp Wed Jun 17 19:12:42 2015
@@ -241,8 +241,10 @@ bool LinkerDriver::link(int Argc, const
     Config->Verbose = true;
 
   // Handle /dll
-  if (Args->hasArg(OPT_dll))
+  if (Args->hasArg(OPT_dll)) {
     Config->DLL = true;
+    Config->ManifestID = 2;
+  }
 
   // Handle /entry
   if (auto *Arg = Args->getLastArg(OPT_entry))
@@ -367,6 +369,30 @@ bool LinkerDriver::link(int Argc, const
       return false;
   }
 
+  // Handle /manifest
+  if (auto *Arg = Args->getLastArg(OPT_manifest_colon)) {
+    if (auto EC = parseManifest(Arg->getValue())) {
+      llvm::errs() << "/manifest: " << EC.message() << "\n";
+      return false;
+    }
+  }
+
+  // Handle /manifestuac
+  if (auto *Arg = Args->getLastArg(OPT_manifestuac)) {
+    if (auto EC = parseManifestUAC(Arg->getValue())) {
+      llvm::errs() << "/manifestuac: " << EC.message() << "\n";
+      return false;
+    }
+  }
+
+  // Handle /manifestdependency
+  if (auto *Arg = Args->getLastArg(OPT_manifestdependency))
+    Config->ManifestDependency = Arg->getValue();
+
+  // Handle /manifestfile
+  if (auto *Arg = Args->getLastArg(OPT_manifestfile))
+    Config->ManifestFile = Arg->getValue();
+
   // Handle miscellaneous boolean flags.
   if (Args->hasArg(OPT_allowbind_no))      Config->AllowBind = false;
   if (Args->hasArg(OPT_allowisolation_no)) Config->AllowIsolation = false;
@@ -405,6 +431,16 @@ bool LinkerDriver::link(int Argc, const
     Config->GCRoots.insert(Sym);
   }
 
+  // Windows specific -- Create a resource file containing a manifest file.
+  if (Config->Manifest == Configuration::Embed) {
+    auto MBOrErr = createManifestRes();
+    if (MBOrErr.getError())
+      return false;
+    std::unique_ptr<MemoryBuffer> MB = std::move(MBOrErr.get());
+    Inputs.push_back(MB->getMemBufferRef());
+    OwningMBs.push_back(std::move(MB)); // take ownership
+  }
+
   // Windows specific -- Input files can be Windows resource files (.res files).
   // We invoke cvtres.exe to convert resource files to a regular COFF file
   // then link the result file normally.
@@ -513,6 +549,11 @@ bool LinkerDriver::link(int Argc, const
       return false;
   }
 
+  // Windows specific -- Create a side-by-side manifest file.
+  if (Config->Manifest == Configuration::SideBySide)
+    if (createSideBySideManifest())
+      return false;
+
   // Write the result.
   Writer Out(&Symtab);
   if (auto EC = Out.write(Config->OutputFile)) {

Modified: lld/trunk/COFF/Driver.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Driver.h?rev=239978&r1=239977&r2=239978&view=diff
==============================================================================
--- lld/trunk/COFF/Driver.h (original)
+++ lld/trunk/COFF/Driver.h Wed Jun 17 19:12:42 2015
@@ -119,6 +119,16 @@ std::error_code parseVersion(StringRef A
 std::error_code parseSubsystem(StringRef Arg, WindowsSubsystem *Sys,
                                uint32_t *Major, uint32_t *Minor);
 
+// Parses a string in the form of "EMBED[,=<integer>]|NO".
+std::error_code parseManifest(StringRef Arg);
+
+// Parses a string in the form of "level=<string>|uiAccess=<string>"
+std::error_code parseManifestUAC(StringRef Arg);
+
+// Create a resource file containing a manifest XML.
+ErrorOr<std::unique_ptr<MemoryBuffer>> createManifestRes();
+std::error_code createSideBySideManifest();
+
 // Used for dllexported symbols.
 ErrorOr<Export> parseExport(StringRef Arg);
 std::error_code fixupExports();

Modified: lld/trunk/COFF/DriverUtils.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/DriverUtils.cpp?rev=239978&r1=239977&r2=239978&view=diff
==============================================================================
--- lld/trunk/COFF/DriverUtils.cpp (original)
+++ lld/trunk/COFF/DriverUtils.cpp Wed Jun 17 19:12:42 2015
@@ -60,7 +60,10 @@ public:
     Args.insert(Args.begin(), Exe);
     Args.push_back(nullptr);
     if (llvm::sys::ExecuteAndWait(Args[0], Args.data()) != 0) {
-      llvm::errs() << Exe << " failed\n";
+      for (const char *S : Args)
+        if (S)
+          llvm::errs() << S << " ";
+      llvm::errs() << "failed\n";
       return make_error_code(LLDError::InvalidOption);
     }
     return std::error_code();
@@ -151,6 +154,163 @@ std::error_code parseSubsystem(StringRef
   return std::error_code();
 }
 
+// Parses a string in the form of "EMBED[,=<integer>]|NO".
+// Results are directly written to Config.
+std::error_code parseManifest(StringRef Arg) {
+  if (Arg.equals_lower("no")) {
+    Config->Manifest = Configuration::No;
+    return std::error_code();
+  }
+  if (!Arg.startswith_lower("embed"))
+    return make_error_code(LLDError::InvalidOption);
+  Config->Manifest = Configuration::Embed;
+  Arg = Arg.substr(strlen("embed"));
+  if (Arg.empty())
+    return std::error_code();
+  if (!Arg.startswith_lower(",id="))
+    return make_error_code(LLDError::InvalidOption);
+  Arg = Arg.substr(strlen(",id="));
+  if (Arg.getAsInteger(0, Config->ManifestID))
+    return make_error_code(LLDError::InvalidOption);
+  return std::error_code();
+}
+
+// Parses a string in the form of "level=<string>|uiAccess=<string>|NO".
+// Results are directly written to Config.
+std::error_code parseManifestUAC(StringRef Arg) {
+  if (Arg.equals_lower("no")) {
+    Config->ManifestUAC = false;
+    return std::error_code();
+  }
+  for (;;) {
+    Arg = Arg.ltrim();
+    if (Arg.empty())
+      return std::error_code();
+    if (Arg.startswith_lower("level=")) {
+      Arg = Arg.substr(strlen("level="));
+      std::tie(Config->ManifestLevel, Arg) = Arg.split(" ");
+      continue;
+    }
+    if (Arg.startswith_lower("uiaccess=")) {
+      Arg = Arg.substr(strlen("uiaccess="));
+      std::tie(Config->ManifestUIAccess, Arg) = Arg.split(" ");
+      continue;
+    }
+    return make_error_code(LLDError::InvalidOption);
+  }
+}
+
+// Quote each line with "". Existing double-quote is converted
+// to two double-quotes.
+static void quoteAndPrint(raw_ostream &Out, StringRef S) {
+  while (!S.empty()) {
+    StringRef Line;
+    std::tie(Line, S) = S.split("\n");
+    if (Line.empty())
+      continue;
+    Out << '\"';
+    for (int I = 0, E = Line.size(); I != E; ++I) {
+      if (Line[I] == '\"') {
+        Out << "\"\"";
+      } else {
+        Out << Line[I];
+      }
+    }
+    Out << "\"\n";
+  }
+}
+
+// Create a manifest file contents.
+static std::string createManifestXml() {
+  std::string S;
+  llvm::raw_string_ostream OS(S);
+  // Emit the XML. Note that we do *not* verify that the XML attributes are
+  // syntactically correct. This is intentional for link.exe compatibility.
+  OS << "<?xml version=\"1.0\" standalone=\"yes\"?>\n"
+     << "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\"\n"
+     << "          manifestVersion=\"1.0\">\n";
+  if (Config->ManifestUAC) {
+    OS << "  <trustInfo>\n"
+       << "    <security>\n"
+       << "      <requestedPrivileges>\n"
+       << "         <requestedExecutionLevel level=" << Config->ManifestLevel
+       << " uiAccess=" << Config->ManifestUIAccess << "/>\n"
+       << "      </requestedPrivileges>\n"
+       << "    </security>\n"
+       << "  </trustInfo>\n";
+    if (!Config->ManifestDependency.empty()) {
+      OS << "  <dependency>\n"
+         << "    <dependentAssembly>\n"
+         << "      <assemblyIdentity " << Config->ManifestDependency << " />\n"
+         << "    </dependentAssembly>\n"
+         << "  </dependency>\n";
+    }
+  }
+  OS << "</assembly>\n";
+  OS.flush();
+  return S;
+}
+
+// Create a resource file containing a manifest XML.
+ErrorOr<std::unique_ptr<MemoryBuffer>> createManifestRes() {
+  // Create a temporary file for the resource script file.
+  SmallString<128> RCPath;
+  if (sys::fs::createTemporaryFile("tmp", "rc", RCPath)) {
+    llvm::errs() << "cannot create a temporary file\n";
+    return make_error_code(LLDError::InvalidOption);
+  }
+  FileRemover RCRemover(RCPath);
+
+  // Open the temporary file for writing.
+  std::error_code EC;
+  llvm::raw_fd_ostream Out(RCPath, EC, sys::fs::F_Text);
+  if (EC) {
+    llvm::errs() << "failed to open " << RCPath << ": " << EC.message() << "\n";
+    return make_error_code(LLDError::InvalidOption);
+  }
+
+  // Write resource script to the RC file.
+  Out << "#define LANG_ENGLISH 9\n"
+      << "#define SUBLANG_DEFAULT 1\n"
+      << "#define APP_MANIFEST " << Config->ManifestID << "\n"
+      << "#define RT_MANIFEST 24\n"
+      << "LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT\n"
+      << "APP_MANIFEST RT_MANIFEST {\n";
+  quoteAndPrint(Out, createManifestXml());
+  Out << "}\n";
+  Out.close();
+
+  // Create output resource file.
+  SmallString<128> ResPath;
+  if (sys::fs::createTemporaryFile("tmp", "res", ResPath)) {
+    llvm::errs() << "cannot create a temporary file\n";
+    return make_error_code(LLDError::InvalidOption);
+  }
+
+  Executor E("rc.exe");
+  E.add("/fo");
+  E.add(ResPath.str());
+  E.add("/nologo");
+  E.add(RCPath.str());
+  if (auto EC = E.run())
+    return EC;
+  return MemoryBuffer::getFile(ResPath);
+}
+
+std::error_code createSideBySideManifest() {
+  std::string Path = Config->ManifestFile;
+  if (Path == "")
+    Path = (Twine(Config->OutputFile) + ".manifest").str();
+  std::error_code EC;
+  llvm::raw_fd_ostream Out(Path, EC, llvm::sys::fs::F_Text);
+  if (EC) {
+    llvm::errs() << EC.message() << "\n";
+    return EC;
+  }
+  Out << createManifestXml();
+  return std::error_code();
+}
+
 // Parse a string in the form of
 // "<name>[=<internalname>][, at ordinal[,NONAME]][,DATA][,PRIVATE]".
 // Used for parsing /export arguments.

Added: lld/trunk/test/COFF/manifest.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/manifest.test?rev=239978&view=auto
==============================================================================
--- lld/trunk/test/COFF/manifest.test (added)
+++ lld/trunk/test/COFF/manifest.test Wed Jun 17 19:12:42 2015
@@ -0,0 +1,59 @@
+# RUN: yaml2obj %p/Inputs/ret42.yaml > %t.obj
+
+# RUN: lld -flavor link2 /out:%t.exe %t.obj
+# RUN: FileCheck -check-prefix=MANIFEST %s < %t.exe.manifest
+
+MANIFEST: <?xml version="1.0" standalone="yes"?>
+MANIFEST: <assembly xmlns="urn:schemas-microsoft-com:asm.v1"
+MANIFEST:           manifestVersion="1.0">
+MANIFEST:   <trustInfo>
+MANIFEST:     <security>
+MANIFEST:       <requestedPrivileges>
+MANIFEST:          <requestedExecutionLevel level='asInvoker' uiAccess='false'/>
+MANIFEST:       </requestedPrivileges>
+MANIFEST:     </security>
+MANIFEST:   </trustInfo>
+MANIFEST: </assembly>
+
+# RUN: lld -flavor link2 /out:%t.exe /manifestuac:"level='requireAdministrator' uiAccess='true'" %t.obj
+# RUN: FileCheck -check-prefix=UAC %s < %t.exe.manifest
+
+UAC: <?xml version="1.0" standalone="yes"?>
+UAC: <assembly xmlns="urn:schemas-microsoft-com:asm.v1"
+UAC:           manifestVersion="1.0">
+UAC:   <trustInfo>
+UAC:     <security>
+UAC:       <requestedPrivileges>
+UAC:          <requestedExecutionLevel level='requireAdministrator' uiAccess='true'/>
+UAC:       </requestedPrivileges>
+UAC:     </security>
+UAC:   </trustInfo>
+UAC: </assembly>
+
+# RUN: lld -flavor link2 /out:%t.exe /manifestdependency:"foo='bar'" %t.obj
+# RUN: FileCheck -check-prefix=DEPENDENCY %s < %t.exe.manifest
+
+DEPENDENCY: <?xml version="1.0" standalone="yes"?>
+DEPENDENCY: <assembly xmlns="urn:schemas-microsoft-com:asm.v1"
+DEPENDENCY:           manifestVersion="1.0">
+DEPENDENCY:   <trustInfo>
+DEPENDENCY:     <security>
+DEPENDENCY:       <requestedPrivileges>
+DEPENDENCY:          <requestedExecutionLevel level='asInvoker' uiAccess='false'/>
+DEPENDENCY:       </requestedPrivileges>
+DEPENDENCY:     </security>
+DEPENDENCY:   </trustInfo>
+DEPENDENCY:   <dependency>
+DEPENDENCY:     <dependentAssembly>
+DEPENDENCY:       <assemblyIdentity foo='bar' />
+DEPENDENCY:     </dependentAssembly>
+DEPENDENCY:   </dependency>
+DEPENDENCY: </assembly>
+
+# RUN: lld -flavor link2 /out:%t.exe /manifestuac:no %t.obj
+# RUN: FileCheck -check-prefix=NOUAC %s < %t.exe.manifest
+
+NOUAC: <?xml version="1.0" standalone="yes"?>
+NOUAC: <assembly xmlns="urn:schemas-microsoft-com:asm.v1"
+NOUAC:           manifestVersion="1.0">
+NOUAC: </assembly>





More information about the llvm-commits mailing list