[lld] c512eda - [lld][COFF] Provide unwinding information for Chunk injected by /delayloaded

via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 16 09:40:19 PST 2023


Author: serge-sans-paille
Date: 2023-01-16T18:39:21+01:00
New Revision: c512eda38ebe4070482545b52050adf307d37d7d

URL: https://github.com/llvm/llvm-project/commit/c512eda38ebe4070482545b52050adf307d37d7d
DIFF: https://github.com/llvm/llvm-project/commit/c512eda38ebe4070482545b52050adf307d37d7d.diff

LOG: [lld][COFF] Provide unwinding information for Chunk injected by /delayloaded

For each symbol in a /delayloaded library, lld injects a small piece of
code to handle the symbol lazy loading. This code doesn't have unwind
information, which may be troublesome.

Provide these information for AMD64.

Thanks to Yannis Juglaret <yjuglaret at mozilla.com> for contributing the
unwinding info and for his support while crafting this patch.

Fix #59639

Differential Revision: https://reviews.llvm.org/D141691

Added: 
    

Modified: 
    lld/COFF/DLL.cpp
    lld/COFF/DLL.h
    lld/COFF/Writer.cpp
    lld/test/COFF/delayimports.test
    lld/test/COFF/delayimporttables.yaml
    lld/test/COFF/giats.s

Removed: 
    


################################################################################
diff  --git a/lld/COFF/DLL.cpp b/lld/COFF/DLL.cpp
index b6e91ea1c197d..417b7041fbf01 100644
--- a/lld/COFF/DLL.cpp
+++ b/lld/COFF/DLL.cpp
@@ -229,6 +229,19 @@ static const uint8_t tailMergeX64[] = {
     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__<FUNCNAME>
     0xE9, 0, 0, 0, 0,  // jmp   __tailMerge_<lib>
@@ -332,6 +345,41 @@ class TailMergeChunkX64 : public NonSectionChunk {
   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 @@ void DelayLoadContents::create(Defined *h) {
   helper = h;
   std::vector<std::vector<DefinedImportData *>> v = binImports(ctx, imports);
 
+  Chunk *unwind = newTailMergeUnwindInfoChunk();
+
   // Create .didat contents for each DLL.
   for (std::vector<DefinedImportData *> &syms : v) {
     // Create the delay import table header.
@@ -680,6 +730,7 @@ void DelayLoadContents::create(Defined *h) {
 
     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<DelayAddressChunk>(ctx, t);
@@ -692,7 +743,7 @@ void DelayLoadContents::create(Defined *h) {
         auto *c = make<HintNameChunk>(extName, 0);
         names.push_back(make<LookupChunk>(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 @@ void DelayLoadContents::create(Defined *h) {
       }
     }
     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 @@ void DelayLoadContents::create(Defined *h) {
     dir->nameTab = names[base];
     dirs.push_back(dir);
   }
+
+  if (unwind)
+    unwindinfo.push_back(unwind);
   // Add null terminator.
   dirs.push_back(make<NullChunk>(sizeof(delay_import_directory_table_entry)));
 }
@@ -739,6 +795,25 @@ Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) {
   }
 }
 
+Chunk *DelayLoadContents::newTailMergeUnwindInfoChunk() {
+  switch (ctx.config.machine) {
+  case AMD64:
+    return make<TailMergeUnwindInfoX64>();
+    // 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<TailMergePDataChunkX64>(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/DLL.h b/lld/COFF/DLL.h
index f90fd575efc10..7cf71f59d7c7d 100644
--- a/lld/COFF/DLL.h
+++ b/lld/COFF/DLL.h
@@ -44,6 +44,8 @@ class DelayLoadContents {
   std::vector<Chunk *> getChunks();
   std::vector<Chunk *> getDataChunks();
   ArrayRef<Chunk *> getCodeChunks() { return thunks; }
+  ArrayRef<Chunk *> getCodePData() { return pdata; }
+  ArrayRef<Chunk *> getCodeUnwindInfo() { return unwindinfo; }
 
   uint64_t getDirRVA() { return dirs[0]->getRVA(); }
   uint64_t getDirSize();
@@ -51,6 +53,8 @@ class DelayLoadContents {
 private:
   Chunk *newThunkChunk(DefinedImportData *s, Chunk *tailMerge);
   Chunk *newTailMergeChunk(Chunk *dir);
+  Chunk *newTailMergePDataChunk(Chunk *tm, Chunk *unwind);
+  Chunk *newTailMergeUnwindInfoChunk();
 
   Defined *helper;
   std::vector<DefinedImportData *> imports;
@@ -60,6 +64,8 @@ class DelayLoadContents {
   std::vector<Chunk *> names;
   std::vector<Chunk *> hintNames;
   std::vector<Chunk *> thunks;
+  std::vector<Chunk *> pdata;
+  std::vector<Chunk *> unwindinfo;
   std::vector<Chunk *> dllNames;
 
   COFFLinkerContext &ctx;

diff  --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index b02ad01bdef74..f1766a4cddf8b 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -1121,6 +1121,10 @@ void Writer::appendImportThunks() {
       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
index 3a6db67e98db2..f410eef35fd1d 100644
--- 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:   Entry {
 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
index c9ceaab48266e..f1e7c61f55a5e 100644
--- 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
index 3d1663b245853..f870429f39d85 100644
--- 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 @@ _load_config_used:
         .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


        


More information about the llvm-commits mailing list