[PECOFF] Support delay-load import table for x86
authorRui Ueyama <ruiu@google.com>
Thu, 16 Oct 2014 19:30:44 +0000 (19:30 +0000)
committerRui Ueyama <ruiu@google.com>
Thu, 16 Oct 2014 19:30:44 +0000 (19:30 +0000)
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/include/lld/ReaderWriter/PECOFFLinkingContext.h
lld/lib/Driver/WinLinkDriver.cpp
lld/lib/ReaderWriter/PECOFF/IdataPass.cpp
lld/lib/ReaderWriter/PECOFF/IdataPass.h
lld/lib/ReaderWriter/PECOFF/Pass.cpp
lld/lib/ReaderWriter/PECOFF/Pass.h
lld/test/pecoff/Inputs/vars-main-x64.obj.yaml
lld/test/pecoff/Inputs/vars-main-x86.obj.yaml
lld/test/pecoff/delayimport.test

index 98b9a2efe10e3c1a0aa45bf47c29fafd388c3bde..b6d416f12d16f4bad2d432119ce313a0f5aa632e 100644 (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);
index eb4c83ccc2de543cc0acf497540194f99b543741..cdd9af160d7cb0779bc4bd84870cd53a6ea54890 100644 (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;
 
index 45900bb43762b795026472688df64b0ec3955c2a..5db2372291cb83f1c4372b7c2fd65faa55e2b8df 100644 (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
index 70f882b489b306a04ca1d0caf5eb19dd1d8dba69..4e01d10371f9ee1fe7f7e3a73fa9b95838f3912a 100644 (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 {
index e756bf5e6273a7e8df09094b2f5f60a5d5b1402a..55c3ef8770effff98ecb0f8ed7740fb25607a1cd 100644 (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
index e0cdee3e8a358b804183180616679733ce9657c9..290aed4c469eaa92625458828f66ad0b98916494 100644 (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
 
index 7f82fb62f9095ed9ff8580d5d44ca188381e5ab3..c888c28e4a670eec9f618e2e004196eb57714d19 100644 (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
index 2395a3d4e569767fa3d8bd79cab878313ed4307f..4bee575ddf4c80a3e6abc41d149c750d0232eaef 100644 (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
index 21cc929a309911e9f025fb0479e9d2f4ce9d03ec..946e32ae909dfefc464a579e24f8bc0b0a901ef2 100644 (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