Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from
Dec 5, 2023

Conversation

cjacek
Copy link
Contributor

@cjacek cjacek commented Nov 16, 2023

ARM64EC needs to handle both ARM and x86_64 exception tables. This is achieved by separating their chunks and sorting them separately. EXCEPTION_TABLE directory references x86_64 variant, while ARM variant is exposed using CHPE metadata, which references __arm64x_extra_rfe_table and __arm64x_extra_rfe_table_size symbols.

Depends on #72518.

cc @bylaws

@llvmbot
Copy link
Collaborator

llvmbot commented Nov 16, 2023

@llvm/pr-subscribers-platform-windows

Author: Jacek Caban (cjacek)

Changes

ARM64EC needs to handle both ARM and x86_64 exception tables. This is achieved by separating their chunks and sorting them separately. EXCEPTION_TABLE directory references x86_64 variant, while ARM variant is exposed using CHPE metadata, which references __arm64x_extra_rfe_table and __arm64x_extra_rfe_table_size symbols.

Depends on #72518.

cc @bylaws


Full diff: https://github.com/llvm/llvm-project/pull/72521.diff

4 Files Affected:

  • (modified) lld/COFF/Driver.cpp (+2)
  • (modified) lld/COFF/Writer.cpp (+87-31)
  • (modified) lld/test/COFF/Inputs/loadconfig-arm64ec.s (+2-2)
  • (added) lld/test/COFF/pdata-arm64ec.test (+132)
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 0477c094c0ac82c..3e0641eeabfbeee 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -243,11 +243,13 @@ class Writer {
   void maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym,
                         StringRef countSym, bool hasFlag=false);
   void setSectionPermissions();
+  void setECSymbols();
   void writeSections();
   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);
@@ -321,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;
 };
@@ -737,6 +741,7 @@ void Writer::run() {
     removeEmptySections();
     assignOutputSectionIndices();
     setSectionPermissions();
+    setECSymbols();
     createSymbolAndStringTable();
 
     if (fileSize > UINT32_MAX)
@@ -751,7 +756,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
@@ -1407,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) {
@@ -1664,10 +1689,13 @@ 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();
@@ -2080,6 +2108,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");
@@ -2164,41 +2210,51 @@ 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();
+  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)));
+  }
+
+  parallelSort(MutableArrayRef<T>((T *)begin, (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;
+  };
+
   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; });
+    sortExceptionTable<EntryX64>(firstPdata, lastPdata);
     return;
   }
-  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; });
+  if (isArm64EC(ctx.config.machine))
+    sortExceptionTable<EntryX64>(firstHybridPdata, lastHybridPdata);
+  if (ctx.config.machine == ARMNT || isAnyArm64(ctx.config.machine)) {
+    sortExceptionTable<EntryArm>(firstPdata, lastPdata);
     return;
   }
-  lld::errs() << "warning: don't know how to handle .pdata.\n";
+  if (firstPdata)
+    lld::errs() << "warning: don't know how to handle .pdata.\n";
 }
 
 // The CRT section contains, among other things, the array of function
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

@llvmbot
Copy link
Collaborator

llvmbot commented Nov 16, 2023

@llvm/pr-subscribers-lld

Author: Jacek Caban (cjacek)

Changes

ARM64EC needs to handle both ARM and x86_64 exception tables. This is achieved by separating their chunks and sorting them separately. EXCEPTION_TABLE directory references x86_64 variant, while ARM variant is exposed using CHPE metadata, which references __arm64x_extra_rfe_table and __arm64x_extra_rfe_table_size symbols.

Depends on #72518.

cc @bylaws


Full diff: https://github.com/llvm/llvm-project/pull/72521.diff

4 Files Affected:

  • (modified) lld/COFF/Driver.cpp (+2)
  • (modified) lld/COFF/Writer.cpp (+87-31)
  • (modified) lld/test/COFF/Inputs/loadconfig-arm64ec.s (+2-2)
  • (added) lld/test/COFF/pdata-arm64ec.test (+132)
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 0477c094c0ac82c..3e0641eeabfbeee 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -243,11 +243,13 @@ class Writer {
   void maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym,
                         StringRef countSym, bool hasFlag=false);
   void setSectionPermissions();
+  void setECSymbols();
   void writeSections();
   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);
@@ -321,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;
 };
