[lld] r239704 - COFF: Support Windows resource files.

Rui Ueyama ruiu at google.com
Sun Jun 14 14:50:50 PDT 2015


Author: ruiu
Date: Sun Jun 14 16:50:50 2015
New Revision: 239704

URL: http://llvm.org/viewvc/llvm-project?rev=239704&view=rev
Log:
COFF: Support Windows resource files.

Resource files are data files containing i18n messages, icon images, etc.
MSVC has a tool to convert a resource file to a regular COFF file so that
you can just link that file to embed resources to an executable.

However, you can directly pass resource files to the linker. If you do that,
the linker invokes the tool automatically. This patch implements that feature.

Added:
    lld/trunk/test/COFF/Inputs/resource.res
    lld/trunk/test/COFF/resource.test
Modified:
    lld/trunk/COFF/Chunks.cpp
    lld/trunk/COFF/Driver.cpp
    lld/trunk/COFF/Driver.h
    lld/trunk/COFF/DriverUtils.cpp
    lld/trunk/COFF/Writer.cpp

Modified: lld/trunk/COFF/Chunks.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Chunks.cpp?rev=239704&r1=239703&r2=239704&view=diff
==============================================================================
--- lld/trunk/COFF/Chunks.cpp (original)
+++ lld/trunk/COFF/Chunks.cpp Sun Jun 14 16:50:50 2015
@@ -31,9 +31,10 @@ SectionChunk::SectionChunk(ObjectFile *F
   // Initialize SectionName.
   File->getCOFFObj()->getSectionName(Header, SectionName);
 
-  // Bit [20:24] contains section alignment.
-  unsigned Shift = ((Header->Characteristics & 0xF00000) >> 20) - 1;
-  Align = uint32_t(1) << Shift;
+  // Bit [20:24] contains section alignment. Both 0 and 1 mean alignment 1.
+  unsigned Shift = (Header->Characteristics >> 20) & 0xF;
+  if (Shift > 0)
+    Align = uint32_t(1) << (Shift - 1);
 
   // When a new chunk is created, we don't if if it's going to make it
   // to the final output. Initially all sections are unmarked in terms

Modified: lld/trunk/COFF/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Driver.cpp?rev=239704&r1=239703&r2=239704&view=diff
==============================================================================
--- lld/trunk/COFF/Driver.cpp (original)
+++ lld/trunk/COFF/Driver.cpp Sun Jun 14 16:50:50 2015
@@ -25,6 +25,7 @@
 #include "llvm/Support/Process.h"
 #include "llvm/Support/TargetSelect.h"
 #include "llvm/Support/raw_ostream.h"
+#include <algorithm>
 #include <memory>
 
 using namespace llvm;
@@ -58,23 +59,26 @@ static std::string getOutputPath(StringR
 
 // Opens a file. Path has to be resolved already.
 // Newly created memory buffers are owned by this driver.
-ErrorOr<std::unique_ptr<InputFile>> LinkerDriver::openFile(StringRef Path) {
+ErrorOr<MemoryBufferRef> LinkerDriver::openFile(StringRef Path) {
   auto MBOrErr = MemoryBuffer::getFile(Path);
   if (auto EC = MBOrErr.getError())
     return EC;
   std::unique_ptr<MemoryBuffer> MB = std::move(MBOrErr.get());
   MemoryBufferRef MBRef = MB->getMemBufferRef();
   OwningMBs.push_back(std::move(MB)); // take ownership
+  return MBRef;
+}
 
+static std::unique_ptr<InputFile> createFile(MemoryBufferRef MB) {
   // File type is detected by contents, not by file extension.
-  file_magic Magic = identify_magic(MBRef.getBuffer());
+  file_magic Magic = identify_magic(MB.getBuffer());
   if (Magic == file_magic::archive)
-    return std::unique_ptr<InputFile>(new ArchiveFile(MBRef));
+    return std::unique_ptr<InputFile>(new ArchiveFile(MB));
   if (Magic == file_magic::bitcode)
-    return std::unique_ptr<InputFile>(new BitcodeFile(MBRef));
+    return std::unique_ptr<InputFile>(new BitcodeFile(MB));
   if (Config->OutputFile == "")
-    Config->OutputFile = getOutputPath(Path);
-  return std::unique_ptr<InputFile>(new ObjectFile(MBRef));
+    Config->OutputFile = getOutputPath(MB.getBufferIdentifier());
+  return std::unique_ptr<InputFile>(new ObjectFile(MB));
 }
 
 // Parses .drectve section contents and returns a list of files
@@ -94,10 +98,10 @@ LinkerDriver::parseDirectives(StringRef
   // Handle /defaultlib
   for (auto *Arg : Args->filtered(OPT_defaultlib)) {
     if (Optional<StringRef> Path = findLib(Arg->getValue())) {
-      auto FileOrErr = openFile(*Path);
-      if (auto EC = FileOrErr.getError())
+      ErrorOr<MemoryBufferRef> MBOrErr = openFile(*Path);
+      if (auto EC = MBOrErr.getError())
         return EC;
-      std::unique_ptr<InputFile> File = std::move(FileOrErr.get());
+      std::unique_ptr<InputFile> File = createFile(MBOrErr.get());
       Res->push_back(std::move(File));
     }
   }
@@ -312,13 +316,22 @@ bool LinkerDriver::link(int Argc, const
 
   // Create a list of input files. Files can be given as arguments
   // for /defaultlib option.
-  std::vector<StringRef> Inputs;
+  std::vector<StringRef> InputPaths;
+  std::vector<MemoryBufferRef> Inputs;
   for (auto *Arg : Args->filtered(OPT_INPUT))
     if (Optional<StringRef> Path = findFile(Arg->getValue()))
-      Inputs.push_back(*Path);
+      InputPaths.push_back(*Path);
   for (auto *Arg : Args->filtered(OPT_defaultlib))
     if (Optional<StringRef> Path = findLib(Arg->getValue()))
-      Inputs.push_back(*Path);
+      InputPaths.push_back(*Path);
+  for (StringRef Path : InputPaths) {
+    ErrorOr<MemoryBufferRef> MBOrErr = openFile(Path);
+    if (auto EC = MBOrErr.getError()) {
+      llvm::errs() << "cannot open " << Path << ": " << EC.message() << "\n";
+      return false;
+    }
+    Inputs.push_back(MBOrErr.get());
+  }
 
   // Create a symbol table.
   SymbolTable Symtab;
@@ -331,19 +344,32 @@ bool LinkerDriver::link(int Argc, const
     Config->GCRoots.insert(Sym);
   }
 
+  // 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.
+  auto IsResource = [](MemoryBufferRef MB) {
+    return identify_magic(MB.getBuffer()) == file_magic::windows_resource;
+  };
+  auto It = std::stable_partition(Inputs.begin(), Inputs.end(), IsResource);
+  if (It != Inputs.begin()) {
+    std::vector<MemoryBufferRef> Files(Inputs.begin(), It);
+    auto MBOrErr = convertResToCOFF(Files);
+    if (MBOrErr.getError())
+      return false;
+    std::unique_ptr<MemoryBuffer> MB = std::move(MBOrErr.get());
+    Inputs = std::vector<MemoryBufferRef>(It, Inputs.end());
+    Inputs.push_back(MB->getMemBufferRef());
+    OwningMBs.push_back(std::move(MB)); // take ownership
+  }
+
   // Parse all input files and put all symbols to the symbol table.
   // The symbol table will take care of name resolution.
-  for (StringRef Path : Inputs) {
-    auto FileOrErr = openFile(Path);
-    if (auto EC = FileOrErr.getError()) {
-      llvm::errs() << Path << ": " << EC.message() << "\n";
-      return false;
-    }
-    std::unique_ptr<InputFile> File = std::move(FileOrErr.get());
+  for (MemoryBufferRef MB : Inputs) {
+    std::unique_ptr<InputFile> File = createFile(MB);
     if (Config->Verbose)
       llvm::outs() << "Reading " << File->getName() << "\n";
     if (auto EC = Symtab.addFile(std::move(File))) {
-      llvm::errs() << Path << ": " << EC.message() << "\n";
+      llvm::errs() << File->getName() << ": " << EC.message() << "\n";
       return false;
     }
   }

Modified: lld/trunk/COFF/Driver.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Driver.h?rev=239704&r1=239703&r2=239704&view=diff
==============================================================================
--- lld/trunk/COFF/Driver.h (original)
+++ lld/trunk/COFF/Driver.h Sun Jun 14 16:50:50 2015
@@ -76,7 +76,7 @@ private:
   ArgParser Parser;
 
   // Opens a file. Path has to be resolved already.
-  ErrorOr<std::unique_ptr<InputFile>> openFile(StringRef Path);
+  ErrorOr<MemoryBufferRef> openFile(StringRef Path);
 
   // Searches a file from search paths.
   Optional<StringRef> findFile(StringRef Filename);
@@ -121,6 +121,11 @@ std::error_code parseSubsystem(StringRef
 // incompatible objects.
 std::error_code checkFailIfMismatch(llvm::opt::InputArgList *Args);
 
+// Convert Windows resource files (.res files) to a .obj file
+// using cvtres.exe.
+ErrorOr<std::unique_ptr<MemoryBuffer>>
+convertResToCOFF(const std::vector<MemoryBufferRef> &MBs);
+
 // Create enum with OPT_xxx values for each option in Options.td
 enum {
   OPT_INVALID = 0,

Modified: lld/trunk/COFF/DriverUtils.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/DriverUtils.cpp?rev=239704&r1=239703&r2=239704&view=diff
==============================================================================
--- lld/trunk/COFF/DriverUtils.cpp (original)
+++ lld/trunk/COFF/DriverUtils.cpp Sun Jun 14 16:50:50 2015
@@ -24,8 +24,8 @@
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/Option.h"
 #include "llvm/Support/CommandLine.h"
-#include "llvm/Support/Path.h"
 #include "llvm/Support/Process.h"
+#include "llvm/Support/Program.h"
 #include "llvm/Support/raw_ostream.h"
 #include <memory>
 
@@ -137,6 +137,45 @@ std::error_code checkFailIfMismatch(llvm
   return std::error_code();
 }
 
+// Convert Windows resource files (.res files) to a .obj file
+// using cvtres.exe.
+ErrorOr<std::unique_ptr<MemoryBuffer>>
+convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) {
+  // Find cvtres.exe.
+  std::string Prog = "cvtres.exe";
+  ErrorOr<std::string> ExeOrErr = llvm::sys::findProgramByName(Prog);
+  if (auto EC = ExeOrErr.getError()) {
+    llvm::errs() << "unable to find " << Prog << " in PATH: "
+                 << EC.message() << "\n";
+    return make_error_code(LLDError::InvalidOption);
+  }
+  llvm::BumpPtrAllocator Alloc;
+  llvm::BumpPtrStringSaver S(Alloc);
+  const char *Exe = S.save(ExeOrErr.get());
+
+  // Create an output file path.
+  SmallString<128> Path;
+  if (llvm::sys::fs::createTemporaryFile("resource", "obj", Path))
+    return make_error_code(LLDError::InvalidOption);
+
+  // Execute cvtres.exe.
+  std::vector<const char *> Args;
+  Args.push_back(Exe);
+  Args.push_back("/machine:x64");
+  Args.push_back("/readonly");
+  Args.push_back("/nologo");
+  Args.push_back(S.save("/out:" + Path));
+  for (MemoryBufferRef MB : MBs)
+    Args.push_back(S.save(MB.getBufferIdentifier()));
+  Args.push_back(nullptr);
+  llvm::errs() << "\n";
+  if (llvm::sys::ExecuteAndWait(Args[0], Args.data()) != 0) {
+    llvm::errs() << Exe << " failed\n";
+    return make_error_code(LLDError::InvalidOption);
+  }
+  return MemoryBuffer::getFile(Path);
+}
+
 // Create OptTable
 
 // Create prefix string literals used in Options.td

Modified: lld/trunk/COFF/Writer.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Writer.cpp?rev=239704&r1=239703&r2=239704&view=diff
==============================================================================
--- lld/trunk/COFF/Writer.cpp (original)
+++ lld/trunk/COFF/Writer.cpp Sun Jun 14 16:50:50 2015
@@ -281,6 +281,10 @@ void Writer::writeHeader() {
     DataDirectory[IAT].RelativeVirtualAddress = Idata->getIATRVA();
     DataDirectory[IAT].Size = Idata->getIATSize();
   }
+  if (OutputSection *Sec = findSection(".rsrc")) {
+    DataDirectory[RESOURCE_TABLE].RelativeVirtualAddress = Sec->getRVA();
+    DataDirectory[RESOURCE_TABLE].Size = Sec->getRawSize();
+  }
 
   // Section table
   // Name field in the section table is 8 byte long. Longer names need

Added: lld/trunk/test/COFF/Inputs/resource.res
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/Inputs/resource.res?rev=239704&view=auto
==============================================================================
Binary files lld/trunk/test/COFF/Inputs/resource.res (added) and lld/trunk/test/COFF/Inputs/resource.res Sun Jun 14 16:50:50 2015 differ

Added: lld/trunk/test/COFF/resource.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/resource.test?rev=239704&view=auto
==============================================================================
--- lld/trunk/test/COFF/resource.test (added)
+++ lld/trunk/test/COFF/resource.test Sun Jun 14 16:50:50 2015
@@ -0,0 +1,14 @@
+# REQUIRES: winres
+
+# RUN: yaml2obj < %p/Inputs/ret42.yaml > %t.obj
+# RUN: lld -flavor link2 /out:%t.exe %t.obj %p/Inputs/resource.res
+
+# Check if the binary contains UTF-16LE string "Hello" copied from resource.res.
+# RUN: FileCheck --check-prefix=EXE %s < %t.exe
+
+EXE: {{H.e.l.l.o}}
+
+# RUN: llvm-readobj -file-headers %t.exe | FileCheck  --check-prefix=HEADER %s
+
+HEADER: ResourceTableRVA: 0x1000
+HEADER: ResourceTableSize: 0x200





More information about the llvm-commits mailing list