[lld] r240250 - COFF: Support delay-load import tables.
David Majnemer
david.majnemer at gmail.com
Sun Jun 21 16:13:48 PDT 2015
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/20150621/290c785c/attachment.html>
More information about the llvm-commits
mailing list