[lld] [lld][COFF] Support .pdata section on ARM64EC targets. (PR #72521)

Jacek Caban via llvm-commits llvm-commits at lists.llvm.org
Thu Nov 16 08:00:42 PST 2023


https://github.com/cjacek updated https://github.com/llvm/llvm-project/pull/72521

>From 29103b40bcc1f25389cfb67a06daea93eacbe629 Mon Sep 17 00:00:00 2001
From: Jacek Caban <jacek at codeweavers.com>
Date: Wed, 15 Nov 2023 23:56:20 +0100
Subject: [PATCH 1/2] [lld][COFF][NFC] Factor out exception table sorting.

---
 lld/COFF/Writer.cpp | 69 +++++++++++++++++++++++++++------------------
 1 file changed, 41 insertions(+), 28 deletions(-)

diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index 0477c094c0ac82c..553e421f36ef965 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -247,7 +247,8 @@ class Writer {
   void writeBuildId();
   void writePEChecksum();
   void sortSections();
-  void sortExceptionTable();
+  template <typename T> void sortExceptionTable(Chunk *first, Chunk *last);
+  void sortExceptionTables();
   void sortCRTSectionChunks(std::vector<Chunk *> &chunks);
   void addSyntheticIdata();
   void sortBySectionOrder(std::vector<Chunk *> &chunks);
@@ -751,7 +752,7 @@ void Writer::run() {
     }
     writeSections();
     prepareLoadConfig();
-    sortExceptionTable();
+    sortExceptionTables();
 
     // Fix up the alignment in the TLS Directory's characteristic field,
     // if a specific alignment value is needed
@@ -2164,41 +2165,53 @@ void Writer::writeBuildId() {
 }
 
 // Sort .pdata section contents according to PE/COFF spec 5.5.
-void Writer::sortExceptionTable() {
-  if (!firstPdata)
+template <typename T>
+void Writer::sortExceptionTable(Chunk *first, Chunk *last) {
+  if (!first)
     return;
-  llvm::TimeTraceScope timeScope("Sort exception table");
   // We assume .pdata contains function table entries only.
+
   auto bufAddr = [&](Chunk *c) {
     OutputSection *os = ctx.getOutputSection(c);
     return buffer->getBufferStart() + os->getFileOff() + c->getRVA() -
            os->getRVA();
   };
-  uint8_t *begin = bufAddr(firstPdata);
-  uint8_t *end = bufAddr(lastPdata) + lastPdata->getSize();
-  if (ctx.config.machine == AMD64) {
-    struct Entry { ulittle32_t begin, end, unwind; };
-    if ((end - begin) % sizeof(Entry) != 0) {
-      fatal("unexpected .pdata size: " + Twine(end - begin) +
-            " is not a multiple of " + Twine(sizeof(Entry)));
-    }
-    parallelSort(
-        MutableArrayRef<Entry>((Entry *)begin, (Entry *)end),
-        [](const Entry &a, const Entry &b) { return a.begin < b.begin; });
-    return;
+  uint8_t *begin = bufAddr(first);
+  uint8_t *end = bufAddr(last) + last->getSize();
+  if ((end - begin) % sizeof(T) != 0) {
+    fatal("unexpected .pdata size: " + Twine(end - begin) +
+          " is not a multiple of " + Twine(sizeof(T)));
   }
-  if (ctx.config.machine == ARMNT || ctx.config.machine == ARM64) {
-    struct Entry { ulittle32_t begin, unwind; };
-    if ((end - begin) % sizeof(Entry) != 0) {
-      fatal("unexpected .pdata size: " + Twine(end - begin) +
-            " is not a multiple of " + Twine(sizeof(Entry)));
-    }
-    parallelSort(
-        MutableArrayRef<Entry>((Entry *)begin, (Entry *)end),
-        [](const Entry &a, const Entry &b) { return a.begin < b.begin; });
-    return;
+
+  parallelSort(MutableArrayRef<T>(reinterpret_cast<T *>(begin),
+                                  reinterpret_cast<T *>(end)),
+               [](const T &a, const T &b) { return a.begin < b.begin; });
+}
+
+// Sort .pdata section contents according to PE/COFF spec 5.5.
+void Writer::sortExceptionTables() {
+  llvm::TimeTraceScope timeScope("Sort exception table");
+
+  struct EntryX64 {
+    ulittle32_t begin, end, unwind;
+  };
+  struct EntryArm {
+    ulittle32_t begin, unwind;
+  };
+
+  switch (ctx.config.machine) {
+  case AMD64:
+    sortExceptionTable<EntryX64>(firstPdata, lastPdata);
+    break;
+  case ARMNT:
+  case ARM64:
+    sortExceptionTable<EntryArm>(firstPdata, lastPdata);
+    break;
+  default:
+    if (firstPdata)
+      lld::errs() << "warning: don't know how to handle .pdata.\n";
+    break;
   }
-  lld::errs() << "warning: don't know how to handle .pdata.\n";
 }
 
 // The CRT section contains, among other things, the array of function

>From ac72d7febe2a61e5a10d0fa4b10550548a453aa6 Mon Sep 17 00:00:00 2001
From: Jacek Caban <jacek at codeweavers.com>
Date: Sat, 10 Jun 2023 19:34:18 +0200
Subject: [PATCH 2/2] [lld][COFF] Support .pdata section on ARM64EC targets.

---
 lld/COFF/Driver.cpp                       |   2 +
 lld/COFF/Writer.cpp                       |  63 ++++++++++-
 lld/test/COFF/Inputs/loadconfig-arm64ec.s |   4 +-
 lld/test/COFF/pdata-arm64ec.test          | 132 ++++++++++++++++++++++
 4 files changed, 193 insertions(+), 8 deletions(-)
 create mode 100644 lld/test/COFF/pdata-arm64ec.test

diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index f5cb379c5a4bf95..a5f20f8ec5c00f4 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -2361,6 +2361,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   ctx.symtab.addAbsolute(mangle("__guard_eh_cont_table"), 0);
 
   if (isArm64EC(config->machine)) {
+    ctx.symtab.addAbsolute("__arm64x_extra_rfe_table", 0);
+    ctx.symtab.addAbsolute("__arm64x_extra_rfe_table_size", 0);
     ctx.symtab.addAbsolute("__hybrid_code_map", 0);
     ctx.symtab.addAbsolute("__hybrid_code_map_count", 0);
   }
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index 553e421f36ef965..cd36b0867b5fceb 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -243,6 +243,7 @@ class Writer {
   void maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym,
                         StringRef countSym, bool hasFlag=false);
   void setSectionPermissions();
+  void setECSymbols();
   void writeSections();
   void writeBuildId();
   void writePEChecksum();
@@ -322,6 +323,8 @@ class Writer {
   // files, so we need to keep track of them separately.
   Chunk *firstPdata = nullptr;
   Chunk *lastPdata;
+  Chunk *firstHybridPdata = nullptr;
+  Chunk *lastHybridPdata;
 
   COFFLinkerContext &ctx;
 };
@@ -738,6 +741,7 @@ void Writer::run() {
     removeEmptySections();
     assignOutputSectionIndices();
     setSectionPermissions();
+    setECSymbols();
     createSymbolAndStringTable();
 
     if (fileSize > UINT32_MAX)
@@ -1408,8 +1412,28 @@ void Writer::createSymbolAndStringTable() {
 void Writer::mergeSections() {
   llvm::TimeTraceScope timeScope("Merge sections");
   if (!pdataSec->chunks.empty()) {
-    firstPdata = pdataSec->chunks.front();
-    lastPdata = pdataSec->chunks.back();
+    if (isArm64EC(ctx.config.machine)) {
+      // On ARM64EC we .pdata may contain both ARM64 and X64 data. Split them by
+      // sorting and store their regions separately.
+      llvm::stable_sort(pdataSec->chunks, [=](const Chunk *a, const Chunk *b) {
+        return (a->getMachine() == AMD64) < (b->getMachine() == AMD64);
+      });
+
+      for (auto chunk : pdataSec->chunks) {
+        if (chunk->getMachine() == AMD64) {
+          firstHybridPdata = chunk;
+          lastHybridPdata = pdataSec->chunks.back();
+          break;
+        }
+
+        if (!firstPdata)
+          firstPdata = chunk;
+        lastPdata = chunk;
+      }
+    } else {
+      firstPdata = pdataSec->chunks.front();
+      lastPdata = pdataSec->chunks.back();
+    }
   }
 
   for (auto &p : ctx.config.merge) {
@@ -1665,10 +1689,15 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
     dir[RESOURCE_TABLE].RelativeVirtualAddress = rsrcSec->getRVA();
     dir[RESOURCE_TABLE].Size = rsrcSec->getVirtualSize();
   }
-  if (firstPdata) {
-    dir[EXCEPTION_TABLE].RelativeVirtualAddress = firstPdata->getRVA();
-    dir[EXCEPTION_TABLE].Size =
-        lastPdata->getRVA() + lastPdata->getSize() - firstPdata->getRVA();
+  Chunk *firstPdataChunk =
+      ctx.config.machine == ARM64EC ? firstHybridPdata : firstPdata;
+  if (firstPdataChunk) {
+    Chunk *lastPdataChunk =
+        ctx.config.machine == ARM64EC ? lastHybridPdata : lastPdata;
+    dir[EXCEPTION_TABLE].RelativeVirtualAddress = firstPdataChunk->getRVA();
+    dir[EXCEPTION_TABLE].Size = lastPdataChunk->getRVA() +
+                                lastPdataChunk->getSize() -
+                                firstPdataChunk->getRVA();
   }
   if (relocSec->getVirtualSize()) {
     dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = relocSec->getRVA();
@@ -2081,6 +2110,24 @@ void Writer::setSectionPermissions() {
   }
 }
 
+// Set symbols used by ARM64EC metadata.
+void Writer::setECSymbols() {
+  if (!isArm64EC(ctx.config.machine))
+    return;
+
+  Symbol *rfeTableSym = ctx.symtab.findUnderscore("__arm64x_extra_rfe_table");
+  replaceSymbol<DefinedSynthetic>(rfeTableSym, "__arm64x_extra_rfe_table",
+                                  firstPdata);
+
+  if (firstPdata) {
+    Symbol *rfeSizeSym =
+        ctx.symtab.findUnderscore("__arm64x_extra_rfe_table_size");
+    cast<DefinedAbsolute>(rfeSizeSym)
+        ->setVA(lastPdata->getRVA() + lastPdata->getSize() -
+                firstPdata->getRVA());
+  }
+}
+
 // Write section contents to a mmap'ed file.
 void Writer::writeSections() {
   llvm::TimeTraceScope timeScope("Write sections");
@@ -2203,6 +2250,10 @@ void Writer::sortExceptionTables() {
   case AMD64:
     sortExceptionTable<EntryX64>(firstPdata, lastPdata);
     break;
+  case ARM64EC:
+  case ARM64X:
+    sortExceptionTable<EntryX64>(firstHybridPdata, lastHybridPdata);
+    [[fallthrough]];
   case ARMNT:
   case ARM64:
     sortExceptionTable<EntryArm>(firstPdata, lastPdata);
diff --git a/lld/test/COFF/Inputs/loadconfig-arm64ec.s b/lld/test/COFF/Inputs/loadconfig-arm64ec.s
index 78ae594a21eff3f..8bb5ccfed8ebc87 100644
--- a/lld/test/COFF/Inputs/loadconfig-arm64ec.s
+++ b/lld/test/COFF/Inputs/loadconfig-arm64ec.s
@@ -79,8 +79,8 @@ __chpe_metadata:
         .word 0 // __arm64x_redirection_metadata_count
         .rva __os_arm64x_get_x64_information
         .rva __os_arm64x_set_x64_information
-        .word 0 // __arm64x_extra_rfe_table
-        .word 0 // __arm64x_extra_rfe_table_size
+        .rva __arm64x_extra_rfe_table
+        .word __arm64x_extra_rfe_table_size
         .rva __os_arm64x_dispatch_fptr
         .word 0 // __hybrid_auxiliary_iat_copy
         .rva __os_arm64x_helper0
diff --git a/lld/test/COFF/pdata-arm64ec.test b/lld/test/COFF/pdata-arm64ec.test
new file mode 100644
index 000000000000000..daabf73709d833e
--- /dev/null
+++ b/lld/test/COFF/pdata-arm64ec.test
@@ -0,0 +1,132 @@
+REQUIRES: aarch64, x86
+RUN: split-file %s %t.dir && cd %t.dir
+
+Test handlign of hybrid .pdata section on ARM64EC target.
+
+RUN: llvm-mc -filetype=obj -triple=arm64-windows arm64-func-sym.s -o arm64-func-sym.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows arm64ec-func-sym.s -o arm64ec-func-sym.obj
+RUN: llvm-mc -filetype=obj -triple=x86_64-windows x86_64-func-sym.s -o x86_64-func-sym.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %p/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
+
+Only arm64ec code:
+
+RUN: lld-link -out:test1.dll -machine:arm64ec arm64ec-func-sym.obj loadconfig-arm64ec.obj -dll -noentry
+
+RUN: llvm-readobj --coff-load-config test1.dll | FileCheck -check-prefix=LOADCFG %s
+LOADCFG:      ExtraRFETable: 0x4000
+LOADCFG-NEXT: ExtraRFETableSize: 0x8
+
+RUN: llvm-readobj --headers test1.dll | FileCheck -check-prefix=NODIR %s
+NODIR: ExceptionTableSize: 0x0
+
+RUN: llvm-objdump -s --section=.pdata test1.dll | FileCheck -check-prefix=DATA %s
+DATA: 180004000 00100000 11000001
+
+Only x86_64 code:
+
+RUN: lld-link -out:test2.dll -machine:arm64ec x86_64-func-sym.obj loadconfig-arm64ec.obj -dll -noentry
+
+RUN: llvm-readobj --coff-load-config test2.dll | FileCheck -check-prefix=NOLOADCFG %s
+NOLOADCFG: ExtraRFETableSize: 0
+
+RUN: llvm-readobj --headers test2.dll | FileCheck -check-prefix=DIR %s
+DIR:      ExceptionTableRVA: 0x4000
+DIR-NEXT: ExceptionTableSize: 0xC
+
+RUN: llvm-objdump -s --section=.pdata test2.dll | FileCheck -check-prefix=DATA2 %s
+DATA2: 180004000 00100000 0e100000
+
+Mixed arm64ec and x86_64 code:
+
+RUN: lld-link -out:test3.dll -machine:arm64ec arm64ec-func-sym.obj x86_64-func-sym.obj \
+RUN:          loadconfig-arm64ec.obj -dll -noentry
+
+RUN: llvm-readobj --coff-load-config test3.dll | FileCheck -check-prefix=LOADCFG2 %s
+LOADCFG2:      ExtraRFETable: 0x5000
+LOADCFG2-NEXT: ExtraRFETableSize: 0x8
+
+RUN: llvm-readobj --headers test3.dll | FileCheck -check-prefix=DIR2 %s
+DIR2:      ExceptionTableRVA: 0x5008
+DIR2-NEXT: ExceptionTableSize: 0xC
+
+RUN: llvm-objdump -s --section=.pdata test3.dll | FileCheck -check-prefix=DATA3 %s
+DATA3: 180005000 00100000 11000001 00200000 0e200000
+
+Mixed arm64x code:
+
+RUN: lld-link -out:test4.dll -machine:arm64x arm64-func-sym.obj arm64ec-func-sym.obj \
+RUN:          x86_64-func-sym.obj loadconfig-arm64ec.obj -dll -noentry
+
+RUN: llvm-readobj --headers test4.dll | FileCheck -check-prefix=DIR3 %s
+DIR3:      ExceptionTableRVA: 0x6000
+DIR3-NEXT: ExceptionTableSize: 0x10
+
+RUN: llvm-objdump -s --section=.pdata test4.dll | FileCheck -check-prefix=DATA4 %s
+DATA4: 180006000 00100000 11000001 00200000 11000001  ......... ......
+DATA4: 180006010 00300000 0e300000
+
+Order of inputs doesn't matter, the data is sorted by type and RVA:
+
+RUN: lld-link -out:test5.dll -machine:arm64ec x86_64-func-sym.obj arm64ec-func-sym.obj \
+RUN:          loadconfig-arm64ec.obj -dll -noentry
+RUN: llvm-readobj --coff-load-config test5.dll | FileCheck -check-prefix=LOADCFG2 %s
+RUN: llvm-readobj --headers test5.dll | FileCheck -check-prefix=DIR2 %s
+RUN: llvm-objdump -s --section=.pdata test5.dll | FileCheck -check-prefix=DATA3 %s
+
+RUN: lld-link -out:test6.dll -machine:arm64x arm64ec-func-sym.obj x86_64-func-sym.obj \
+RUN:          arm64-func-sym.obj loadconfig-arm64ec.obj -dll -noentry
+RUN: llvm-readobj --headers test6.dll | FileCheck -check-prefix=DIR3 %s
+RUN: llvm-objdump -s --section=.pdata test6.dll | FileCheck -check-prefix=DATA4 %s
+
+RUN: lld-link -out:test7.dll -machine:arm64x x86_64-func-sym.obj arm64ec-func-sym.obj \
+RUN:          arm64-func-sym.obj loadconfig-arm64ec.obj -dll -noentry
+RUN: llvm-readobj --headers test7.dll | FileCheck -check-prefix=DIR3 %s
+RUN: llvm-objdump -s --section=.pdata test7.dll | FileCheck -check-prefix=DATA4 %s
+
+#--- arm64-func-sym.s
+    .text
+    .globl arm64_func_sym
+    .p2align 2, 0x0
+arm64_func_sym:
+    .seh_proc arm64_func_sym
+    sub sp, sp, #32
+    .seh_stackalloc 32
+    .seh_endprologue
+    mov w0, #2
+    .seh_startepilogue
+    add sp, sp, #32
+    .seh_stackalloc 32
+    .seh_endepilogue
+    ret
+    .seh_endproc
+
+#--- arm64ec-func-sym.s
+    .text
+    .globl arm64ec_func_sym
+    .p2align 2, 0x0
+arm64ec_func_sym:
+    .seh_proc arm64ec_func_sym
+    sub sp, sp, #32
+    .seh_stackalloc 32
+    .seh_endprologue
+    mov w0, #3
+    .seh_startepilogue
+    add sp, sp, #32
+    .seh_stackalloc 32
+    .seh_endepilogue
+    ret
+    .seh_endproc
+
+#--- x86_64-func-sym.s
+    .text
+    .globl x86_64_func_sym
+    .p2align 2, 0x0
+x86_64_func_sym:
+    .seh_proc x86_64_func_sym
+    subq    $40, %rsp
+    .seh_stackalloc 40
+    .seh_endprologue
+    movl $4, %eax
+    addq $40, %rsp
+    retq
+    .seh_endproc



More information about the llvm-commits mailing list