[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