diff --git a/lld/COFF/DLL.h b/lld/COFF/DLL.h --- a/lld/COFF/DLL.h +++ b/lld/COFF/DLL.h @@ -44,6 +44,8 @@ std::vector getChunks(); std::vector getDataChunks(); ArrayRef getCodeChunks() { return thunks; } + ArrayRef getCodePData() { return pdata; } + ArrayRef getCodeUnwindInfo() { return unwindinfo; } uint64_t getDirRVA() { return dirs[0]->getRVA(); } uint64_t getDirSize(); @@ -51,6 +53,8 @@ private: Chunk *newThunkChunk(DefinedImportData *s, Chunk *tailMerge); Chunk *newTailMergeChunk(Chunk *dir); + Chunk *newTailMergePDataChunk(Chunk *tm, Chunk *unwind); + Chunk *newTailMergeUnwindInfoChunk(); Defined *helper; std::vector imports; @@ -60,6 +64,8 @@ std::vector names; std::vector hintNames; std::vector thunks; + std::vector pdata; + std::vector unwindinfo; std::vector dllNames; COFFLinkerContext &ctx; diff --git a/lld/COFF/DLL.cpp b/lld/COFF/DLL.cpp --- a/lld/COFF/DLL.cpp +++ b/lld/COFF/DLL.cpp @@ -229,6 +229,19 @@ 0xFF, 0xE0, // jmp rax }; +static const uint8_t tailMergeUnwindInfoX64[] = { + 0x01, // Version=1, Flags=UNW_FLAG_NHANDLER + 0x0a, // Size of prolog + 0x05, // Count of unwind codes + 0x00, // No frame register + 0x0a, 0x82, // Offset 0xa: UWOP_ALLOC_SMALL(0x48) + 0x06, 0x02, // Offset 6: UWOP_ALLOC_SMALL(8) + 0x04, 0x02, // Offset 4: UWOP_ALLOC_SMALL(8) + 0x02, 0x02, // Offset 2: UWOP_ALLOC_SMALL(8) + 0x01, 0x02, // Offset 1: UWOP_ALLOC_SMALL(8) + 0x00, 0x00 // Padding to align on 32-bits +}; + static const uint8_t thunkX86[] = { 0xB8, 0, 0, 0, 0, // mov eax, offset ___imp__ 0xE9, 0, 0, 0, 0, // jmp __tailMerge_ @@ -332,6 +345,41 @@ Defined *helper = nullptr; }; +class TailMergePDataChunkX64 : public NonSectionChunk { +public: + TailMergePDataChunkX64(Chunk *tm, Chunk *unwind) : tm(tm), unwind(unwind) { + // See + // https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-runtime_function + setAlignment(4); + } + + size_t getSize() const override { return 3 * sizeof(uint32_t); } + + void writeTo(uint8_t *buf) const override { + write32le(buf + 0, tm->getRVA()); // TailMergeChunk start RVA + write32le(buf + 4, tm->getRVA() + tm->getSize()); // TailMergeChunk stop RVA + write32le(buf + 8, unwind->getRVA()); // UnwindInfo RVA + } + + Chunk *tm = nullptr; + Chunk *unwind = nullptr; +}; + +class TailMergeUnwindInfoX64 : public NonSectionChunk { +public: + TailMergeUnwindInfoX64() { + // See + // https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-unwind_info + setAlignment(4); + } + + size_t getSize() const override { return sizeof(tailMergeUnwindInfoX64); } + + void writeTo(uint8_t *buf) const override { + memcpy(buf, tailMergeUnwindInfoX64, sizeof(tailMergeUnwindInfoX64)); + } +}; + class ThunkChunkX86 : public NonSectionChunk { public: ThunkChunkX86(COFFLinkerContext &ctx, Defined *i, Chunk *tm) @@ -672,6 +720,8 @@ helper = h; std::vector> v = binImports(ctx, imports); + Chunk *unwind = newTailMergeUnwindInfoChunk(); + // Create .didat contents for each DLL. for (std::vector &syms : v) { // Create the delay import table header. @@ -680,6 +730,7 @@ size_t base = addresses.size(); Chunk *tm = newTailMergeChunk(dir); + Chunk *pdataChunk = unwind ? newTailMergePDataChunk(tm, unwind) : nullptr; for (DefinedImportData *s : syms) { Chunk *t = newThunkChunk(s, tm); auto *a = make(ctx, t); @@ -692,7 +743,7 @@ auto *c = make(extName, 0); names.push_back(make(ctx, c)); hintNames.push_back(c); - // Add a syntentic symbol for this load thunk, using the "__imp___load" + // Add a synthetic symbol for this load thunk, using the "__imp___load" // prefix, in case this thunk needs to be added to the list of valid // call targets for Control Flow Guard. StringRef symName = saver().save("__imp___load_" + extName); @@ -701,6 +752,8 @@ } } thunks.push_back(tm); + if (pdataChunk) + pdata.push_back(pdataChunk); StringRef tmName = saver().save("__tailMerge_" + syms[0]->getDLLName().lower()); ctx.symtab.addSynthetic(tmName, tm); @@ -720,6 +773,9 @@ dir->nameTab = names[base]; dirs.push_back(dir); } + + if (unwind) + unwindinfo.push_back(unwind); // Add null terminator. dirs.push_back(make(sizeof(delay_import_directory_table_entry))); } @@ -739,6 +795,25 @@ } } +Chunk *DelayLoadContents::newTailMergeUnwindInfoChunk() { + switch (ctx.config.machine) { + case AMD64: + return make(); + // FIXME: Add support for other architectures. + default: + return nullptr; // Just don't generate unwind info. + } +} +Chunk *DelayLoadContents::newTailMergePDataChunk(Chunk *tm, Chunk *unwind) { + switch (ctx.config.machine) { + case AMD64: + return make(tm, unwind); + // FIXME: Add support for other architectures. + default: + return nullptr; // Just don't generate unwind info. + } +} + Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s, Chunk *tailMerge) { switch (ctx.config.machine) { diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -1121,6 +1121,10 @@ dataSec->addChunk(c); for (Chunk *c : delayIdata.getCodeChunks()) textSec->addChunk(c); + for (Chunk *c : delayIdata.getCodePData()) + pdataSec->addChunk(c); + for (Chunk *c : delayIdata.getCodeUnwindInfo()) + rdataSec->addChunk(c); } } diff --git a/lld/test/COFF/delayimports.test b/lld/test/COFF/delayimports.test --- a/lld/test/COFF/delayimports.test +++ b/lld/test/COFF/delayimports.test @@ -3,13 +3,14 @@ # RUN: /alternatename:__delayLoadHelper2=main # RUN: llvm-readobj --coff-imports %t.exe | FileCheck -check-prefix=IMPORT %s # RUN: llvm-readobj --coff-basereloc %t.exe | FileCheck -check-prefix=BASEREL %s +# RUN: llvm-readobj --unwind %t.exe | FileCheck -check-prefix=UNWIND %s IMPORT: DelayImport { IMPORT-NEXT: Name: std64.dll IMPORT-NEXT: Attributes: 0x1 IMPORT-NEXT: ModuleHandle: 0x3018 IMPORT-NEXT: ImportAddressTable: 0x3020 -IMPORT-NEXT: ImportNameTable: 0x2040 +IMPORT-NEXT: ImportNameTable: 0x2050 IMPORT-NEXT: BoundDelayImportTable: 0x0 IMPORT-NEXT: UnloadDelayImportTable: 0x0 IMPORT-NEXT: Import { @@ -39,3 +40,27 @@ BASEREL-NEXT: Type: DIR64 BASEREL-NEXT: Address: 0x3030 BASEREL-NEXT: } + +UNWIND: UnwindInformation [ +UNWIND-NEXT: RuntimeFunction { +UNWIND-NEXT: StartAddress: (0x14000108A) +UNWIND-NEXT: EndAddress: (0x1400010DD) +UNWIND-NEXT: UnwindInfoAddress: (0x140002000) +UNWIND-NEXT: UnwindInfo { +UNWIND-NEXT: Version: 1 +UNWIND-NEXT: Flags [ (0x0) +UNWIND-NEXT: ] +UNWIND-NEXT: PrologSize: 10 +UNWIND-NEXT: FrameRegister: - +UNWIND-NEXT: FrameOffset: - +UNWIND-NEXT: UnwindCodeCount: 5 +UNWIND-NEXT: UnwindCodes [ +UNWIND-NEXT: 0x0A: ALLOC_SMALL size=72 +UNWIND-NEXT: 0x06: ALLOC_SMALL size=8 +UNWIND-NEXT: 0x04: ALLOC_SMALL size=8 +UNWIND-NEXT: 0x02: ALLOC_SMALL size=8 +UNWIND-NEXT: 0x01: ALLOC_SMALL size=8 +UNWIND-NEXT: ] +UNWIND-NEXT: } +UNWIND-NEXT: } +UNWIND-NEXT: ] diff --git a/lld/test/COFF/delayimporttables.yaml b/lld/test/COFF/delayimporttables.yaml --- a/lld/test/COFF/delayimporttables.yaml +++ b/lld/test/COFF/delayimporttables.yaml @@ -14,7 +14,7 @@ # CHECK-NEXT: Attributes: 0x1 # CHECK-NEXT: ModuleHandle: 0x3000 # CHECK-NEXT: ImportAddressTable: 0x3010 -# CHECK-NEXT: ImportNameTable: 0x2060 +# CHECK-NEXT: ImportNameTable: 0x2070 # CHECK-NEXT: BoundDelayImportTable: 0x0 # CHECK-NEXT: UnloadDelayImportTable: 0x0 # CHECK-NEXT: Import { @@ -31,7 +31,7 @@ # CHECK-NEXT: Attributes: 0x1 # CHECK-NEXT: ModuleHandle: 0x3008 # CHECK-NEXT: ImportAddressTable: 0x3028 -# CHECK-NEXT: ImportNameTable: 0x2078 +# CHECK-NEXT: ImportNameTable: 0x2088 # CHECK-NEXT: BoundDelayImportTable: 0x0 # CHECK-NEXT: UnloadDelayImportTable: 0x0 # CHECK-NEXT: Import { diff --git a/lld/test/COFF/giats.s b/lld/test/COFF/giats.s --- a/lld/test/COFF/giats.s +++ b/lld/test/COFF/giats.s @@ -37,14 +37,14 @@ # DELAY-CHECK: ImageBase: 0x140000000 # DELAY-CHECK: LoadConfig [ -# DELAY-CHECK: GuardCFFunctionTable: 0x140002114 +# DELAY-CHECK: GuardCFFunctionTable: 0x140002124 # DELAY-CHECK: GuardCFFunctionCount: 2 # DELAY-CHECK: GuardFlags [ (0x10500) # DELAY-CHECK: CF_FUNCTION_TABLE_PRESENT (0x400) # DELAY-CHECK: CF_INSTRUMENTED (0x100) # DELAY-CHECK: CF_LONGJUMP_TABLE_PRESENT (0x10000) # DELAY-CHECK: ] -# DELAY-CHECK: GuardAddressTakenIatEntryTable: 0x14000211C +# DELAY-CHECK: GuardAddressTakenIatEntryTable: 0x14000212C # DELAY-CHECK: GuardAddressTakenIatEntryCount: 1 # DELAY-CHECK: ] # DELAY-CHECK: GuardFidTable [ @@ -122,4 +122,4 @@ .quad __guard_iat_count .quad __guard_longjmp_table .quad __guard_fids_count - .fill 84, 1, 0 \ No newline at end of file + .fill 84, 1, 0