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] Add support for CHPE redirection metadata. #105739

Merged
merged 1 commit into from
Aug 23, 2024

Conversation

cjacek
Copy link
Contributor

@cjacek cjacek commented Aug 22, 2024

This is part of CHPE metadata containing a sorted list of x86_64 export thunks RVAs and RVAs of ARM64EC functions associated with them. It's stored in a dedicated .a64xrm section.

This is part of CHPE metadata containing a sorted list of x86_64 export thunks RVAs
and RVAs of ARM64EC functions associated with them. It's stored in a dedicated
.a64xrm section.
@llvmbot
Copy link
Member

llvmbot commented Aug 22, 2024

@llvm/pr-subscribers-lld-coff
@llvm/pr-subscribers-lld

@llvm/pr-subscribers-platform-windows

Author: Jacek Caban (cjacek)

Changes

This is part of CHPE metadata containing a sorted list of x86_64 export thunks RVAs and RVAs of ARM64EC functions associated with them. It's stored in a dedicated .a64xrm section.


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

7 Files Affected:

  • (modified) lld/COFF/Chunks.cpp (+13)
  • (modified) lld/COFF/Chunks.h (+11)
  • (modified) lld/COFF/Driver.cpp (+2)
  • (modified) lld/COFF/Writer.cpp (+25-1)
  • (modified) lld/test/COFF/Inputs/loadconfig-arm64ec.s (+2-2)
  • (modified) lld/test/COFF/arm64ec-export-thunks.test (+27-2)
  • (modified) lld/test/COFF/arm64ec-patchable-thunks.test (+5-1)
diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp
index be44950a1720e3..4e3a564ebacd87 100644
--- a/lld/COFF/Chunks.cpp
+++ b/lld/COFF/Chunks.cpp
@@ -1078,4 +1078,17 @@ void ECExportThunkChunk::writeTo(uint8_t *buf) const {
   write32le(buf + 10, target->getRVA() - rva - 14);
 }
 
+size_t CHPERedirectionChunk::getSize() const {
+  return exportThunks.size() * sizeof(chpe_redirection_entry);
+}
+
+void CHPERedirectionChunk::writeTo(uint8_t *buf) const {
+  auto entries = reinterpret_cast<chpe_redirection_entry *>(buf);
+
+  for (uint32_t i = 0; i < exportThunks.size(); i++) {
+    entries[i].Source = exportThunks[i].first->getRVA();
+    entries[i].Destination = exportThunks[i].second->getRVA();
+  }
+}
+
 } // namespace lld::coff
diff --git a/lld/COFF/Chunks.h b/lld/COFF/Chunks.h
index 5443d4619a977e..015df41b04c67d 100644
--- a/lld/COFF/Chunks.h
+++ b/lld/COFF/Chunks.h
@@ -749,6 +749,17 @@ class ECCodeMapChunk : public NonSectionChunk {
   std::vector<ECCodeMapEntry> &map;
 };
 
