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

Rui Ueyama ruiu at google.com
Mon Jun 22 10:30:48 PDT 2015


Fixed in r240298.

On Sun, Jun 21, 2015 at 4:13 PM, David Majnemer <david.majnemer at gmail.com>
wrote:

>
>
> On Sun, Jun 21, 2015 at 3:31 PM, Rui Ueyama <ruiu at google.com> wrote:
>
>> 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
>>
>
> Looks like a typo, should probably be __delayLoadHelper2?
>
>
>> +// 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: }
>>
>>
>> _______________________________________________
>> llvm-commits mailing list
>> llvm-commits at cs.uiuc.edu
>> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20150622/5086f5c1/attachment.html>


More information about the llvm-commits mailing list