@@ -737,6 +741,7 @@ void Writer::run() {
     removeEmptySections();
     assignOutputSectionIndices();
     setSectionPermissions();
+    setECSymbols();
     createSymbolAndStringTable();
 
     if (fileSize > UINT32_MAX)
@@ -751,7 +756,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
@@ -1407,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) {
@@ -1664,10 +1689,13 @@ 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();
@@ -2080,6 +2108,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");
@@ -2164,41 +2210,51 @@ 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();
+  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)));
+  }
+
+  parallelSort(MutableArrayRef<T>((T *)begin, (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;
+  };
+
   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; });
+    sortExceptionTable<EntryX64>(firstPdata, lastPdata);
     return;
   }
-  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; });
+  if (isArm64EC(ctx.config.machine))
+    sortExceptionTable<EntryX64>(firstHybridPdata, lastHybridPdata);
+  if (ctx.config.machine == ARMNT || isAnyArm64(ctx.config.machine)) {
+    sortExceptionTable<EntryArm>(firstPdata, lastPdata);
     return;
   }
-  lld::errs() << "warning: don't know how to handle .pdata.\n";
+  if (firstPdata)
+    lld::errs() << "warning: don't know how to handle .pdata.\n";
 }
 
 // The CRT section contains, among other things, the array of function
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

@llvmbot
Copy link
Collaborator

llvmbot commented Nov 16, 2023

@llvm/pr-subscribers-lld-coff

Author: Jacek Caban (cjacek)

Changes

ARM64EC needs to handle both ARM and x86_64 exception tables. This is achieved by separating their chunks and sorting them separately. EXCEPTION_TABLE directory references x86_64 variant, while ARM variant is exposed using CHPE metadata, which references __arm64x_extra_rfe_table and __arm64x_extra_rfe_table_size symbols.

Depends on #72518.

cc @bylaws


Full diff: https://github.com/llvm/llvm-project/pull/72521.diff

4 Files Affected:

  • (modified) lld/COFF/Driver.cpp (+2)
  • (modified) lld/COFF/Writer.cpp (+87-31)
  • (modified) lld/test/COFF/Inputs/loadconfig-arm64ec.s (+2-2)
  • (added) lld/test/COFF/pdata-arm64ec.test (+132)
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 0477c094c0ac82c..3e0641eeabfbeee 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -243,11 +243,13 @@ class Writer {
   void maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym,
                         StringRef countSym, bool hasFlag=false);
   void setSectionPermissions();
+  void setECSymbols();
   void writeSections();
   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);
@@ -321,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;
 };
@@ -737,6 +741,7 @@ void Writer::run() {
     removeEmptySections();
     assignOutputSectionIndices();
     setSectionPermissions();
+    setECSymbols();
     createSymbolAndStringTable();
 
     if (fileSize > UINT32_MAX)
@@ -751,7 +756,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
@@ -1407,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) {
@@ -1664,10 +1689,13 @@ 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();
@@ -2080,6 +2108,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");
@@ -2164,41 +2210,51 @@ 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();
+  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)));
+  }
+
+  parallelSort(MutableArrayRef<T>((T *)begin, (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;
+  };
+
   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; });
+    sortExceptionTable<EntryX64>(firstPdata, lastPdata);
     return;
   }
-  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; });
+  if (isArm64EC(ctx.config.machine))
+    sortExceptionTable<EntryX64>(firstHybridPdata, lastHybridPdata);
+  if (ctx.config.machine == ARMNT || isAnyArm64(ctx.config.machine)) {
+    sortExceptionTable<EntryArm>(firstPdata, lastPdata);
     return;
   }
-  lld::errs() << "warning: don't know how to handle .pdata.\n";
+  if (firstPdata)
+    lld::errs() << "warning: don't know how to handle .pdata.\n";
 }
 
 // The CRT section contains, among other things, the array of function
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

Copy link

github-actions bot commented Nov 16, 2023

✅ With the latest revision this PR passed the C/C++ code formatter.

@cjacek cjacek force-pushed the arm64ec-pdata branch 3 times, most recently from ac72d7f to fc73537 Compare November 17, 2023 11:45
@cjacek
Copy link
Contributor Author

cjacek commented Nov 17, 2023

Rebased on top of upstreamed #72518.

lld/COFF/Writer.cpp Outdated Show resolved Hide resolved
lld/COFF/Writer.cpp Outdated Show resolved Hide resolved
lld/test/COFF/pdata-arm64ec.test Outdated Show resolved Hide resolved
@cjacek
Copy link
Contributor Author

cjacek commented Dec 2, 2023

Rebased on top of #74024.

Copy link
Member

@mstorsjo mstorsjo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@cjacek cjacek merged commit 72c6ca6 into llvm:main Dec 5, 2023
2 of 3 checks passed
@cjacek cjacek deleted the arm64ec-pdata branch December 5, 2023 11:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants