[lld] r240250 - COFF: Support delay-load import tables.

Rui Ueyama ruiu at google.com
Sun Jun 21 15:31:52 PDT 2015


Author: ruiu
Date: Sun Jun 21 17:31:52 2015
New Revision: 240250

URL: http://llvm.org/viewvc/llvm-project?rev=240250&view=rev
Log:
COFF: Support delay-load import tables.

DLLs are usually resolved at process startup, but you can
delay-load them by passing /delayload option to the linker.

If a /delayload is specified, the linker has to create data
which is similar to regular import table.
One notable difference is that the pointers in a delay-load
import table are originally pointing to thunks that resolves
themselves. Each thunk loads a DLL, resolve its name, and then
overwrites the pointer with the result so that subsequent
function calls directly call a desired function. The linker
has to emit thunks.

Added:
    lld/trunk/test/COFF/delayimports.test
Modified:
    lld/trunk/COFF/Config.h
    lld/trunk/COFF/DLL.cpp
    lld/trunk/COFF/DLL.h
    lld/trunk/COFF/Driver.cpp
    lld/trunk/COFF/Writer.cpp
    lld/trunk/COFF/Writer.h

Modified: lld/trunk/COFF/Config.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Config.h?rev=240250&r1=240249&r2=240250&view=diff
==============================================================================
--- lld/trunk/COFF/Config.h (original)
+++ lld/trunk/COFF/Config.h Sun Jun 21 17:31:52 2015
@@ -60,6 +60,7 @@ struct Configuration {
   bool DLL = false;
   StringRef Implib;
   std::vector<Export> Exports;
+  std::set<StringRef> DelayLoads;
 
   // Options for manifest files.
   ManifestKind Manifest = SideBySide;

Modified: lld/trunk/COFF/DLL.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/DLL.cpp?rev=240250&r1=240249&r2=240250&view=diff
==============================================================================
--- lld/trunk/COFF/DLL.cpp (original)
+++ lld/trunk/COFF/DLL.cpp Sun Jun 21 17:31:52 2015
@@ -7,8 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// This file defines various types of chunks for the DLL import
-// descriptor table. They are inherently Windows-specific.
+// This file defines various types of chunks for the DLL import or export
+// descriptor tables. They are inherently Windows-specific.
 // You need to read Microsoft PE/COFF spec to understand details
 // about the data structures.
 //
@@ -114,6 +114,7 @@ public:
   explicit NullChunk(size_t N) : Size(N) {}
   bool hasData() const override { return false; }
   size_t getSize() const override { return Size; }
+  void setAlign(size_t N) { Align = N; }
 
 private:
   size_t Size;
@@ -149,23 +150,33 @@ std::vector<Chunk *> IdataContents::getC
   return V;
 }
 
