[lld] [lld] Add support for EC code map. (PR #69101)

Jacek Caban via llvm-commits llvm-commits at lists.llvm.org
Thu Nov 2 07:43:00 PDT 2023


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

>From 6252d72db441115d8c9a0f33eb6b677ed40267c7 Mon Sep 17 00:00:00 2001
From: Jacek Caban <jacek at codeweavers.com>
Date: Thu, 8 Jun 2023 22:59:53 +0200
Subject: [PATCH] [lld] Add support for EC code map.

---
 lld/COFF/Chunks.cpp                       | 14 +++++
 lld/COFF/Chunks.h                         | 21 +++++++
 lld/COFF/Driver.cpp                       |  5 ++
 lld/COFF/Writer.cpp                       | 66 +++++++++++++++++++++
 lld/test/COFF/Inputs/loadconfig-arm64ec.s |  4 +-
 lld/test/COFF/arm64ec-codemap.test        | 70 ++++++-----------------
 6 files changed, 125 insertions(+), 55 deletions(-)

diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp
index 4e845afa8947a5f..39f4575031be549 100644
--- a/lld/COFF/Chunks.cpp
+++ b/lld/COFF/Chunks.cpp
@@ -896,6 +896,20 @@ void RVAFlagTableChunk::writeTo(uint8_t *buf) const {
          "RVA tables should be de-duplicated");
 }
 
+size_t ECCodeMapChunk::getSize() const {
+  return map.size() * sizeof(chpe_range_entry);
+}
+
+void ECCodeMapChunk::writeTo(uint8_t *buf) const {
+  auto table = reinterpret_cast<chpe_range_entry *>(buf);
+  for (uint32_t i = 0; i < map.size(); i++) {
+    const ECCodeMapEntry &entry = map[i];
+    uint32_t start = entry.first->getRVA();
+    table[i].StartOffset = start | entry.type;
+    table[i].Length = entry.last->getRVA() + entry.last->getSize() - start;
+  }
+}
+
 // MinGW specific, for the "automatic import of variables from DLLs" feature.
 size_t PseudoRelocTableChunk::getSize() const {
   if (relocs.empty())
diff --git a/lld/COFF/Chunks.h b/lld/COFF/Chunks.h
index 156e7a807cb8fd7..7b6bdeae4234ef5 100644
--- a/lld/COFF/Chunks.h
+++ b/lld/COFF/Chunks.h
@@ -703,6 +703,27 @@ class EmptyChunk : public NonSectionChunk {
   void writeTo(uint8_t *buf) const override {}
 };
 
+class ECCodeMapEntry {
+public:
+  ECCodeMapEntry(Chunk *first, Chunk *last, chpe_range_type type)
+      : first(first), last(last), type(type) {}
+  Chunk *first;
+  Chunk *last;
+  chpe_range_type type;
+};
+
+// This is a chunk containing CHPE code map on EC targets. It's a table
+// of address ranges and their types.
+class ECCodeMapChunk : public NonSectionChunk {
+public:
+  ECCodeMapChunk(std::vector<ECCodeMapEntry> &map) : map(map) {}
+  size_t getSize() const override;
+  void writeTo(uint8_t *buf) const override;
+
+private:
+  std::vector<ECCodeMapEntry> ↦
+};
+
 // MinGW specific, for the "automatic import of variables from DLLs" feature.
 // This provides the table of runtime pseudo relocations, for variable
 // references that turned out to need to be imported from a DLL even though
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 5613c2e6993a5af..337b0e6bb0e99b2 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -2355,6 +2355,11 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   ctx.symtab.addAbsolute(mangle("__guard_eh_cont_count"), 0);
   ctx.symtab.addAbsolute(mangle("__guard_eh_cont_table"), 0);
 
+  if (isArm64EC(config->machine)) {
+    ctx.symtab.addAbsolute("__hybrid_code_map", 0);
+    ctx.symtab.addAbsolute("__hybrid_code_map_count", 0);
+  }
+
   if (config->pseudoRelocs) {
     ctx.symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0);
     ctx.symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0);
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index 960328d686852a3..4ff4b9412974aa1 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -221,6 +221,7 @@ class Writer {
                                       uint16_t type, int margin);
   bool createThunks(OutputSection *os, int margin);
   bool verifyRanges(const std::vector<Chunk *> chunks);
+  void createECCodeMap();
   void finalizeAddresses();
   void removeEmptySections();
   void assignOutputSectionIndices();
