[lld] [lld] Align EC code region boundaries. (PR #69100)

Jacek Caban via llvm-commits llvm-commits at lists.llvm.org
Mon Oct 30 13:38:09 PDT 2023


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

>From 7cf2da25f79b7073b705e3a39b849d2d6329f829 Mon Sep 17 00:00:00 2001
From: Jacek Caban <jacek at codeweavers.com>
Date: Mon, 30 Oct 2023 14:19:17 +0100
Subject: [PATCH 1/3] [lld] Implement getOutputCharacteristics for non-section
 code thunks.

This will be useful for ARM64EC, but it also fixes MinGW export handling when synthetic function symbols are exported.
---
 lld/COFF/Chunks.h               | 18 ++++++++++++++----
 lld/COFF/DLL.cpp                | 16 ++++++++--------
 lld/test/COFF/export-thunk.test | 14 ++++++++++++++
 3 files changed, 36 insertions(+), 12 deletions(-)
 create mode 100644 lld/test/COFF/export-thunk.test

diff --git a/lld/COFF/Chunks.h b/lld/COFF/Chunks.h
index cbfeb5c025adbb2..b82bc9416775d7c 100644
--- a/lld/COFF/Chunks.h
+++ b/lld/COFF/Chunks.h
@@ -180,6 +180,16 @@ class NonSectionChunk : public Chunk {
   NonSectionChunk(Kind k = OtherKind) : Chunk(k) {}
 };
 
+class NonSectionCodeChunk : public NonSectionChunk {
+public:
+  virtual uint32_t getOutputCharacteristics() const override {
+    return llvm::COFF::IMAGE_SCN_MEM_READ | llvm::COFF::IMAGE_SCN_MEM_EXECUTE;
+  }
+
+protected:
+  NonSectionCodeChunk(Kind k = OtherKind) : NonSectionChunk(k) {}
+};
+
 // MinGW specific; information about one individual location in the image
 // that needs to be fixed up at runtime after loading. This represents
 // one individual element in the PseudoRelocTableChunk table.
@@ -508,10 +518,10 @@ static const uint8_t importThunkARM64[] = {
 // Windows-specific.
 // A chunk for DLL import jump table entry. In a final output, its
 // contents will be a JMP instruction to some __imp_ symbol.
-class ImportThunkChunk : public NonSectionChunk {
+class ImportThunkChunk : public NonSectionCodeChunk {
 public:
   ImportThunkChunk(COFFLinkerContext &ctx, Defined *s)
-      : NonSectionChunk(ImportThunkKind), impSymbol(s), ctx(ctx) {}
+      : NonSectionCodeChunk(ImportThunkKind), impSymbol(s), ctx(ctx) {}
   static bool classof(const Chunk *c) { return c->kind() == ImportThunkKind; }
 
 protected:
@@ -560,7 +570,7 @@ class ImportThunkChunkARM64 : public ImportThunkChunk {
   MachineTypes getMachine() const override { return ARM64; }
 };
 
-class RangeExtensionThunkARM : public NonSectionChunk {
+class RangeExtensionThunkARM : public NonSectionCodeChunk {
 public:
   explicit RangeExtensionThunkARM(COFFLinkerContext &ctx, Defined *t)
       : target(t), ctx(ctx) {
@@ -576,7 +586,7 @@ class RangeExtensionThunkARM : public NonSectionChunk {
   COFFLinkerContext &ctx;
 };
 
-class RangeExtensionThunkARM64 : public NonSectionChunk {
+class RangeExtensionThunkARM64 : public NonSectionCodeChunk {
 public:
   explicit RangeExtensionThunkARM64(COFFLinkerContext &ctx, Defined *t)
       : target(t), ctx(ctx) {
diff --git a/lld/COFF/DLL.cpp b/lld/COFF/DLL.cpp
index 0b337a209c377db..6b516d8c6d5ef89 100644
--- a/lld/COFF/DLL.cpp
+++ b/lld/COFF/DLL.cpp
@@ -313,7 +313,7 @@ static const uint8_t tailMergeARM64[] = {
 };
 
 // A chunk for the delay import thunk.
-class ThunkChunkX64 : public NonSectionChunk {
+class ThunkChunkX64 : public NonSectionCodeChunk {
 public:
   ThunkChunkX64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {}
 
@@ -330,7 +330,7 @@ class ThunkChunkX64 : public NonSectionChunk {
   Chunk *tailMerge = nullptr;
 };
 
-class TailMergeChunkX64 : public NonSectionChunk {
+class TailMergeChunkX64 : public NonSectionCodeChunk {
 public:
   TailMergeChunkX64(Chunk *d, Defined *h) : desc(d), helper(h) {}
 
@@ -382,7 +382,7 @@ class TailMergeUnwindInfoX64 : public NonSectionChunk {
   }
 };
 
-class ThunkChunkX86 : public NonSectionChunk {
+class ThunkChunkX86 : public NonSectionCodeChunk {
 public:
   ThunkChunkX86(COFFLinkerContext &ctx, Defined *i, Chunk *tm)
       : imp(i), tailMerge(tm), ctx(ctx) {}
@@ -407,7 +407,7 @@ class ThunkChunkX86 : public NonSectionChunk {
   const COFFLinkerContext &ctx;
 };
 
-class TailMergeChunkX86 : public NonSectionChunk {
+class TailMergeChunkX86 : public NonSectionCodeChunk {
 public:
   TailMergeChunkX86(COFFLinkerContext &ctx, Chunk *d, Defined *h)
       : desc(d), helper(h), ctx(ctx) {}
@@ -432,7 +432,7 @@ class TailMergeChunkX86 : public NonSectionChunk {
   const COFFLinkerContext &ctx;
 };
 
-class ThunkChunkARM : public NonSectionChunk {
+class ThunkChunkARM : public NonSectionCodeChunk {
 public:
   ThunkChunkARM(COFFLinkerContext &ctx, Defined *i, Chunk *tm)
       : imp(i), tailMerge(tm), ctx(ctx) {
@@ -459,7 +459,7 @@ class ThunkChunkARM : public NonSectionChunk {
   const COFFLinkerContext &ctx;
 };
 
-class TailMergeChunkARM : public NonSectionChunk {
+class TailMergeChunkARM : public NonSectionCodeChunk {
 public:
   TailMergeChunkARM(COFFLinkerContext &ctx, Chunk *d, Defined *h)
       : desc(d), helper(h), ctx(ctx) {
@@ -486,7 +486,7 @@ class TailMergeChunkARM : public NonSectionChunk {
   const COFFLinkerContext &ctx;
 };
 
-class ThunkChunkARM64 : public NonSectionChunk {
+class ThunkChunkARM64 : public NonSectionCodeChunk {
 public:
   ThunkChunkARM64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {
     setAlignment(4);
@@ -506,7 +506,7 @@ class ThunkChunkARM64 : public NonSectionChunk {
   Chunk *tailMerge = nullptr;
 };
 
-class TailMergeChunkARM64 : public NonSectionChunk {
+class TailMergeChunkARM64 : public NonSectionCodeChunk {
 public:
   TailMergeChunkARM64(Chunk *d, Defined *h) : desc(d), helper(h) {
     setAlignment(4);
diff --git a/lld/test/COFF/export-thunk.test b/lld/test/COFF/export-thunk.test
new file mode 100644
index 000000000000000..85e0e92bc0639b7
--- /dev/null
+++ b/lld/test/COFF/export-thunk.test
@@ -0,0 +1,14 @@
+REQUIRES: x86
+
+RUN: echo -e 'LIBRARY test.dll\nEXPORTS\nimpfunc\n' > %t.imp.def
+RUN: llvm-dlltool -m i386:x86-64 -d %t.imp.def -l %t.imp.lib
+RUN: lld-link -machine:amd64 -out:%t.dll -dll -noentry -lldmingw %t.imp.lib -export:impfunc -output-def:%t.def
+
+Check that the synthetic import thunk is exported as a function, not data.
+
+RUN: cat %t.def | FileCheck %s
+CHECK: EXPORTS
+CHECK-NEXT: impfunc @1
+
+RUN: cat %t.def | FileCheck -check-prefix=CHECK-NO-DATA %s
+CHECK-NO-DATA-NOT: DATA

>From cb3d1cc1df7f476f4b7eb3da118ed1038d115e24 Mon Sep 17 00:00:00 2001
From: Jacek Caban <jacek at codeweavers.com>
Date: Mon, 30 Oct 2023 20:47:43 +0100
Subject: [PATCH 2/3] [lld] Sort data chunks before code chunks on ARM64EC.

---
 lld/COFF/Chunks.h                  |  8 +++++--
 lld/COFF/Writer.cpp                |  4 +++-
 lld/test/COFF/arm64ec-codemap.test | 38 ++++++++++++++++++++++++++++++
 3 files changed, 47 insertions(+), 3 deletions(-)

diff --git a/lld/COFF/Chunks.h b/lld/COFF/Chunks.h
index b82bc9416775d7c..156e7a807cb8fd7 100644
--- a/lld/COFF/Chunks.h
+++ b/lld/COFF/Chunks.h
@@ -116,7 +116,7 @@ class Chunk {
   bool isHotPatchable() const;
 
   MachineTypes getMachine() const;
-  chpe_range_type getArm64ECRangeType() const;
+  std::optional<chpe_range_type> getArm64ECRangeType() const;
 
 protected:
   Chunk(Kind k = OtherKind) : chunkKind(k), hasData(true), p2Align(0) {}
@@ -437,7 +437,11 @@ inline MachineTypes Chunk::getMachine() const {
   return static_cast<const NonSectionChunk *>(this)->getMachine();
 }
 
-inline chpe_range_type Chunk::getArm64ECRangeType() const {
+inline std::optional<chpe_range_type> Chunk::getArm64ECRangeType() const {
+  // Data sections don't need codemap entries.
+  if (!(getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE))
+    return std::nullopt;
+
   switch (getMachine()) {
   case AMD64:
     return chpe_range_type::Amd64;
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index 43d8e7c1d530859..895a6e00710c634 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -1389,7 +1389,9 @@ void Writer::sortECChunks() {
   for (OutputSection *sec : ctx.outputSections) {
     if (sec->isCodeSection())
       llvm::stable_sort(sec->chunks, [=](const Chunk *a, const Chunk *b) {
-        return a->getArm64ECRangeType() < b->getArm64ECRangeType();
+        std::optional<chpe_range_type> aType = a->getArm64ECRangeType(),
+                                       bType = b->getArm64ECRangeType();
+        return !aType || (bType && *aType < *bType);
       });
   }
 }
diff --git a/lld/test/COFF/arm64ec-codemap.test b/lld/test/COFF/arm64ec-codemap.test
index 424456a6dee66f0..018810a86a34f22 100644
--- a/lld/test/COFF/arm64ec-codemap.test
+++ b/lld/test/COFF/arm64ec-codemap.test
@@ -110,6 +110,40 @@ DISASMM-NEXT:                 ...
 DISASMM-NEXT: 180002ffe: 00 00                        addb    %al, (%rax)
 DISASMM-NEXT: 180003000: b8 06 00 00 00               movl    $0x6, %eax
 
+RUN: lld-link -out:testdm.dll -machine:arm64ec arm64ec-func-sym.obj x86_64-func-sym.obj codemap.obj \
+RUN:          data-sec.obj loadconfig-arm64ec.obj -dll -noentry -merge:.testdata=.text -merge:.rdata=test
+
+RUN: llvm-readobj --coff-load-config testdm.dll | FileCheck -check-prefix=CODEMAPDM %s
+CODEMAPDM:      CodeMap [
+CODEMAPDM-NEXT:     0x2000 - 0x2008  ARM64EC
+CODEMAPDM-NEXT:     0x3000 - 0x3006  X64
+CODEMAPDM-NEXT:     0x5200 - 0x5208  ARM64EC
+CODEMAPDM-NEXT:     0x6000 - 0x6006  X64
+CODEMAPDM-NEXT: ]
+
+Merging code data into code sections causes data to be separated from the code when sorting chunks.
+
+RUN: llvm-objdump -d testdm.dll | FileCheck -check-prefix=DISASMDM %s
+DISASMDM:      Disassembly of section .text:
+DISASMDM-EMPTY:
+DISASMDM-NEXT: 0000000180001000 <.text>:
+DISASMDM-NEXT: 180001000: 00000001     udf     #0x1
+DISASMDM-NEXT:                 ...
+DISASMDM-NEXT: 180002000: 52800040     mov     w0, #0x2
+DISASMDM-NEXT: 180002004: d65f03c0     ret
+DISASMDM-NEXT:                 ...
+DISASMDM-NEXT: 180003000: b8 03 00 00 00               movl    $0x3, %eax
+DISASMDM-NEXT: 180003005: c3                           retq
+DISASMDM-EMPTY:
+DISASMDM-NEXT: Disassembly of section test:
+DISASMDM-EMPTY:
+DISASMDM-NEXT: 0000000180005000 <test>:
+DISASMDM:      180005200: 528000a0     mov     w0, #0x5
+DISASMDM-NEXT: 180005204: d65f03c0     ret
+DISASMDM-NEXT:                 ...
+DISASMDM-NEXT: 180006000: b8 06 00 00 00               movl    $0x6, %eax
+DISASMDM-NEXT: 180006005: c3                           retq
+
 #--- arm64-func-sym.s
     .text
     .globl arm64_func_sym
@@ -148,6 +182,10 @@ x86_64_func_sym2:
     movl $6, %eax
     retq
 
+#--- data-sec.s
+    .section .testdata, "rd"
+    .xword 1
+
 #--- codemap.s
     .section .rdata,"dr"
     .globl code_map

>From 6314e783287b5930045933d1a28f0c370a84b10b Mon Sep 17 00:00:00 2001
From: Jacek Caban <jacek at codeweavers.com>
Date: Fri, 5 May 2023 00:41:47 +0200
Subject: [PATCH 3/3] [lld] Align EC code region boundaries.

Boundaries between code chunks of different architecture are always aligned. 0x1000 seems to be a constant, this does not seem to be affected by any command line alignment argument.
---
 lld/COFF/Writer.cpp                |  9 +++++++++
 lld/test/COFF/arm64ec-codemap.test | 28 ++++++++++++++--------------
 2 files changed, 23 insertions(+), 14 deletions(-)

diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index 895a6e00710c634..960328d686852a3 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -1423,8 +1423,17 @@ void Writer::assignAddresses() {
     // If /FUNCTIONPADMIN is used, functions are padded in order to create a
     // hotpatchable image.
     uint32_t padding = sec->isCodeSection() ? config->functionPadMin : 0;
+    std::optional<chpe_range_type> prevECRange;
 
     for (Chunk *c : sec->chunks) {
+      // Alignment EC code range baudaries.
+      if (isArm64EC(ctx.config.machine) && sec->isCodeSection()) {
+        std::optional<chpe_range_type> rangeType = c->getArm64ECRangeType();
+        if (rangeType != prevECRange) {
+          virtualSize = alignTo(virtualSize, 4096);
+          prevECRange = rangeType;
+        }
+      }
       if (padding && c->isHotPatchable())
         virtualSize += padding;
       virtualSize = alignTo(virtualSize, c->getAlignment());
diff --git a/lld/test/COFF/arm64ec-codemap.test b/lld/test/COFF/arm64ec-codemap.test
index 018810a86a34f22..131644c4983ca3d 100644
--- a/lld/test/COFF/arm64ec-codemap.test
+++ b/lld/test/COFF/arm64ec-codemap.test
@@ -92,7 +92,7 @@ RUN:          codemap3.obj loadconfig-arm64ec.obj -dll -noentry -merge:test=.tex
 RUN: llvm-readobj --coff-load-config testm.dll | FileCheck -check-prefix=CODEMAPM %s
 CODEMAPM:      CodeMap [
 CODEMAPM-NEXT:   0x1000 - 0x1010  ARM64EC
-CODEMAPM-NEXT:   0x2000 - 0x3004  X64
+CODEMAPM-NEXT:   0x2000 - 0x200E  X64
 CODEMAPM-NEXT: ]
 
 RUN: llvm-objdump -d testm.dll | FileCheck -check-prefix=DISASMM %s
@@ -106,9 +106,9 @@ DISASMM-NEXT: 18000100c: d65f03c0     ret
 DISASMM-NEXT:                 ...
 DISASMM-NEXT: 180002000: b8 03 00 00 00               movl    $0x3, %eax
 DISASMM-NEXT: 180002005: c3                           retq
-DISASMM-NEXT:                 ...
-DISASMM-NEXT: 180002ffe: 00 00                        addb    %al, (%rax)
-DISASMM-NEXT: 180003000: b8 06 00 00 00               movl    $0x6, %eax
+DISASMM-NEXT: 180002006: 00 00                        addb    %al, (%rax)
+DISASMM-NEXT: 180002008: b8 06 00 00 00               movl    $0x6, %eax
+DISASMM-NEXT: 18000200d: c3                           retq
 
 RUN: lld-link -out:testdm.dll -machine:arm64ec arm64ec-func-sym.obj x86_64-func-sym.obj codemap.obj \
 RUN:          data-sec.obj loadconfig-arm64ec.obj -dll -noentry -merge:.testdata=.text -merge:.rdata=test
@@ -117,8 +117,8 @@ RUN: llvm-readobj --coff-load-config testdm.dll | FileCheck -check-prefix=CODEMA
 CODEMAPDM:      CodeMap [
 CODEMAPDM-NEXT:     0x2000 - 0x2008  ARM64EC
 CODEMAPDM-NEXT:     0x3000 - 0x3006  X64
-CODEMAPDM-NEXT:     0x5200 - 0x5208  ARM64EC
-CODEMAPDM-NEXT:     0x6000 - 0x6006  X64
+CODEMAPDM-NEXT:     0x6000 - 0x6008  ARM64EC
+CODEMAPDM-NEXT:     0x7000 - 0x7006  X64
 CODEMAPDM-NEXT: ]
 
 Merging code data into code sections causes data to be separated from the code when sorting chunks.
@@ -138,11 +138,11 @@ DISASMDM-EMPTY:
 DISASMDM-NEXT: Disassembly of section test:
 DISASMDM-EMPTY:
 DISASMDM-NEXT: 0000000180005000 <test>:
-DISASMDM:      180005200: 528000a0     mov     w0, #0x5
-DISASMDM-NEXT: 180005204: d65f03c0     ret
+DISASMDM:      180006000: 528000a0     mov     w0, #0x5
+DISASMDM-NEXT: 180006004: d65f03c0     ret
 DISASMDM-NEXT:                 ...
-DISASMDM-NEXT: 180006000: b8 06 00 00 00               movl    $0x6, %eax
-DISASMDM-NEXT: 180006005: c3                           retq
+DISASMDM-NEXT: 180007000: b8 06 00 00 00               movl    $0x6, %eax
+DISASMDM-NEXT: 180007005: c3                           retq
 
 #--- arm64-func-sym.s
     .text
@@ -155,7 +155,7 @@ arm64_func_sym:
 #--- arm64ec-func-sym.s
     .text
     .globl arm64ec_func_sym
-    .p2align 12, 0x0
+    .p2align 2, 0x0
 arm64ec_func_sym:
     mov w0, #2
     ret
@@ -170,14 +170,14 @@ arm64ec_func_sym2:
 #--- x86_64-func-sym.s
     .text
     .globl x86_64_func_sym
-    .p2align 12, 0x0
+    .p2align 2, 0x0
 x86_64_func_sym:
     movl $3, %eax
     retq
 
     .section test, "xr"
     .globl x86_64_func_sym2
-    .p2align 12, 0x0
+    .p2align 2, 0x0
 x86_64_func_sym2:
     movl $6, %eax
     retq
@@ -227,7 +227,7 @@ code_map:
     .rva arm64ec_func_sym + 1
     .word 16
     .rva x86_64_func_sym + 2
-    .word 0x1004
+    .word 14
 
     .globl code_map_count
 code_map_count = 2



More information about the llvm-commits mailing list