[PECOFF] Support delay-load import table for x86

This patch creates the import address table and sets its
address to the delay-load import table. This also creates
wrapper functions for __delayLoadHelper2.

x86 only for now.

llvm-svn: 219948
This commit is contained in:
Rui Ueyama 2014-10-16 19:30:44 +00:00
parent ec81c0b40d
commit b38b96ab4c
9 changed files with 130 additions and 10 deletions

View File

@ -262,6 +262,10 @@ public:
std::vector<ExportDesc> &getDllExports() { return _dllExports; }
const std::vector<ExportDesc> &getDllExports() const { return _dllExports; }
StringRef getDelayLoadHelperName() const {
return is64Bit() ? "__delayLoadHelper2" : "___delayLoadHelper2@8";
}
StringRef allocate(StringRef ref) const {
_allocMutex.lock();
char *x = _allocator.Allocate<char>(ref.size() + 1);

View File

@ -1226,6 +1226,7 @@ bool WinLinkDriver::parse(int argc, const char *argv[],
break;
case OPT_delayload:
ctx.addInitialUndefinedSymbol(ctx.getDelayLoadHelperName());
ctx.addDelayLoadDLL(inputArg->getValue());
break;

View File

@ -131,30 +131,101 @@ std::vector<uint8_t> DelayImportDirectoryAtom::createContent() {
return r;
}
// Find "___delayLoadHelper2@8" (or "__delayLoadHelper2" on x64).
// This is not efficient but should be OK for now.
static const Atom *
findDelayLoadHelper(MutableFile &file, const PECOFFLinkingContext &ctx) {
StringRef sym = ctx.getDelayLoadHelperName();
for (const DefinedAtom *atom : file.defined())
if (atom->name() == sym)
return atom;
std::string msg = (sym + " was not found").str();
llvm_unreachable(msg.c_str());
}
// Create the data referred by the delay-import table.
void DelayImportDirectoryAtom::addRelocations(
IdataContext &context, StringRef loadName,
const std::vector<COFFSharedLibraryAtom *> &sharedAtoms) {
// "ModuleHandle" field
auto *hmodule = new (_alloc) DelayImportHModuleAtom(context);
// "ModuleHandle" field. This points to an array of pointer-size data
// in ".data" section. Initially the array is initialized with zero.
// The delay-load import helper will set DLL base address at runtime.
auto *hmodule = new (_alloc) DelayImportAddressAtom(context);
addDir32NBReloc(this, hmodule, context.ctx.getMachineType(),
offsetof(delay_import_directory_table_entry, ModuleHandle));
// "NameTable" field
// "NameTable" field. The data structure of this field is the same
// as (non-delay) import table's Import Lookup Table. Contains
// imported function names. This is a parallel array of AddressTable
// field.
std::vector<ImportTableEntryAtom *> nameTable =
createImportTableAtoms(context, sharedAtoms, true, ".didat", _alloc);
createImportTableAtoms(context, sharedAtoms, false, ".didat", _alloc);
addDir32NBReloc(
this, nameTable[0], context.ctx.getMachineType(),
offsetof(delay_import_directory_table_entry, DelayImportNameTable));
// "Name" field
// "Name" field. This points to the NUL-terminated DLL name string.
auto *name = new (_alloc)
COFFStringAtom(context.dummyFile, context.dummyFile.getNextOrdinal(),
".didat", loadName);
context.file.addAtom(*name);
addDir32NBReloc(this, name, context.ctx.getMachineType(),
offsetof(delay_import_directory_table_entry, Name));
// TODO: emit other fields
// "AddressTable" field. This points to an array of pointers, which
// in turn pointing to delay-load functions.
std::vector<DelayImportAddressAtom *> addrTable;
for (int i = 0, e = sharedAtoms.size() + 1; i < e; ++i)
addrTable.push_back(new (_alloc) DelayImportAddressAtom(context));
for (int i = 0, e = sharedAtoms.size(); i < e; ++i)
sharedAtoms[i]->setImportTableEntry(addrTable[i]);
addDir32NBReloc(
this, addrTable[0], context.ctx.getMachineType(),
offsetof(delay_import_directory_table_entry, DelayImportAddressTable));
const Atom *delayLoadHelper = findDelayLoadHelper(context.file, context.ctx);
for (int i = 0, e = sharedAtoms.size(); i < e; ++i) {
const DefinedAtom *loader = new (_alloc) DelayLoaderAtom(
context, addrTable[i], this, delayLoadHelper);
addDir32Reloc(addrTable[i], loader, context.ctx.getMachineType(), 0);
}
}
DelayLoaderAtom::DelayLoaderAtom(IdataContext &context, const Atom *impAtom,
const Atom *descAtom, const Atom *delayLoadHelperAtom)
: IdataAtom(context, createContent()) {
MachineTypes machine = context.ctx.getMachineType();
addDir32Reloc(this, impAtom, machine, 3);
addDir32Reloc(this, descAtom, machine, 8);
addRel32Reloc(this, delayLoadHelperAtom, machine, 13);
}
// DelayLoaderAtom contains a wrapper function for __delayLoadHelper2.
//
// __delayLoadHelper2 takes two pointers: a pointer to the delay-load
// table descripter and a pointer to _imp_ symbol for the function
// to be resolved.
//
// __delayLoadHelper2 looks at the table descriptor to know the DLL
// name, calls dlopen()-like function to load it, resolves all
// imported symbols, and then writes the resolved addresses to the
// import address table. It returns a pointer to the resolved
// function.
//
// __delayLoadHelper2 is defined in delayimp.lib.
std::vector<uint8_t> DelayLoaderAtom::createContent() const {
// NB: x86 only for now. ECX and EDX are caller-save.
static const uint8_t array[] = {
0x51, // push ecx
0x52, // push edx
0x68, 0, 0, 0, 0, // push offset ___imp__<FUNCNAME>
0x68, 0, 0, 0, 0, // push offset ___DELAY_IMPORT_DESCRIPTOR_<DLLNAME>_dll
0xE8, 0, 0, 0, 0, // call ___delayLoadHelper2@8
0x5A, // pop edx
0x59, // pop ecx
0xFF, 0xE0, // jmp eax
};
return std::vector<uint8_t>(array, array + sizeof(array));
}
} // namespace idata

View File

@ -161,9 +161,9 @@ private:
}
};
class DelayImportHModuleAtom : public IdataAtom {
class DelayImportAddressAtom : public IdataAtom {
public:
explicit DelayImportHModuleAtom(IdataContext &context)
explicit DelayImportAddressAtom(IdataContext &context)
: IdataAtom(context, createContent(context.ctx)) {}
StringRef customSectionName() const override { return ".data"; }
ContentPermissions permissions() const override { return permRW_; }
@ -175,6 +175,19 @@ private:
}
};
// DelayLoaderAtom contains a wrapper function for __delayLoadHelper2.
class DelayLoaderAtom : public IdataAtom {
public:
DelayLoaderAtom(IdataContext &context, const Atom *impAtom,
const Atom *descAtom, const Atom *delayLoadHelperAtom);
StringRef customSectionName() const override { return ".text"; }
ContentPermissions permissions() const override { return permR_X; }
Alignment alignment() const override { return Alignment(0); }
private:
std::vector<uint8_t> createContent() const;
};
} // namespace idata
class IdataPass : public lld::Pass {

View File

@ -55,5 +55,21 @@ void addDir32NBReloc(COFFBaseDefinedAtom *atom, const Atom *target,
}
}
void addRel32Reloc(COFFBaseDefinedAtom *atom, const Atom *target,
llvm::COFF::MachineTypes machine, size_t offsetInAtom) {
switch (machine) {
case llvm::COFF::IMAGE_FILE_MACHINE_I386:
addReloc(atom, target, offsetInAtom, Reference::KindArch::x86,
llvm::COFF::IMAGE_REL_I386_REL32);
return;
case llvm::COFF::IMAGE_FILE_MACHINE_AMD64:
addReloc(atom, target, offsetInAtom, Reference::KindArch::x86_64,
llvm::COFF::IMAGE_REL_AMD64_REL32);
return;
default:
llvm_unreachable("unsupported machine type");
}
}
} // end namespace pecoff
} // end namespace lld

