#include "llvm/Support/Process.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
#include <memory>
using namespace llvm;
// 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
// 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));
}
}
// 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;
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;
}
}
#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>
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