COFF: Create import library files.

On Windows, we have to create a .lib file for each .dll.
When linking against DLLs, the linker doesn't use the DLL files,
but instead read a list of dllexported symbols from corresponding
lib files.

A library file containing descriptors of a DLL is called an
import library file.

lib.exe has a feature to create an import library file from a
module-definition file. In this patch, we create a module-definition
file and pass that to lib.exe.

We eventually want to create an import library file by ourselves
to eliminate dependency to lib.exe. For now, we just use the MSVC
tool.

llvm-svn: 239937
This commit is contained in:
Rui Ueyama 2015-06-17 20:40:43 +00:00
parent 63e09bf70b
commit 151d862d97
5 changed files with 137 additions and 0 deletions

View File

@ -500,6 +500,11 @@ bool LinkerDriver::link(int Argc, const char *Argv[]) {
}
}
// Windows specific -- when we are creating a .dll file, we also
// need to create a .lib file.
if (!Config->Exports.empty())
writeImportLibrary();
// Windows specific -- fix up dllexported symbols.
if (!Config->Exports.empty()) {
for (Export &E : Config->Exports)

View File

@ -98,6 +98,7 @@ private:
};
std::error_code parseModuleDefs(MemoryBufferRef MB);
std::error_code writeImportLibrary();
// Functions below this line are defined in DriverUtils.cpp.

View File

@ -24,6 +24,8 @@
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/raw_ostream.h"
@ -269,6 +271,75 @@ convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) {
return MemoryBuffer::getFile(Path);
}
static std::string writeToTempFile(StringRef Contents) {
SmallString<128> Path;
int FD;
if (llvm::sys::fs::createTemporaryFile("tmp", "def", FD, Path)) {
llvm::errs() << "failed to create a temporary file\n";
return "";
}
llvm::raw_fd_ostream OS(FD, /*shouldClose*/ true);
OS << Contents;
return Path.str();
}
/// Creates a .def file containing the list of exported symbols.
static std::string createModuleDefinitionFile() {
std::string S;
llvm::raw_string_ostream OS(S);
OS << "LIBRARY \"" << llvm::sys::path::filename(Config->OutputFile) << "\"\n"
<< "EXPORTS\n";
for (Export &E : Config->Exports) {
OS << " " << E.ExtName;
if (E.Ordinal > 0)
OS << " @" << E.Ordinal;
if (E.Noname)
OS << " NONAME";
if (E.Data)
OS << " DATA";
if (E.Private)
OS << " PRIVATE";
OS << "\n";
}
OS.flush();
return S;
}
// Creates a .def file and runs lib.exe on it to create an import library.
std::error_code writeImportLibrary() {
std::string Prog = "lib.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());
std::string Contents = createModuleDefinitionFile();
StringRef Def = S.save(StringRef(writeToTempFile(Contents)));
llvm::FileRemover TempFile(Def);
SmallString<128> Out = StringRef(Config->OutputFile);
sys::path::replace_extension(Out, ".lib");
std::vector<const char *> Args;
Args.push_back(Exe);
Args.push_back("/nologo");
Args.push_back("/machine:x64");
Args.push_back(S.save("/def:" + Def));
Args.push_back(S.save("/out:" + Out));
Args.push_back(nullptr);
if (sys::ExecuteAndWait(Exe, Args.data()) != 0) {
llvm::errs() << Exe << " failed\n";
return make_error_code(LLDError::InvalidOption);
}
return std::error_code();
}
// Create OptTable
// Create prefix string literals used in Options.td

View File

@ -0,0 +1,41 @@
---
header:
Machine: IMAGE_FILE_MACHINE_AMD64
Characteristics: []
sections:
- Name: .text
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 4
SectionData: 0000000000000000
symbols:
- Name: .text
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
SectionDefinition:
Length: 8
NumberOfRelocations: 0
NumberOfLinenumbers: 0
CheckSum: 0
Number: 0
- Name: mainCRTStartup
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: exportfn1
Value: 0
SectionNumber: 0
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: exportfn2
Value: 0
SectionNumber: 0
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
...

19
lld/test/COFF/dll.test Normal file
View File

@ -0,0 +1,19 @@
# REQUIRES: winres
# RUN: yaml2obj < %p/Inputs/export.yaml > %t.obj
# RUN: lld -flavor link2 /out:%t.dll /dll %t.obj /export:exportfn1 /export:exportfn2
# RUN: llvm-objdump -p %t.dll | FileCheck -check-prefix=EXPORT %s
EXPORT: Export Table:
EXPORT: DLL name: dll.test.tmp.dll
EXPORT: Ordinal RVA Name
EXPORT-NEXT: 0 0
EXPORT-NEXT: 1 0x1008 exportfn1
EXPORT-NEXT: 2 0x1010 exportfn2
# RUN: yaml2obj < %p/Inputs/import.yaml > %t2.obj
# RUN: lld -flavor link2 /out:%t2.exe %t2.obj %t.lib
# RUN: llvm-readobj -coff-imports %t2.exe | FileCheck -check-prefix=IMPORT %s
IMPORT: Symbol: exportfn1
IMPORT: Symbol: exportfn2