+class CHPERedirectionChunk : public NonSectionChunk {
+public:
+  CHPERedirectionChunk(std::vector<std::pair<Chunk *, Defined *>> &exportThunks)
+      : exportThunks(exportThunks) {}
+  size_t getSize() const override;
+  void writeTo(uint8_t *buf) const override;
+
+private:
+  std::vector<std::pair<Chunk *, Defined *>> &exportThunks;
+};
+
 static const uint8_t ECExportThunkCode[] = {
     0x48, 0x8b, 0xc4,          // movq    %rsp, %rax
     0x48, 0x89, 0x58, 0x20,    // movq    %rbx, 0x20(%rax)
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index c09c91fe4b1719..472f5074ba8b8c 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -2440,6 +2440,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   if (isArm64EC(config->machine)) {
     ctx.symtab.addAbsolute("__arm64x_extra_rfe_table", 0);
     ctx.symtab.addAbsolute("__arm64x_extra_rfe_table_size", 0);
+    ctx.symtab.addAbsolute("__arm64x_redirection_metadata", 0);
+    ctx.symtab.addAbsolute("__arm64x_redirection_metadata_count", 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 776595d98c391d..358d16fe330cea 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -298,6 +298,9 @@ class Writer {
   CVDebugRecordChunk *buildId = nullptr;
   ArrayRef<uint8_t> sectionTable;
 
+  // List of Arm64EC export thunks.
+  std::vector<std::pair<Chunk *, Defined *>> exportThunks;
+
   uint64_t fileSize;
   uint32_t pointerToSymbolTable = 0;
   uint64_t sizeOfImage;
@@ -312,6 +315,7 @@ class Writer {
   OutputSection *idataSec;
   OutputSection *edataSec;
   OutputSection *didatSec;
+  OutputSection *a64xrmSec;
   OutputSection *rsrcSec;
   OutputSection *relocSec;
   OutputSection *ctorsSec;
@@ -995,6 +999,8 @@ void Writer::createSections() {
   idataSec = createSection(".idata", data | r);
   edataSec = createSection(".edata", data | r);
   didatSec = createSection(".didat", data | r);
+  if (isArm64EC(ctx.config.machine))
+    a64xrmSec = createSection(".a64xrm", data | r);
   rsrcSec = createSection(".rsrc", data | r);
   relocSec = createSection(".reloc", data | discardable | r);
   ctorsSec = createSection(".ctors", data | r | w);
@@ -2053,8 +2059,10 @@ void Writer::createECChunks() {
     auto sym = dyn_cast<Defined>(s);
     if (!sym || !sym->getChunk())
       continue;
-    if (auto thunk = dyn_cast<ECExportThunkChunk>(sym->getChunk()))
+    if (auto thunk = dyn_cast<ECExportThunkChunk>(sym->getChunk())) {
       hexpthkSec->addChunk(thunk);
+      exportThunks.push_back({thunk, thunk->target});
+    }
   }
 
   auto codeMapChunk = make<ECCodeMapChunk>(codeMap);
@@ -2062,6 +2070,13 @@ void Writer::createECChunks() {
   Symbol *codeMapSym = ctx.symtab.findUnderscore("__hybrid_code_map");
   replaceSymbol<DefinedSynthetic>(codeMapSym, codeMapSym->getName(),
                                   codeMapChunk);
+
+  CHPERedirectionChunk *entryPoints = make<CHPERedirectionChunk>(exportThunks);
+  a64xrmSec->addChunk(entryPoints);
+  Symbol *entryPointsSym =
+      ctx.symtab.findUnderscore("__arm64x_redirection_metadata");
+  replaceSymbol<DefinedSynthetic>(entryPointsSym, entryPointsSym->getName(),
+                                  entryPoints);
 }
 
 // MinGW specific. Gather all relocations that are imported from a DLL even
@@ -2154,6 +2169,11 @@ void Writer::setECSymbols() {
   if (!isArm64EC(ctx.config.machine))
     return;
 
+  llvm::stable_sort(exportThunks, [](const std::pair<Chunk *, Defined *> &a,
+                                     const std::pair<Chunk *, Defined *> &b) {
+    return a.first->getRVA() < b.first->getRVA();
+  });
+
   Symbol *rfeTableSym = ctx.symtab.findUnderscore("__arm64x_extra_rfe_table");
   replaceSymbol<DefinedSynthetic>(rfeTableSym, "__arm64x_extra_rfe_table",
                                   pdata.first);
@@ -2165,6 +2185,10 @@ void Writer::setECSymbols() {
         ->setVA(pdata.last->getRVA() + pdata.last->getSize() -
                 pdata.first->getRVA());
   }
+
+  Symbol *entryPointCountSym =
+      ctx.symtab.findUnderscore("__arm64x_redirection_metadata_count");
+  cast<DefinedAbsolute>(entryPointCountSym)->setVA(exportThunks.size());
 }
 
 // Write section contents to a mmap'ed file.
diff --git a/lld/test/COFF/Inputs/loadconfig-arm64ec.s b/lld/test/COFF/Inputs/loadconfig-arm64ec.s
index a270d281095dd6..62a6d0cab642e9 100644
--- a/lld/test/COFF/Inputs/loadconfig-arm64ec.s
+++ b/lld/test/COFF/Inputs/loadconfig-arm64ec.s
@@ -67,7 +67,7 @@ __chpe_metadata:
         .rva __hybrid_code_map
         .word __hybrid_code_map_count
         .word 0 // __x64_code_ranges_to_entry_points
-        .word 0 //__arm64x_redirection_metadata
+        .rva __arm64x_redirection_metadata
         .rva __os_arm64x_dispatch_call_no_redirect
         .rva __os_arm64x_dispatch_ret
         .rva __os_arm64x_check_call
@@ -76,7 +76,7 @@ __chpe_metadata:
         .word 0 // __arm64x_native_entrypoint
         .word 0 // __hybrid_auxiliary_iat
         .word 0 // __x64_code_ranges_to_entry_points_count
-        .word 0 // __arm64x_redirection_metadata_count
+        .word __arm64x_redirection_metadata_count
         .rva __os_arm64x_get_x64_information
         .rva __os_arm64x_set_x64_information
         .rva __arm64x_extra_rfe_table
diff --git a/lld/test/COFF/arm64ec-export-thunks.test b/lld/test/COFF/arm64ec-export-thunks.test
index 6ed0514d4b17f3..2e4cfd6203b751 100644
--- a/lld/test/COFF/arm64ec-export-thunks.test
+++ b/lld/test/COFF/arm64ec-export-thunks.test
@@ -49,7 +49,7 @@ EXP-DISASM-NEXT: 18000301f: cc                           int3
 RUN: llvm-objdump -p exports.dll | FileCheck -check-prefix=EXP-EXPORT %s
 EXP-EXPORT:      Ordinal      RVA  Name
 EXP-EXPORT-NEXT:       1   0x3010  arm64ec_func
-EXP-EXPORT-NEXT:       2   0x6000  data_sym
+EXP-EXPORT-NEXT:       2   0x7000  data_sym
 EXP-EXPORT-NEXT:       3   0x3000  func
 EXP-EXPORT-NEXT:       4   0x2000  x86_64_func
 
@@ -58,9 +58,30 @@ EXP-CHPE:       CodeMap [
 EXP-CHPE-NEXT:    0x1000 - 0x100C  ARM64EC
 EXP-CHPE-NEXT:    0x2000 - 0x3020  X64
 EXP-CHPE-NEXT:  ]
+EXP-CHPE-NEXT:  CodeRangesToEntryPoints: 0
+EXP-CHPE-NEXT:  RedirectionMetadata [
+EXP-CHPE-NEXT:    0x3000 -> 0x1000
+EXP-CHPE-NEXT:    0x3010 -> 0x1000
+EXP-CHPE-NEXT:  ]
+
+RUN: llvm-readobj --sections exports.dll | FileCheck --check-prefix=A64XRM %s
+
+A64XRM:      Name: .a64xrm (2E 61 36 34 78 72 6D 00)
+A64XRM-NEXT: VirtualSize: 0x10
+A64XRM-NEXT: VirtualAddress: 0x6000
+A64XRM-NEXT: RawDataSize: 512
+A64XRM-NEXT: PointerToRawData:
+A64XRM-NEXT: PointerToRelocations: 0x0
+A64XRM-NEXT: PointerToLineNumbers: 0x0
+A64XRM-NEXT: RelocationCount: 0
+A64XRM-NEXT: LineNumberCount: 0
+A64XRM-NEXT: Characteristics [ (0x40000040)
+A64XRM-NEXT:   IMAGE_SCN_CNT_INITIALIZED_DATA (0x40)
+A64XRM-NEXT:   IMAGE_SCN_MEM_READ (0x40000000)
+A64XRM-NEXT: ]
 
 RUN: llvm-objdump -s --section=.test exports.dll | FileCheck --check-prefix=EXP-DATA %s
-EXP-DATA: 180006000 00300000 10300000
+EXP-DATA: 180007000 00300000 10300000
 
 RUN: lld-link -out:exports2.dll -machine:arm64ec antidep-func.obj x86_64-func.obj loadconfig-arm64ec.obj \
 RUN:          arm64ec-data.obj -dll -noentry -export:arm64ec_func -export:func=arm64ec_func \
@@ -100,6 +121,10 @@ ENTRY-CHPE:       CodeMap [
 ENTRY-CHPE-NEXT:    0x1000 - 0x100C  ARM64EC
 ENTRY-CHPE-NEXT:    0x2000 - 0x2010  X64
 ENTRY-CHPE-NEXT:  ]
+ENTRY-CHPE-NEXT:  CodeRangesToEntryPoints: 0
+ENTRY-CHPE-NEXT:  RedirectionMetadata [
+ENTRY-CHPE-NEXT:    0x2000 -> 0x1000
+ENTRY-CHPE-NEXT:  ]
 
 
 Test exporting data symbol as a function:
diff --git a/lld/test/COFF/arm64ec-patchable-thunks.test b/lld/test/COFF/arm64ec-patchable-thunks.test
index cccd42eebfd367..044f3c7cebdf8e 100644
--- a/lld/test/COFF/arm64ec-patchable-thunks.test
+++ b/lld/test/COFF/arm64ec-patchable-thunks.test
@@ -27,13 +27,17 @@ PATCH-DISASM-NEXT: 18000200e: cc                           int3
 PATCH-DISASM-NEXT: 18000200f: cc                           int3
 
 RUN: llvm-readobj --hex-dump=.test test.dll | FileCheck -check-prefix=RVA %s
-RVA: 0x180005000 00200000
+RVA: 0x180006000 00200000
 
 RUN: llvm-readobj --coff-load-config test.dll | FileCheck -check-prefix=PATCH-CHPE %s
 PATCH-CHPE:       CodeMap [
 PATCH-CHPE-NEXT:    0x1000 - 0x1008  ARM64EC
 PATCH-CHPE-NEXT:    0x2000 - 0x2010  X64
 PATCH-CHPE-NEXT:  ]
+PATCH-CHPE-NEXT:  CodeRangesToEntryPoints: 0
+PATCH-CHPE-NEXT:  RedirectionMetadata [
+PATCH-CHPE-NEXT:    0x2000 -> 0x1000
+PATCH-CHPE-NEXT:  ]
 
 
 RUN: lld-link -out:test2.dll -machine:arm64ec arm64ec-alias.obj test-sec.obj loadconfig-arm64ec.obj -dll -noentry

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, this looks quite straightforward. Just one naming question.

@@ -749,6 +749,17 @@ class ECCodeMapChunk : public NonSectionChunk {
std::vector<ECCodeMapEntry> &map;
};

class CHPERedirectionChunk : public NonSectionChunk {
Copy link
Member

Choose a reason for hiding this comment

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

Is this documented somewhere as being named with CHPE, as opposed to ARM64EC in some form?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not aware of this being explicitly documented. It's used in CHPE metadata, which is a somewhat public name; that's how it's referred to in the IMAGE_LOAD_CONFIG_DIRECTORY64 structure. The __chpe_metadata is somewhat public too - it’s explicitly referenced by the linker in the case of hybrid ARM64X images. These images have two separate load configurations but a single CHPE metadata, so the native load config can't directly reference the ARM64EC __chpe_metadata symbol, and the linker needs to look it up and use it to modify the load config instead.

Another place where this is named is in the __arm64x_redirection_metadata symbol from this PR. Note that it uses "arm64x," not "arm64ec." I think there is some ambiguity about what "arm64x" means. I usually use it to refer to hybrid files (as implied by -machine:arm64x) as ARM64X, while PE files containing only ARM64EC code (as implied by -machine:arm64ec) are referred to as ARM64EC. However, I'm no longer sure if this is the intended nomenclature. I’ve seen the ARM64X name used for both image types, so I guess ARM64X would refer to PE files containing ARM64EC code, while ARM64EC is the name of the ABI. As such, using ARM64X here would also be correct.

That said, I can see arguments for using any of the CHPE, ARM64EC, or ARM64X variants. I chose CHPE to emphasize that it's a part of CHPE metadata, but I wouldn't mind renaming it if you feel differently.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks, if this is referred to in the load configs with a CHPE prefix, using these names is totally fine with me.

(I guess historically, the naming may stem from which parts were used in CHPEv1 and which are new for ARM64EC etc.)

@cjacek cjacek merged commit caa844e into llvm:main Aug 23, 2024
12 checks passed
@cjacek cjacek deleted the arm64ec-redirection branch August 23, 2024 18:29
5chmidti pushed a commit that referenced this pull request Aug 24, 2024
This is part of CHPE metadata containing a sorted list of x86_64 export
thunks RVAs and RVAs of ARM64EC functions associated with them. It's
stored in a dedicated .a64xrm section.
dmpolukhin pushed a commit to dmpolukhin/llvm-project that referenced this pull request Sep 2, 2024
This is part of CHPE metadata containing a sorted list of x86_64 export
thunks RVAs and RVAs of ARM64EC functions associated with them. It's
stored in a dedicated .a64xrm section.
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.

3 participants