View File

@ -22,6 +22,9 @@ void addDir32Reloc(COFFBaseDefinedAtom *atom, const Atom *target,
void addDir32NBReloc(COFFBaseDefinedAtom *atom, const Atom *target,
llvm::COFF::MachineTypes machine, size_t offsetInAtom);
void addRel32Reloc(COFFBaseDefinedAtom *atom, const Atom *target,
llvm::COFF::MachineTypes machine, size_t offsetInAtom);
} // namespace pecoff
} // namespace lld

View File

@ -48,6 +48,12 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: "__delayLoadHelper2"
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: __imp_var
Value: 0
SectionNumber: 0

View File

@ -48,6 +48,12 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: "___delayLoadHelper2@8"
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: __imp__var
Value: 0
SectionNumber: 0

View File

@ -13,7 +13,7 @@ X86: DelayImport {
X86-NEXT: Name: vars.dll
X86-NEXT: Attributes: 0x1
X86-NEXT: ModuleHandle: 0x1000
X86-NEXT: ImportAddressTable: 0x0
X86-NEXT: ImportAddressTable: 0x1008
X86-NEXT: ImportNameTable: 0x2000
X86-NEXT: BoundDelayImportTable: 0x0
X86-NEXT: UnloadDelayImportTable: 0x0
@ -26,7 +26,7 @@ X64: DelayImport {
X64-NEXT: Name: vars64.dll
X64-NEXT: Attributes: 0x1
X64-NEXT: ModuleHandle: 0x1000
X64-NEXT: ImportAddressTable: 0x0
X64-NEXT: ImportAddressTable: 0x1008
X64-NEXT: ImportNameTable: 0x2000
X64-NEXT: BoundDelayImportTable: 0x0
X64-NEXT: UnloadDelayImportTable: 0x0