From b38b96ab4c491b914835d005532e8f8e4842c77a Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Thu, 16 Oct 2014 19:30:44 +0000 Subject: [PATCH] [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 --- .../lld/ReaderWriter/PECOFFLinkingContext.h | 4 ++ lld/lib/Driver/WinLinkDriver.cpp | 1 + lld/lib/ReaderWriter/PECOFF/IdataPass.cpp | 83 ++++++++++++++++++++-- lld/lib/ReaderWriter/PECOFF/IdataPass.h | 17 ++++- lld/lib/ReaderWriter/PECOFF/Pass.cpp | 16 +++++ lld/lib/ReaderWriter/PECOFF/Pass.h | 3 + lld/test/pecoff/Inputs/vars-main-x64.obj.yaml | 6 ++ lld/test/pecoff/Inputs/vars-main-x86.obj.yaml | 6 ++ lld/test/pecoff/delayimport.test | 4 +- 9 files changed, 130 insertions(+), 10 deletions(-) diff --git a/lld/include/lld/ReaderWriter/PECOFFLinkingContext.h b/lld/include/lld/ReaderWriter/PECOFFLinkingContext.h index 98b9a2e..b6d416f 100644 --- a/lld/include/lld/ReaderWriter/PECOFFLinkingContext.h +++ b/lld/include/lld/ReaderWriter/PECOFFLinkingContext.h @@ -262,6 +262,10 @@ public: std::vector &getDllExports() { return _dllExports; } const std::vector &getDllExports() const { return _dllExports; } + StringRef getDelayLoadHelperName() const { + return is64Bit() ? "__delayLoadHelper2" : "___delayLoadHelper2@8"; + } + StringRef allocate(StringRef ref) const { _allocMutex.lock(); char *x = _allocator.Allocate(ref.size() + 1); diff --git a/lld/lib/Driver/WinLinkDriver.cpp b/lld/lib/Driver/WinLinkDriver.cpp index eb4c83c..cdd9af1 100644 --- a/lld/lib/Driver/WinLinkDriver.cpp +++ b/lld/lib/Driver/WinLinkDriver.cpp @@ -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; diff --git a/lld/lib/ReaderWriter/PECOFF/IdataPass.cpp b/lld/lib/ReaderWriter/PECOFF/IdataPass.cpp index 45900bb..5db2372 100644 --- a/lld/lib/ReaderWriter/PECOFF/IdataPass.cpp +++ b/lld/lib/ReaderWriter/PECOFF/IdataPass.cpp @@ -131,30 +131,101 @@ std::vector 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 &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 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 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 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__ + 0x68, 0, 0, 0, 0, // push offset ___DELAY_IMPORT_DESCRIPTOR__dll + 0xE8, 0, 0, 0, 0, // call ___delayLoadHelper2@8 + 0x5A, // pop edx + 0x59, // pop ecx + 0xFF, 0xE0, // jmp eax + }; + return std::vector(array, array + sizeof(array)); } } // namespace idata diff --git a/lld/lib/ReaderWriter/PECOFF/IdataPass.h b/lld/lib/ReaderWriter/PECOFF/IdataPass.h index 70f882b..4e01d10 100644 --- a/lld/lib/ReaderWriter/PECOFF/IdataPass.h +++ b/lld/lib/ReaderWriter/PECOFF/IdataPass.h @@ -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 createContent() const; +}; + } // namespace idata class IdataPass : public lld::Pass { diff --git a/lld/lib/ReaderWriter/PECOFF/Pass.cpp b/lld/lib/ReaderWriter/PECOFF/Pass.cpp index e756bf5..55c3ef8 100644 --- a/lld/lib/ReaderWriter/PECOFF/Pass.cpp +++ b/lld/lib/ReaderWriter/PECOFF/Pass.cpp @@ -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 diff --git a/lld/lib/ReaderWriter/PECOFF/Pass.h b/lld/lib/ReaderWriter/PECOFF/Pass.h index e0cdee3..290aed4 100644 --- a/lld/lib/ReaderWriter/PECOFF/Pass.h +++ b/lld/lib/ReaderWriter/PECOFF/Pass.h @@ -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 diff --git a/lld/test/pecoff/Inputs/vars-main-x64.obj.yaml b/lld/test/pecoff/Inputs/vars-main-x64.obj.yaml index 7f82fb6..c888c28 100644 --- a/lld/test/pecoff/Inputs/vars-main-x64.obj.yaml +++ b/lld/test/pecoff/Inputs/vars-main-x64.obj.yaml @@ -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 diff --git a/lld/test/pecoff/Inputs/vars-main-x86.obj.yaml b/lld/test/pecoff/Inputs/vars-main-x86.obj.yaml index 2395a3d..4bee575 100644 --- a/lld/test/pecoff/Inputs/vars-main-x86.obj.yaml +++ b/lld/test/pecoff/Inputs/vars-main-x86.obj.yaml @@ -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 diff --git a/lld/test/pecoff/delayimport.test b/lld/test/pecoff/delayimport.test index 21cc929..946e32a 100644 --- a/lld/test/pecoff/delayimport.test +++ b/lld/test/pecoff/delayimport.test @@ -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 -- 2.7.4