-void IdataContents::create() {
+static std::map<StringRef, std::vector<DefinedImportData *>>
+binImports(const std::vector<DefinedImportData *> &Imports) {
   // Group DLL-imported symbols by DLL name because that's how
   // symbols are layed out in the import descriptor table.
-  std::map<StringRef, std::vector<DefinedImportData *>> Map;
+  std::map<StringRef, std::vector<DefinedImportData *>> M;
   for (DefinedImportData *Sym : Imports)
-    Map[Sym->getDLLName()].push_back(Sym);
-
-  // Create .idata contents for each DLL.
-  for (auto &P : Map) {
-    StringRef Name = P.first;
-    std::vector<DefinedImportData *> &Syms = P.second;
+    M[Sym->getDLLName()].push_back(Sym);
 
+  for (auto &P : M) {
     // Sort symbols by name for each group.
+    std::vector<DefinedImportData *> &Syms = P.second;
     std::sort(Syms.begin(), Syms.end(),
               [](DefinedImportData *A, DefinedImportData *B) {
                 return A->getName() < B->getName();
               });
+  }
+  return M;
+}
+
+void IdataContents::create() {
+  std::map<StringRef, std::vector<DefinedImportData *>> Map =
+      binImports(Imports);
+
+  // Create .idata contents for each DLL.
+  for (auto &P : Map) {
+    StringRef Name = P.first;
+    std::vector<DefinedImportData *> &Syms = P.second;
 
     // Create lookup and address tables. If they have external names,
     // we need to create HintName chunks to store the names.
@@ -204,6 +215,148 @@ void IdataContents::create() {
 }
 
 // Export table
+// See Microsoft PE/COFF spec 4.3 for details.
+
+// A chunk for the delay import descriptor table etnry.
+class DelayDirectoryChunk : public Chunk {
+public:
+  explicit DelayDirectoryChunk(Chunk *N) : DLLName(N) {}
+
+  size_t getSize() const override {
+    return sizeof(delay_import_directory_table_entry);
+  }
+
+  void writeTo(uint8_t *Buf) override {
+    auto *E = (delay_import_directory_table_entry *)(Buf + FileOff);
+    E->Name = DLLName->getRVA();
+    E->ModuleHandle = ModuleHandle->getRVA();
+    E->DelayImportAddressTable = AddressTab->getRVA();
+    E->DelayImportNameTable = NameTab->getRVA();
+  }
+
+  Chunk *DLLName;
+  Chunk *ModuleHandle;
+  Chunk *AddressTab;
+  Chunk *NameTab;
+};
+
+// Initial contents for delay-loaded functions.
+// This code calls __delayLoadHerper2 function to resolve a symbol
+// and then overwrites its jump table slot with the result
+// for subsequent function calls.
+static const uint8_t Thunk[] = {
+    0x51,                               // push    rcx
+    0x52,                               // push    rdx
+    0x41, 0x50,                         // push    r8
+    0x41, 0x51,                         // push    r9
+    0x48, 0x83, 0xEC, 0x48,             // sub     rsp, 48h
+    0x66, 0x0F, 0x7F, 0x04, 0x24,       // movdqa  xmmword ptr [rsp], xmm0
+    0x66, 0x0F, 0x7F, 0x4C, 0x24, 0x10, // movdqa  xmmword ptr [rsp+10h], xmm1
+    0x66, 0x0F, 0x7F, 0x54, 0x24, 0x20, // movdqa  xmmword ptr [rsp+20h], xmm2
+    0x66, 0x0F, 0x7F, 0x5C, 0x24, 0x30, // movdqa  xmmword ptr [rsp+30h], xmm3
+    0x48, 0x8D, 0x15, 0, 0, 0, 0,       // lea     rdx, [__imp_<FUNCNAME>]
+    0x48, 0x8D, 0x0D, 0, 0, 0, 0,       // lea     rcx, [___DELAY_IMPORT_...]
+    0xE8, 0, 0, 0, 0,                   // call    __delayLoadHelper2
+    0x66, 0x0F, 0x6F, 0x04, 0x24,       // movdqa  xmm0, xmmword ptr [rsp]
+    0x66, 0x0F, 0x6F, 0x4C, 0x24, 0x10, // movdqa  xmm1, xmmword ptr [rsp+10h]
+    0x66, 0x0F, 0x6F, 0x54, 0x24, 0x20, // movdqa  xmm2, xmmword ptr [rsp+20h]
+    0x66, 0x0F, 0x6F, 0x5C, 0x24, 0x30, // movdqa  xmm3, xmmword ptr [rsp+30h]
+    0x48, 0x83, 0xC4, 0x48,             // add     rsp, 48h
+    0x41, 0x59,                         // pop     r9
+    0x41, 0x58,                         // pop     r8
+    0x5A,                               // pop     rdx
+    0x59,                               // pop     rcx
+    0xFF, 0xE0,                         // jmp     rax
+};
+
+// A chunk for the delay import thunk.
+class ThunkChunk : public Chunk {
+public:
+  ThunkChunk(Defined *I, Defined *H) : Imp(I), Helper(H) {}
+
+  size_t getSize() const override { return sizeof(Thunk); }
+
+  void writeTo(uint8_t *Buf) override {
+    memcpy(Buf + FileOff, Thunk, sizeof(Thunk));
+    write32le(Buf + FileOff + 36, Imp->getRVA());
+    write32le(Buf + FileOff + 43, Desc->getRVA());
+    write32le(Buf + FileOff + 48, Helper->getRVA());
+  }
+
+  Defined *Imp = nullptr;
+  Chunk *Desc = nullptr;
+  Defined *Helper = nullptr;
+};
+
+std::vector<Chunk *> DelayLoadContents::getChunks(Defined *H) {
+  Helper = H;
+  create();
+  std::vector<Chunk *> V;
+  for (std::unique_ptr<Chunk> &C : Dirs)
+    V.push_back(C.get());
+  for (std::unique_ptr<Chunk> &C : Addresses)
+    V.push_back(C.get());
+  for (std::unique_ptr<Chunk> &C : Names)
+    V.push_back(C.get());
+  for (std::unique_ptr<Chunk> &C : HintNames)
+    V.push_back(C.get());
+  for (auto &P : DLLNames) {
+    std::unique_ptr<Chunk> &C = P.second;
+    V.push_back(C.get());
+  }
+  return V;
+}
+
+uint64_t DelayLoadContents::getDirSize() {
+  return Dirs.size() * sizeof(delay_import_directory_table_entry);
+}
+
+void DelayLoadContents::create() {
+  std::map<StringRef, std::vector<DefinedImportData *>> Map =
+      binImports(Imports);
+
+  // Create .didat contents for each DLL.
+  for (auto &P : Map) {
+    StringRef Name = P.first;
+    std::vector<DefinedImportData *> &Syms = P.second;
+
+    size_t Base = Addresses.size();
+    for (DefinedImportData *S : Syms) {
+      auto T = make_unique<ThunkChunk>(S, Helper);
+      auto A = make_unique<LookupChunk>(T.get());
+      T->Desc = A.get();
+      Addresses.push_back(std::move(A));
+      Thunks.push_back(std::move(T));
+      auto C =
+          make_unique<HintNameChunk>(S->getExternalName(), S->getOrdinal());
+      Names.push_back(make_unique<LookupChunk>(C.get()));
+      HintNames.push_back(std::move(C));
+    }
+    // Terminate with null values.
+    Addresses.push_back(make_unique<NullChunk>(LookupChunkSize));
+    Names.push_back(make_unique<NullChunk>(LookupChunkSize));
+
+    for (int I = 0, E = Syms.size(); I < E; ++I)
+      Syms[I]->setLocation(Addresses[Base + I].get());
+    auto *MH = new NullChunk(8);
+    MH->setAlign(8);
+    ModuleHandles.push_back(std::unique_ptr<Chunk>(MH));
+
+    // Create the delay import table header.
+    if (!DLLNames.count(Name))
+      DLLNames[Name] = make_unique<StringChunk>(Name);
+    auto Dir = make_unique<DelayDirectoryChunk>(DLLNames[Name].get());
+    Dir->ModuleHandle = MH;
+    Dir->AddressTab = Addresses[Base].get();
+    Dir->NameTab = Names[Base].get();
+    Dirs.push_back(std::move(Dir));
+  }
+  // Add null terminator.
+  Dirs.push_back(
+      make_unique<NullChunk>(sizeof(delay_import_directory_table_entry)));
+}
+
+// Export table
 // Read Microsoft PE/COFF spec 5.3 for details.
 
 // A chunk for the export descriptor table.

Modified: lld/trunk/COFF/DLL.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/DLL.h?rev=240250&r1=240249&r2=240250&view=diff
==============================================================================
--- lld/trunk/COFF/DLL.h (original)
+++ lld/trunk/COFF/DLL.h Sun Jun 21 17:31:52 2015
@@ -23,6 +23,7 @@ namespace coff {
 class IdataContents {
 public:
   void add(DefinedImportData *Sym) { Imports.push_back(Sym); }
+  bool empty() { return Imports.empty(); }
   std::vector<Chunk *> getChunks();
 
   uint64_t getDirRVA() { return Dirs[0]->getRVA(); }
@@ -41,6 +42,32 @@ private:
   std::map<StringRef, std::unique_ptr<Chunk>> DLLNames;
 };
 
+// Windows-specific.
+// DelayLoadContents creates all chunks for the delay-load DLL import table.
+class DelayLoadContents {
+public:
+  void add(DefinedImportData *Sym) { Imports.push_back(Sym); }
+  bool empty() { return Imports.empty(); }
+  std::vector<Chunk *> getChunks(Defined *Helper);
+  std::vector<std::unique_ptr<Chunk>> &getDataChunks() { return ModuleHandles; }
+  std::vector<std::unique_ptr<Chunk>> &getCodeChunks() { return Thunks; }
+
+  uint64_t getDirRVA() { return Dirs[0]->getRVA(); }
+  uint64_t getDirSize();
+
+private:
+  void create();
+  Defined *Helper;
+  std::vector<DefinedImportData *> Imports;
+  std::vector<std::unique_ptr<Chunk>> Dirs;
+  std::vector<std::unique_ptr<Chunk>> ModuleHandles;
+  std::vector<std::unique_ptr<Chunk>> Addresses;
+  std::vector<std::unique_ptr<Chunk>> Names;
+  std::vector<std::unique_ptr<Chunk>> HintNames;
+  std::vector<std::unique_ptr<Chunk>> Thunks;
+  std::map<StringRef, std::unique_ptr<Chunk>> DLLNames;
+};
+
 // Windows-specific.
 // EdataContents creates all chunks for the DLL export table.
 class EdataContents {

Modified: lld/trunk/COFF/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Driver.cpp?rev=240250&r1=240249&r2=240250&view=diff
==============================================================================
--- lld/trunk/COFF/Driver.cpp (original)
+++ lld/trunk/COFF/Driver.cpp Sun Jun 21 17:31:52 2015
@@ -378,6 +378,12 @@ bool LinkerDriver::link(llvm::ArrayRef<c
     Config->Exports.push_back(E.get());
   }
 
+  // Handle /delayload
+  for (auto *Arg : Args->filtered(OPT_delayload)) {
+    Config->DelayLoads.insert(Arg->getValue());
+    Config->Includes.insert("__delayLoadHelper2");
+  }
+
   // Handle /failifmismatch
   for (auto *Arg : Args->filtered(OPT_failifmismatch))
     if (checkFailIfMismatch(Arg->getValue()))

Modified: lld/trunk/COFF/Writer.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Writer.cpp?rev=240250&r1=240249&r2=240250&view=diff
==============================================================================
--- lld/trunk/COFF/Writer.cpp (original)
+++ lld/trunk/COFF/Writer.cpp Sun Jun 21 17:31:52 2015
@@ -159,29 +159,45 @@ void Writer::createImportTables() {
   if (Symtab->ImportFiles.empty())
     return;
   OutputSection *Text = createSection(".text");
-  Idata.reset(new IdataContents());
   for (std::unique_ptr<ImportFile> &File : Symtab->ImportFiles) {
-    for (SymbolBody *Body : File->getSymbols()) {
-      if (auto *Import = dyn_cast<DefinedImportData>(Body)) {
-        Idata->add(Import);
+    for (SymbolBody *B : File->getSymbols()) {
+      auto *Import = dyn_cast<DefinedImportData>(B);
+      if (!Import) {
+        // Linker-created function thunks for DLL symbols are added to
+        // .text section.
+        Text->addChunk(cast<DefinedImportThunk>(B)->getChunk());
         continue;
       }
-      // Linker-created function thunks for DLL symbols are added to
-      // .text section.
-      Text->addChunk(cast<DefinedImportThunk>(Body)->getChunk());
+      if (Config->DelayLoads.count(Import->getDLLName())) {
+        DelayIdata.add(Import);
+      } else {
+        Idata.add(Import);
+      }
     }
   }
-  OutputSection *Sec = createSection(".idata");
-  for (Chunk *C : Idata->getChunks())
-    Sec->addChunk(C);
+  if (!Idata.empty()) {
+    OutputSection *Sec = createSection(".idata");
+    for (Chunk *C : Idata.getChunks())
+      Sec->addChunk(C);
+  }
+  if (!DelayIdata.empty()) {
+    OutputSection *Sec = createSection(".didat");
+    for (Chunk *C : DelayIdata.getChunks(Symtab->find("__delayLoadHelper2")))
+      Sec->addChunk(C);
+    Sec = createSection(".text");
+    for (std::unique_ptr<Chunk> &C : DelayIdata.getCodeChunks())
+      Sec->addChunk(C.get());
+    Sec = createSection(".data");
+    for (std::unique_ptr<Chunk> &C : DelayIdata.getDataChunks())
+      Sec->addChunk(C.get());
+  }
 }
 
 void Writer::createExportTable() {
   if (Config->Exports.empty())
     return;
-  Edata.reset(new EdataContents());
   OutputSection *Sec = createSection(".edata");
-  for (std::unique_ptr<Chunk> &C : Edata->Chunks)
+  for (std::unique_ptr<Chunk> &C : Edata.Chunks)
     Sec->addChunk(C.get());
 }
 
@@ -309,11 +325,16 @@ void Writer::writeHeader() {
     Dir[EXPORT_TABLE].RelativeVirtualAddress = Sec->getRVA();
     Dir[EXPORT_TABLE].Size = Sec->getVirtualSize();
   }
-  if (Idata) {
-    Dir[IMPORT_TABLE].RelativeVirtualAddress = Idata->getDirRVA();
-    Dir[IMPORT_TABLE].Size = Idata->getDirSize();
-    Dir[IAT].RelativeVirtualAddress = Idata->getIATRVA();
-    Dir[IAT].Size = Idata->getIATSize();
+  if (!Idata.empty()) {
+    Dir[IMPORT_TABLE].RelativeVirtualAddress = Idata.getDirRVA();
+    Dir[IMPORT_TABLE].Size = Idata.getDirSize();
+    Dir[IAT].RelativeVirtualAddress = Idata.getIATRVA();
+    Dir[IAT].Size = Idata.getIATSize();
+  }
+  if (!DelayIdata.empty()) {
+    Dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress =
+        DelayIdata.getDirRVA();
+    Dir[DELAY_IMPORT_DESCRIPTOR].Size = DelayIdata.getDirSize();
   }
   if (OutputSection *Sec = findSection(".rsrc")) {
     Dir[RESOURCE_TABLE].RelativeVirtualAddress = Sec->getRVA();

Modified: lld/trunk/COFF/Writer.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Writer.h?rev=240250&r1=240249&r2=240250&view=diff
==============================================================================
--- lld/trunk/COFF/Writer.h (original)
+++ lld/trunk/COFF/Writer.h Sun Jun 21 17:31:52 2015
@@ -100,8 +100,9 @@ private:
   llvm::SpecificBumpPtrAllocator<OutputSection> CAlloc;
   llvm::SpecificBumpPtrAllocator<BaserelChunk> BAlloc;
   std::vector<OutputSection *> OutputSections;
-  std::unique_ptr<IdataContents> Idata;
-  std::unique_ptr<EdataContents> Edata;
+  IdataContents Idata;
+  DelayLoadContents DelayIdata;
+  EdataContents Edata;
 
   uint64_t FileSize;
   uint64_t SizeOfImage;

Added: lld/trunk/test/COFF/delayimports.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/delayimports.test?rev=240250&view=auto
==============================================================================
--- lld/trunk/test/COFF/delayimports.test (added)
+++ lld/trunk/test/COFF/delayimports.test Sun Jun 21 17:31:52 2015
@@ -0,0 +1,26 @@
+# RUN: lld -flavor link2 /out:%t.exe /entry:main /subsystem:console \
+# RUN:   %p/Inputs/hello64.obj %p/Inputs/std64.lib /delayload:std64.dll \
+# RUN:   /alternatename:__delayLoadHelper2=main
+# RUN: llvm-readobj -coff-imports %t.exe | FileCheck %s
+
+CHECK:      DelayImport {
+CHECK-NEXT:   Name: std64.dll
+CHECK-NEXT:   Attributes: 0x0
+CHECK-NEXT:   ModuleHandle: 0x1018
+CHECK-NEXT:   ImportAddressTable: 0x3040
+CHECK-NEXT:   ImportNameTable: 0x3060
+CHECK-NEXT:   BoundDelayImportTable: 0x0
+CHECK-NEXT:   UnloadDelayImportTable: 0x0
+CHECK-NEXT:   Import {
+CHECK-NEXT:     Symbol: ExitProcess (0)
+CHECK-NEXT:     Address: 0x2045
+CHECK-NEXT:   }
+CHECK-NEXT:   Import {
+CHECK-NEXT:     Symbol:  (50)
+CHECK-NEXT:     Address: 0x209C
+CHECK-NEXT:   }
+CHECK-NEXT:   Import {
+CHECK-NEXT:     Symbol: MessageBoxA (1)
+CHECK-NEXT:     Address: 0x20F3
+CHECK-NEXT:   }
+CHECK-NEXT: }





More information about the llvm-commits mailing list