@@ -229,6 +230,7 @@ class Writer {
   template <typename PEHeaderTy> void writeHeader();
   void createSEHTable();
   void createRuntimePseudoRelocs();
+  void createECChunks();
   void insertCtorDtorSymbols();
   void markSymbolsWithRelocations(ObjFile *file, SymbolRVASet &usedSymbols);
   void createGuardCFTables();
@@ -271,6 +273,7 @@ class Writer {
   std::map<PartialSectionKey, PartialSection *> partialSections;
   std::vector<char> strtab;
   std::vector<llvm::object::coff_symbol16> outputSymtab;
+  std::vector<ECCodeMapEntry> codeMap;
   IdataContents idata;
   Chunk *importTableStart = nullptr;
   uint64_t importTableSize = 0;
@@ -528,6 +531,53 @@ bool Writer::createThunks(OutputSection *os, int margin) {
   return addressesChanged;
 }
 
+// Create a code map for CHPE metadata.
+void Writer::createECCodeMap() {
+  if (!isArm64EC(ctx.config.machine))
+    return;
+
+  // Clear the map in case we were're recomputing the map after adding
+  // a range extension thunk.
+  codeMap.clear();
+
+  std::optional<chpe_range_type> lastType;
+  Chunk *first, *last;
+
+  auto closeRange = [&]() {
+    if (lastType) {
+      codeMap.push_back({first, last, *lastType});
+      lastType.reset();
+    }
+  };
+
+  for (OutputSection *sec : ctx.outputSections) {
+    if (!sec->isCodeSection()) {
+      closeRange();
+      continue;
+    }
+
+    for (Chunk *c : sec->chunks) {
+      // Skip empty section chunks. MSVC does not seem to do that and
+      // generates empty code ranges in some cases.
+      if (isa<SectionChunk>(c) && !c->getSize())
+        continue;
+
+      std::optional<chpe_range_type> chunkType = c->getArm64ECRangeType();
+      if (chunkType != lastType) {
+        closeRange();
+        first = c;
+        lastType = chunkType;
+      }
+      last = c;
+    }
+  }
+
+  closeRange();
+
+  Symbol *tableCountSym = ctx.symtab.findUnderscore("__hybrid_code_map_count");
+  cast<DefinedAbsolute>(tableCountSym)->setVA(codeMap.size());
+}
+
 // Verify that all relocations are in range, with no extra margin requirements.
 bool Writer::verifyRanges(const std::vector<Chunk *> chunks) {
   for (Chunk *c : chunks) {
@@ -1077,6 +1127,9 @@ void Writer::createMiscChunks() {
   if (config->guardCF != GuardCFLevel::Off)
     createGuardCFTables();
 
+  if (isArm64EC(config->machine))
+    createECChunks();
+
   if (config->autoImport)
     createRuntimePseudoRelocs();
 
@@ -1402,6 +1455,10 @@ void Writer::assignAddresses() {
   llvm::TimeTraceScope timeScope("Assign addresses");
   Configuration *config = &ctx.config;
 
+  // We need to create EC code map so that ECCodeMapChunk knows its size.
+  // We do it here to make sure that we account for range extension chunks.
+  createECCodeMap();
+
   sizeOfHeaders = dosStubSize + sizeof(PEMagic) + sizeof(coff_file_header) +
                   sizeof(data_directory) * numberOfDataDirectory +
                   sizeof(coff_section) * ctx.outputSections.size();
@@ -1937,6 +1994,15 @@ void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym,
   cast<DefinedAbsolute>(c)->setVA(tableChunk->getSize() / (hasFlag ? 5 : 4));
 }
 
+// Create CHPE metadata chunks.
+void Writer::createECChunks() {
+  auto codeMapChunk = make<ECCodeMapChunk>(codeMap);
+  rdataSec->addChunk(codeMapChunk);
+  Symbol *codeMapSym = ctx.symtab.findUnderscore("__hybrid_code_map");
+  replaceSymbol<DefinedSynthetic>(codeMapSym, codeMapSym->getName(),
+                                  codeMapChunk);
+}
+
 // MinGW specific. Gather all relocations that are imported from a DLL even
 // though the code didn't expect it to, produce the table that the runtime
 // uses for fixing them up, and provide the synthetic symbols that the
diff --git a/lld/test/COFF/Inputs/loadconfig-arm64ec.s b/lld/test/COFF/Inputs/loadconfig-arm64ec.s
index 1efd02406ca691b..78ae594a21eff3f 100644
--- a/lld/test/COFF/Inputs/loadconfig-arm64ec.s
+++ b/lld/test/COFF/Inputs/loadconfig-arm64ec.s
@@ -64,8 +64,8 @@ __os_arm64x_helper8:
         .p2align 3, 0
 __chpe_metadata:
         .word 1
-        .rva code_map
-        .word code_map_count
+        .rva __hybrid_code_map
+        .word __hybrid_code_map_count
         .word 0 // __x64_code_ranges_to_entry_points
         .word 0 //__arm64x_redirection_metadata
         .rva __os_arm64x_dispatch_call_no_redirect
diff --git a/lld/test/COFF/arm64ec-codemap.test b/lld/test/COFF/arm64ec-codemap.test
index 24eedc6f8e96e20..f2ad0566c8d20a7 100644
--- a/lld/test/COFF/arm64ec-codemap.test
+++ b/lld/test/COFF/arm64ec-codemap.test
@@ -4,16 +4,15 @@ RUN: split-file %s %t.dir && cd %t.dir
 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=arm64ec-windows data-sec.s -o data-sec.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows empty-sec.s -o arm64ec-empty-sec.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 codemap.s -o codemap.obj
-RUN: llvm-mc -filetype=obj -triple=arm64ec-windows codemap2.s -o codemap2.obj
-RUN: llvm-mc -filetype=obj -triple=arm64ec-windows codemap3.s -o codemap3.obj
+RUN: llvm-mc -filetype=obj -triple=x86_64-windows empty-sec.s -o x86_64-empty-sec.obj
 RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
 
 Link ARM64EC DLL and verify that the code is arranged as expected.
 
 RUN: lld-link -out:test.dll -machine:arm64ec arm64ec-func-sym.obj x86_64-func-sym.obj \
-RUN:          codemap.obj loadconfig-arm64ec.obj -dll -noentry
+RUN:          loadconfig-arm64ec.obj -dll -noentry
 
 RUN: llvm-readobj --coff-load-config test.dll | FileCheck -check-prefix=CODEMAP %s
 CODEMAP:       CodeMap [
@@ -45,12 +44,18 @@ DISASM-NEXT: 180006005: c3                           retq
 Order of arguments doesn't matter in this case, chunks are sorted by target type anyway.
 
 RUN: lld-link -out:test2.dll -machine:arm64ec x86_64-func-sym.obj arm64ec-func-sym.obj \
-RUN:          codemap.obj loadconfig-arm64ec.obj -dll -noentry
+RUN:          loadconfig-arm64ec.obj -dll -noentry
 RUN: llvm-readobj --coff-load-config test2.dll | FileCheck -check-prefix=CODEMAP %s
 RUN: llvm-objdump -d test2.dll | FileCheck -check-prefix=DISASM %s
 
 RUN: lld-link -out:testx.dll -machine:arm64x arm64-func-sym.obj arm64ec-func-sym.obj \
-RUN:          x86_64-func-sym.obj codemap2.obj loadconfig-arm64ec.obj -dll -noentry
+RUN:          x86_64-func-sym.obj loadconfig-arm64ec.obj -dll -noentry
+
+Adding empty chunks does not affect code map ranges.
+
+RUN: lld-link -out:test3.dll -machine:arm64ec x86_64-empty-sec.obj arm64ec-empty-sec.obj \
+RUN:          arm64ec-func-sym.obj  x86_64-func-sym.obj loadconfig-arm64ec.obj -dll -noentry
+RUN: llvm-readobj --coff-load-config test3.dll | FileCheck -check-prefix=CODEMAP %s
 
 Do the same with ARM64X target.
 
@@ -88,7 +93,7 @@ DISASMX-NEXT: 180007005: c3                           retq
 Test merged sections.
 
 RUN: lld-link -out:testm.dll -machine:arm64ec arm64ec-func-sym.obj x86_64-func-sym.obj \
-RUN:          codemap3.obj loadconfig-arm64ec.obj -dll -noentry -merge:test=.text
+RUN:          loadconfig-arm64ec.obj -dll -noentry -merge:test=.text
 
 RUN: llvm-readobj --coff-load-config testm.dll | FileCheck -check-prefix=CODEMAPM %s
 CODEMAPM:      CodeMap [
@@ -113,7 +118,7 @@ DISASMM-NEXT: 18000200d: c3                           retq
 
 Merging data sections into code sections causes data to be separated from the code when sorting chunks.
 
-RUN: lld-link -out:testdm.dll -machine:arm64ec arm64ec-func-sym.obj x86_64-func-sym.obj codemap.obj \
+RUN: lld-link -out:testdm.dll -machine:arm64ec arm64ec-func-sym.obj x86_64-func-sym.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
@@ -187,48 +192,7 @@ x86_64_func_sym2:
     .section .testdata, "rd"
     .xword 1
 
-#--- codemap.s
-    .section .rdata,"dr"
-    .globl code_map
-code_map:
-    .rva arm64ec_func_sym + 1
-    .word 8
-    .rva x86_64_func_sym + 2
-    .word 6
-    .rva arm64ec_func_sym2 + 1
-    .word 8
-    .rva x86_64_func_sym2 + 2
-    .word 6
-
-    .globl code_map_count
-code_map_count = 4
-
-#--- codemap2.s
-    .section .rdata,"dr"
-    .globl code_map
-code_map:
-    .rva arm64_func_sym
-    .word 8
-    .rva arm64ec_func_sym + 1
-    .word 8
-    .rva x86_64_func_sym + 2
-    .word 6
-    .rva arm64ec_func_sym2 + 1
-    .word 8
-    .rva x86_64_func_sym2 + 2
-    .word 6
-
-    .globl code_map_count
-code_map_count = 5
-
-#--- codemap3.s
-    .section .rdata,"dr"
-    .globl code_map
-code_map:
-    .rva arm64ec_func_sym + 1
-    .word 16
-    .rva x86_64_func_sym + 2
-    .word 14
-
-    .globl code_map_count
-code_map_count = 2
+#--- empty-sec.s
+    .section .empty1, "xr"
+    .section .empty2, "xr"
+    .section .empty3, "xr"



More information about the llvm-commits mailing list