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

>From 4c73c72726d6e76041a106700b4322c6cc0b2665 Mon Sep 17 00:00:00 2001
From: Jacek Caban <[email protected]>
Date: Wed, 27 May 2026 16:07:54 +0200
Subject: [PATCH 1/3] [Archive][COFF] Split hybrid COFF files when adding them
 to an archive

Create a separate member for the embedded hybrid object so that it's
properly reflected in the archive's symbol map and can be correctly
processed by tools unaware of multi-arch object files. Prefix the
embedded member name with 'llvm.arm64x/' to avoid name collisions.
---
 llvm/include/llvm/Object/COFF.h               |   1 +
 llvm/lib/Object/ArchiveWriter.cpp             |  82 ++++++--
 llvm/lib/Object/COFFObjectFile.cpp            |  67 +++++++
 llvm/test/tools/llvm-ar/arm64x-hybridobj.yaml |  58 ++++++
 llvm/test/tools/llvm-ar/arm64x-split.yaml     | 184 ++++++++++++++++++
 .../test/tools/llvm-lib/arm64x-hybridobj.yaml |  49 +++++
 6 files changed, 420 insertions(+), 21 deletions(-)
 create mode 100644 llvm/test/tools/llvm-ar/arm64x-hybridobj.yaml
 create mode 100644 llvm/test/tools/llvm-ar/arm64x-split.yaml
 create mode 100644 llvm/test/tools/llvm-lib/arm64x-hybridobj.yaml

diff --git a/llvm/include/llvm/Object/COFF.h b/llvm/include/llvm/Object/COFF.h
index dea922553ca3d..02efa1ea55612 100644
--- a/llvm/include/llvm/Object/COFF.h
+++ b/llvm/include/llvm/Object/COFF.h
@@ -1103,6 +1103,7 @@ class LLVM_ABI COFFObjectFile : public ObjectFile {
   }
   std::unique_ptr<MemoryBuffer> getHybridObjectView() const;
   std::optional<MemoryBufferRef> findHybridObjectSection() const;
+  std::unique_ptr<MemoryBuffer> stripHybridSection() const;
 
   import_directory_iterator import_directory_begin() const;
   import_directory_iterator import_directory_end() const;
diff --git a/llvm/lib/Object/ArchiveWriter.cpp 
b/llvm/lib/Object/ArchiveWriter.cpp
index 0ebe2d2ba37a3..3b2d8c30bb301 100644
--- a/llvm/lib/Object/ArchiveWriter.cpp
+++ b/llvm/lib/Object/ArchiveWriter.cpp
@@ -301,24 +301,24 @@ static bool is64BitKind(object::Archive::Kind Kind) {
 static void
 printMemberHeader(raw_ostream &Out, uint64_t Pos, raw_ostream &StringTable,
                   StringMap<uint64_t> &MemberNames, object::Archive::Kind Kind,
-                  bool Thin, const NewArchiveMember &M,
+                  bool Thin, const NewArchiveMember &M, StringRef MemberName,
                   sys::TimePoint<std::chrono::seconds> ModTime, uint64_t Size) 
{
   if (isBSDLike(Kind))
-    return printBSDMemberHeader(Out, Pos, M.MemberName, ModTime, M.UID, M.GID,
+    return printBSDMemberHeader(Out, Pos, MemberName, ModTime, M.UID, M.GID,
                                 M.Perms, Size);
-  if (!useStringTable(Thin, M.MemberName))
-    return printGNUSmallMemberHeader(Out, M.MemberName, ModTime, M.UID, M.GID,
+  if (!useStringTable(Thin, MemberName))
+    return printGNUSmallMemberHeader(Out, MemberName, ModTime, M.UID, M.GID,
                                      M.Perms, Size);
   Out << '/';
   uint64_t NamePos;
   if (Thin) {
     NamePos = StringTable.tell();
-    StringTable << M.MemberName << "/\n";
+    StringTable << MemberName << "/\n";
   } else {
-    auto Insertion = MemberNames.insert({M.MemberName, uint64_t(0)});
+    auto Insertion = MemberNames.insert({MemberName, uint64_t(0)});
     if (Insertion.second) {
       Insertion.first->second = StringTable.tell();
-      StringTable << M.MemberName;
+      StringTable << MemberName;
       if (isCOFFArchive(Kind))
         StringTable << '\0';
       else
@@ -338,6 +338,8 @@ struct MemberData {
   StringRef Padding;
   uint64_t PreHeadPadSize = 0;
   std::unique_ptr<SymbolicFile> SymFile = nullptr;
+  std::string HybridName = "";
+  std::unique_ptr<MemoryBuffer> NativeBuf = nullptr;
 };
 } // namespace
 
@@ -846,6 +848,7 @@ computeMemberData(raw_ostream &StringTable, raw_ostream 
&SymNames,
 
   for (const NewArchiveMember &M : NewMembers) {
     MemberData &D = Ret.emplace_back();
+    D.Data = M.Buf->getBuffer();
 
     if (NeedSymbols != SymtabWritingMode::NoSymtab || isAIXBigArchive(Kind)) {
       Expected<std::unique_ptr<SymbolicFile>> SymFileOrErr = getSymbolicFile(
@@ -855,6 +858,35 @@ computeMemberData(raw_ostream &StringTable, raw_ostream 
&SymNames,
       if (!SymFileOrErr)
         return createFileError(M.MemberName, SymFileOrErr.takeError());
       D.SymFile = std::move(*SymFileOrErr);
+
+      if (SymMap && D.SymFile.get()) {
+        auto COFFObj = dyn_cast<COFFObjectFile>(D.SymFile.get());
+        std::optional<MemoryBufferRef> HybridView;
+        if (COFFObj && (HybridView = COFFObj->findHybridObjectSection())) {
+          // Strip the hybrid section.
+          D.NativeBuf = COFFObj->stripHybridSection();
+          D.Data = D.NativeBuf->getBuffer();
+
+          // Create a separate archive member for the hybrid ARM64X object.
+          MemberData &ECData = Ret.emplace_back();
+          ECData.Data = HybridView->getBuffer();
+
+          SymFileOrErr =
+              getSymbolicFile(*HybridView, Context, Kind, [&](Error Err) {
+                Warn(createFileError(M.MemberName, std::move(Err)));
+              });
+          if (!SymFileOrErr)
+            return createFileError(M.MemberName, SymFileOrErr.takeError());
+          ECData.SymFile = std::move(*SymFileOrErr);
+
+          // Use obj.arm64ec subdirectory for the hybrid object name.
+          size_t Pos = M.MemberName.find_last_of("/\\");
+          Pos = Pos == StringRef::npos ? 0 : Pos + 1;
+          ECData.HybridName = (M.MemberName.substr(0, Pos) + "obj.arm64ec/" +
+                               M.MemberName.substr(Pos))
+                                  .str();
+        }
+      }
     }
   }
 
@@ -887,13 +919,19 @@ computeMemberData(raw_ostream &StringTable, raw_ostream 
&SymNames,
   uint64_t PrevOffset = 0;
   uint64_t NextMemHeadPadSize = 0;
 
-  for (uint32_t Index = 0; Index < Ret.size(); ++Index) {
+  for (uint32_t Index = 0, MemberIndex = 0; Index < Ret.size(); ++Index) {
     MemberData &D = Ret[Index];
-    const NewArchiveMember *M = &NewMembers[Index];
+    const NewArchiveMember *M = &NewMembers[MemberIndex];
+    // Native COFF members (resulting from stripping a hybrid object section)
+    // are followed by an extracted hybrid object member, using the same
+    // NewArchiveMember.
+    if (!D.NativeBuf.get())
+      ++MemberIndex;
     raw_string_ostream Out(D.Header);
 
-    MemoryBufferRef Buf = M->Buf->getMemBufferRef();
-    D.Data = Thin ? "" : Buf.getBuffer();
+    uint64_t Size = D.Data.size();
+    if (Thin)
+      D.Data = "";
 
     // ld64 expects the members to be 8-byte aligned for 64-bit content and at
     // least 4-byte aligned for 32-bit content.  Opt for the larger encoding
@@ -905,17 +943,19 @@ computeMemberData(raw_ostream &StringTable, raw_ostream 
&SymNames,
         offsetToAlignment(D.Data.size() + MemberPadding, Align(2));
     D.Padding = StringRef(PaddingData, MemberPadding + TailPadding);
 
+    StringRef MemberName = D.HybridName.size() ? D.HybridName : M->MemberName;
+
     sys::TimePoint<std::chrono::seconds> ModTime;
     if (UniqueTimestamps)
       // Increment timestamp for each file of a given name.
-      ModTime = sys::toTimePoint(FilenameCount[M->MemberName]++);
+      ModTime = sys::toTimePoint(FilenameCount[MemberName]++);
     else
       ModTime = M->ModTime;
 
-    uint64_t Size = Buf.getBufferSize() + MemberPadding;
+    Size += MemberPadding;
     if (Size > object::Archive::MaxMemberSize) {
       std::string StringMsg =
-          "File " + M->MemberName.str() + " exceeds size limit";
+          "File " + MemberName.str() + " exceeds size limit";
       return make_error<object::GenericBinaryError>(
           std::move(StringMsg), object::object_error::parse_failed);
     }
@@ -923,8 +963,8 @@ computeMemberData(raw_ostream &StringTable, raw_ostream 
&SymNames,
     // In the big archive file format, we need to calculate and include the 
next
     // member offset and previous member offset in the file member header.
     if (isAIXBigArchive(Kind)) {
-      uint64_t OffsetToMemData = Pos + sizeof(object::BigArMemHdrType) +
-                                 alignTo(M->MemberName.size(), 2);
+      uint64_t OffsetToMemData =
+          Pos + sizeof(object::BigArMemHdrType) + alignTo(MemberName.size(), 
2);
 
       if (Index == 0)
         NextMemHeadPadSize =
@@ -935,33 +975,33 @@ computeMemberData(raw_ostream &StringTable, raw_ostream 
&SymNames,
       D.PreHeadPadSize = NextMemHeadPadSize;
       Pos += D.PreHeadPadSize;
       uint64_t NextOffset = Pos + sizeof(object::BigArMemHdrType) +
-                            alignTo(M->MemberName.size(), 2) + alignTo(Size, 
2);
+                            alignTo(MemberName.size(), 2) + alignTo(Size, 2);
 
       // If there is another member file after this, we need to calculate the
       // padding before the header.
       if (Index + 1 != Ret.size()) {
         uint64_t OffsetToNextMemData =
             NextOffset + sizeof(object::BigArMemHdrType) +
-            alignTo(NewMembers[Index + 1].MemberName.size(), 2);
+            alignTo(NewMembers[MemberIndex].MemberName.size(), 2);
         NextMemHeadPadSize =
             alignToPowerOf2(OffsetToNextMemData,
                             getMemberAlignment(Ret[Index + 1].SymFile.get())) -
             OffsetToNextMemData;
         NextOffset += NextMemHeadPadSize;
       }
-      printBigArchiveMemberHeader(Out, M->MemberName, ModTime, M->UID, M->GID,
+      printBigArchiveMemberHeader(Out, MemberName, ModTime, M->UID, M->GID,
                                   M->Perms, Size, PrevOffset, NextOffset);
       PrevOffset = Pos;
     } else {
       printMemberHeader(Out, Pos, StringTable, MemberNames, Kind, Thin, *M,
-                        ModTime, Size);
+                        MemberName, ModTime, Size);
     }
 
     if (NeedSymbols != SymtabWritingMode::NoSymtab) {
       Expected<std::vector<unsigned>> SymbolsOrErr =
           getSymbols(D.SymFile.get(), Index + 1, SymNames, SymMap);
       if (!SymbolsOrErr)
-        return createFileError(M->MemberName, SymbolsOrErr.takeError());
+        return createFileError(MemberName, SymbolsOrErr.takeError());
       D.Symbols = std::move(*SymbolsOrErr);
       if (D.SymFile)
         HasObject = true;
diff --git a/llvm/lib/Object/COFFObjectFile.cpp 
b/llvm/lib/Object/COFFObjectFile.cpp
index 5cf10b6e95ab7..0231206ea1e79 100644
--- a/llvm/lib/Object/COFFObjectFile.cpp
+++ b/llvm/lib/Object/COFFObjectFile.cpp
@@ -1580,6 +1580,73 @@ std::optional<MemoryBufferRef> 
COFFObjectFile::findHybridObjectSection() const {
   return std::nullopt;
 }
 
+std::unique_ptr<MemoryBuffer> COFFObjectFile::stripHybridSection() const {
+  if (getDOSHeader() || getMachine() != COFF::IMAGE_FILE_MACHINE_ARM64)
+    return nullptr;
+
+  for (SectionRef S : sections()) {
+    Expected<StringRef> Name = S.getName();
+    if (errorToBool(Name.takeError()) || *Name != kArm64ECSectionName)
+      continue;
+
+    const coff_section *HybridSec = getCOFFSection(S);
+    if (!HybridSec->SizeOfRawData)
+      continue;
+
+    // Copy the original buffer, skipping the hybrid section content.
+    // The section itself is still present in the COFF headers, so section
+    // indices are not affected by the change, but its size is reduced
+    // to 0.
+    std::unique_ptr<WritableMemoryBuffer> NativeView =
+        WritableMemoryBuffer::getNewUninitMemBuffer(Data.getBufferSize() -
+                                                    HybridSec->SizeOfRawData);
+    uint32_t HybridEnd = HybridSec->PointerToRawData + 
HybridSec->SizeOfRawData;
+    memcpy(NativeView->getBufferStart(), Data.getBufferStart(),
+           HybridSec->PointerToRawData);
+    memcpy(NativeView->getBufferStart() + HybridSec->PointerToRawData,
+           Data.getBufferStart() + HybridEnd, Data.getBufferSize() - 
HybridEnd);
+
+    auto getTargetPtr = [&](const void *Ptr) {
+      return NativeView->getBufferStart() +
+             (reinterpret_cast<const char *>(Ptr) - Data.getBufferStart());
+    };
+
+    // Adjust the COFF header, if necessary.
+    if (COFFHeader) {
+      auto Header =
+          reinterpret_cast<coff_file_header *>(getTargetPtr(COFFHeader));
+      if (Header->PointerToSymbolTable >= HybridEnd)
+        Header->PointerToSymbolTable -= HybridSec->SizeOfRawData;
+    } else {
+      auto Header = reinterpret_cast<coff_bigobj_file_header *>(
+          getTargetPtr(COFFBigObjHeader));
+      if (Header->PointerToSymbolTable >= HybridEnd)
+        Header->PointerToSymbolTable -= HybridSec->SizeOfRawData;
+    }
+
+    // Adjust section headers.
+    auto COFFSec = reinterpret_cast<coff_section *>(getTargetPtr(HybridSec));
+    COFFSec->VirtualSize = 0;
+    COFFSec->PointerToRawData = 0;
+    COFFSec->SizeOfRawData = 0;
+
+    for (SectionRef Sec : sections()) {
+      COFFSec =
+          reinterpret_cast<coff_section *>(getTargetPtr(getCOFFSection(Sec)));
+      if (COFFSec->PointerToRawData >= HybridEnd)
+        COFFSec->PointerToRawData -= HybridSec->SizeOfRawData;
+      if (COFFSec->PointerToRelocations >= HybridEnd)
+        COFFSec->PointerToRelocations -= HybridSec->SizeOfRawData;
+      if (COFFSec->PointerToLinenumbers >= HybridEnd)
+        COFFSec->PointerToLinenumbers -= HybridSec->SizeOfRawData;
+    }
+
+    return NativeView;
+  }
+
+  return nullptr;
+}
+
 bool ImportDirectoryEntryRef::
 operator==(const ImportDirectoryEntryRef &Other) const {
   return ImportTable == Other.ImportTable && Index == Other.Index;
diff --git a/llvm/test/tools/llvm-ar/arm64x-hybridobj.yaml 
b/llvm/test/tools/llvm-ar/arm64x-hybridobj.yaml
new file mode 100644
index 0000000000000..89d6fb508846a
--- /dev/null
+++ b/llvm/test/tools/llvm-ar/arm64x-hybridobj.yaml
@@ -0,0 +1,58 @@
+## Create a hybrid ARM64/ARM64EC object file and verify that it is split when 
added to an archive.
+# RUN: yaml2obj -DMACHINE=IMAGE_FILE_MACHINE_ARM64 %s -o %t-aarch64.o
+# RUN: yaml2obj -DMACHINE=IMAGE_FILE_MACHINE_ARM64EC %s -o %t-arm64ec.o
+# RUN: llvm-objcopy --add-section=.obj.arm64ec=%t-arm64ec.o 
--set-section-flags=.obj.arm64ec=debug %t-aarch64.o %t.o
+# RUN: rm -f %t.lib %t-s.lib
+# RUN: llvm-ar rc %t.lib %t.o
+# RUN: llvm-nm --print-armap %t.lib | FileCheck %s
+
+## Check that the object file is split when creating a symbol table.
+# RUN: llvm-ar rcS %t-s.lib %t.o
+# RUN: llvm-nm --print-armap %t-s.lib | FileCheck --check-prefix=NOSPLIT %s
+# NOSPLIT-NOT: obj.arm64ec
+# RUN: llvm-ar s %t-s.lib
+# RUN: llvm-nm --print-armap %t-s.lib | FileCheck %s
+
+# CHECK:      Archive map
+# CHECK-NEXT: sym in arm64x-hybridobj.yaml.tmp.o
+# CHECK-EMPTY:
+# CHECK-NEXT: Archive EC map
+# CHECK-NEXT: sym in obj.arm64ec/arm64x-hybridobj.yaml.tmp.o
+# CHECK-EMPTY:
+# CHECK-EMPTY:
+# CHECK-NEXT: arm64x-hybridobj.yaml.tmp.o:
+# CHECK-NEXT: 00000000 D sym
+# CHECK-EMPTY:
+# CHECK-NEXT: obj.arm64ec/arm64x-hybridobj.yaml.tmp.o:
+# CHECK-NEXT: 00000000 D sym
+
+--- !COFF
+header:
+  Machine:         [[MACHINE]]
+  Characteristics: [  ]
+sections:
+  - Name:            .data
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, 
IMAGE_SCN_MEM_WRITE ]
+    Alignment:       4
+    SectionData:     '00000000'
+    SizeOfRawData:   4
+symbols:
+  - Name:            .data
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          4
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        0
+      Number:          2
+  - Name:            sym
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/llvm/test/tools/llvm-ar/arm64x-split.yaml 
b/llvm/test/tools/llvm-ar/arm64x-split.yaml
new file mode 100644
index 0000000000000..607d6f3d9954f
--- /dev/null
+++ b/llvm/test/tools/llvm-ar/arm64x-split.yaml
@@ -0,0 +1,184 @@
+## Test splitting a hybrid ARM64X object file with a .obj.arm64ec section that 
is not the
+## last section, thereby requiring more fixups.
+
+# RUN: yaml2obj %s -o %t.o
+# RUN: rm -f %t.lib
+# RUN: llvm-ar cr %t.lib %t.o
+# RUN: llvm-readobj --sections --symbols --relocations --hex-dump=.data %t.lib 
| FileCheck %s
+
+# CHECK:      File: {{.*}}(arm64x-split.yaml.tmp.o)
+# CHECK-NEXT: Format: COFF-ARM64
+# CHECK-NEXT: Arch: aarch64
+# CHECK-NEXT: AddressSize: 64bit
+# CHECK-NEXT: Sections [
+# CHECK-NEXT:   Section {
+# CHECK-NEXT:     Number: 1
+# CHECK-NEXT:     Name: .obj.arm64ec (2F 34 00 00 00 00 00 00)
+# CHECK-NEXT:     VirtualSize: 0x0
+# CHECK-NEXT:     VirtualAddress: 0x0
+# CHECK-NEXT:     RawDataSize: 0
+# CHECK-NEXT:     PointerToRawData: 0x0
+# CHECK-NEXT:     PointerToRelocations: 0x0
+# CHECK-NEXT:     PointerToLineNumbers: 0x0
+# CHECK-NEXT:     RelocationCount: 0
+# CHECK-NEXT:     LineNumberCount: 0
+# CHECK-NEXT:     Characteristics [ (0xC2000040)
+# CHECK-NEXT:       IMAGE_SCN_CNT_INITIALIZED_DATA (0x40)
+# CHECK-NEXT:       IMAGE_SCN_MEM_DISCARDABLE (0x2000000)
+# CHECK-NEXT:       IMAGE_SCN_MEM_READ (0x40000000)
+# CHECK-NEXT:       IMAGE_SCN_MEM_WRITE (0x80000000)
+# CHECK-NEXT:     ]
+# CHECK-NEXT:   }
+# CHECK-NEXT:   Section {
+# CHECK-NEXT:     Number: 2
+# CHECK-NEXT:     Name: .data (2E 64 61 74 61 00 00 00)
+# CHECK-NEXT:     VirtualSize: 0x0
+# CHECK-NEXT:     VirtualAddress: 0x0
+# CHECK-NEXT:     RawDataSize: 4
+# CHECK-NEXT:     PointerToRawData: 0x66
+# CHECK-NEXT:     PointerToRelocations: 0x6A
+# CHECK-NEXT:     PointerToLineNumbers: 0x0
+# CHECK-NEXT:     RelocationCount: 1
+# CHECK-NEXT:     LineNumberCount: 0
+# CHECK-NEXT:     Characteristics [ (0xC0300040)
+# CHECK-NEXT:       IMAGE_SCN_ALIGN_4BYTES (0x300000)
+# CHECK-NEXT:       IMAGE_SCN_CNT_INITIALIZED_DATA (0x40)
+# CHECK-NEXT:       IMAGE_SCN_MEM_READ (0x40000000)
+# CHECK-NEXT:       IMAGE_SCN_MEM_WRITE (0x80000000)
+# CHECK-NEXT:     ]
+# CHECK-NEXT:   }
+# CHECK-NEXT: ]
+# CHECK-NEXT: Relocations [
+# CHECK-NEXT:   Section (2) .data {
+# CHECK-NEXT:     0x0 IMAGE_REL_ARM64_PAGEBASE_REL21 sym (2)
+# CHECK-NEXT:   }
+# CHECK-NEXT: ]
+# CHECK-NEXT: Symbols [
+# CHECK-NEXT:   Symbol {
+# CHECK-NEXT:     Name: .data
+# CHECK-NEXT:     Value: 0
+# CHECK-NEXT:     Section: .data (2)
+# CHECK-NEXT:     BaseType: Null (0x0)
+# CHECK-NEXT:     ComplexType: Null (0x0)
+# CHECK-NEXT:     StorageClass: Static (0x3)
+# CHECK-NEXT:     AuxSymbolCount: 1
+# CHECK-NEXT:     AuxSectionDef {
+# CHECK-NEXT:       Length: 4
+# CHECK-NEXT:       RelocationCount: 0
+# CHECK-NEXT:       LineNumberCount: 0
+# CHECK-NEXT:       Checksum: 0x0
+# CHECK-NEXT:       Number: 1
+# CHECK-NEXT:       Selection: 0x0
+# CHECK-NEXT:     }
+# CHECK-NEXT:   }
+# CHECK-NEXT:   Symbol {
+# CHECK-NEXT:     Name: sym
+# CHECK-NEXT:     Value: 0
+# CHECK-NEXT:     Section: .data (2)
+# CHECK-NEXT:     BaseType: Null (0x0)
+# CHECK-NEXT:     ComplexType: Null (0x0)
+# CHECK-NEXT:     StorageClass: External (0x2)
+# CHECK-NEXT:     AuxSymbolCount: 0
+# CHECK-NEXT:   }
+# CHECK-NEXT: ]
+# CHECK-EMPTY:
+# CHECK-NEXT: Hex dump of section '.data':
+# CHECK-NEXT: 0x00000000 12345678                            .4Vx
+# CHECK-EMPTY:
+# CHECK-NEXT: File: {{.*}}(obj.arm64ec/arm64x-split.yaml.tmp.o)
+# CHECK-NEXT: Format: COFF-ARM64EC
+# CHECK-NEXT: Arch: aarch64
+# CHECK-NEXT: AddressSize: 64bit
+# CHECK-NEXT: Sections [
+# CHECK-NEXT:   Section {
+# CHECK-NEXT:     Number: 1
+# CHECK-NEXT:     Name: .data (2E 64 61 74 61 00 00 00)
+# CHECK-NEXT:     VirtualSize: 0x0
+# CHECK-NEXT:     VirtualAddress: 0x0
+# CHECK-NEXT:     RawDataSize: 4
+# CHECK-NEXT:     PointerToRawData: 0x3C
+# CHECK-NEXT:     PointerToRelocations: 0x0
+# CHECK-NEXT:     PointerToLineNumbers: 0x0
+# CHECK-NEXT:     RelocationCount: 0
+# CHECK-NEXT:     LineNumberCount: 0
+# CHECK-NEXT:     Characteristics [ (0xC0300040)
+# CHECK-NEXT:       IMAGE_SCN_ALIGN_4BYTES (0x300000)
+# CHECK-NEXT:       IMAGE_SCN_CNT_INITIALIZED_DATA (0x40)
+# CHECK-NEXT:       IMAGE_SCN_MEM_READ (0x40000000)
+# CHECK-NEXT:       IMAGE_SCN_MEM_WRITE (0x80000000)
+# CHECK-NEXT:     ]
+# CHECK-NEXT:   }
+# CHECK-NEXT: ]
+# CHECK-NEXT: Relocations [
+# CHECK-NEXT: ]
+# CHECK-NEXT: Symbols [
+# CHECK-NEXT:   Symbol {
+# CHECK-NEXT:     Name: .data
+# CHECK-NEXT:     Value: 0
+# CHECK-NEXT:     Section: .data (1)
+# CHECK-NEXT:     BaseType: Null (0x0)
+# CHECK-NEXT:     ComplexType: Null (0x0)
+# CHECK-NEXT:     StorageClass: Static (0x3)
+# CHECK-NEXT:     AuxSymbolCount: 1
+# CHECK-NEXT:     AuxSectionDef {
+# CHECK-NEXT:       Length: 4
+# CHECK-NEXT:       RelocationCount: 0
+# CHECK-NEXT:       LineNumberCount: 0
+# CHECK-NEXT:       Checksum: 0x0
+# CHECK-NEXT:       Number: 2
+# CHECK-NEXT:       Selection: 0x0
+# CHECK-NEXT:     }
+# CHECK-NEXT:   }
+# CHECK-NEXT:   Symbol {
+# CHECK-NEXT:     Name: sym
+# CHECK-NEXT:     Value: 0
+# CHECK-NEXT:     Section: .data (1)
+# CHECK-NEXT:     BaseType: Null (0x0)
+# CHECK-NEXT:     ComplexType: Null (0x0)
+# CHECK-NEXT:     StorageClass: External (0x2)
+# CHECK-NEXT:     AuxSymbolCount: 0
+# CHECK-NEXT:   }
+# CHECK-NEXT: ]
+# CHECK-EMPTY:
+# CHECK-NEXT: Hex dump of section '.data':
+# CHECK-NEXT: 0x00000000 12345678                            .4Vx
+
+--- !COFF
+header:
+  Machine:         IMAGE_FILE_MACHINE_ARM64
+  Characteristics: [  ]
+sections:
+  - Name:            .obj.arm64ec
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, 
IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+    VirtualSize:     122
+    SectionData:     
41A60100000000004000000003000000000000002E646174610000000000000000000000040000003C000000000000000000000000000000400030C0123456782E646174610000000000000001000000030104000000000000000000000002000000000073796D00000000000000000001000000020004000000
+    SizeOfRawData:   122
+  - Name:            .data
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, 
IMAGE_SCN_MEM_WRITE ]
+    Alignment:       4
+    SectionData:     '12345678'
+    SizeOfRawData:   4
+    Relocations:
+      - VirtualAddress: 0
+        SymbolName: sym
+        Type: IMAGE_REL_ARM64_PAGEBASE_REL21
+symbols:
+  - Name:            .data
+    Value:           0
+    SectionNumber:   2
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          4
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        0
+      Number:          1
+  - Name:            sym
+    Value:           0
+    SectionNumber:   2
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/llvm/test/tools/llvm-lib/arm64x-hybridobj.yaml 
b/llvm/test/tools/llvm-lib/arm64x-hybridobj.yaml
new file mode 100644
index 0000000000000..52ca0c5990030
--- /dev/null
+++ b/llvm/test/tools/llvm-lib/arm64x-hybridobj.yaml
@@ -0,0 +1,49 @@
+# RUN: yaml2obj -DMACHINE=IMAGE_FILE_MACHINE_ARM64 %s -o %t-aarch64.o
+# RUN: yaml2obj -DMACHINE=IMAGE_FILE_MACHINE_ARM64EC %s -o %t-arm64ec.o
+# RUN: llvm-objcopy --add-section=.obj.arm64ec=%t-arm64ec.o 
--set-section-flags=.obj.arm64ec=debug %t-aarch64.o %t.o
+# RUN: llvm-lib -machine:arm64x -out:%t.lib %t.o
+# RUN: llvm-nm --print-armap %t.lib | FileCheck %s
+
+# CHECK:      Archive map
+# CHECK-NEXT: sym in {{.*}}arm64x-hybridobj.yaml.tmp.o
+# CHECK-EMPTY:
+# CHECK-NEXT: Archive EC map
+# CHECK-NEXT: sym in {{.*}}obj.arm64ec/arm64x-hybridobj.yaml.tmp.o
+# CHECK-EMPTY:
+# CHECK-EMPTY:
+# CHECK-NEXT: {{.*}}arm64x-hybridobj.yaml.tmp.o:
+# CHECK-NEXT: 00000000 D sym
+# CHECK-EMPTY:
+# CHECK-NEXT: {{.*}}obj.arm64ec/arm64x-hybridobj.yaml.tmp.o:
+# CHECK-NEXT: 00000000 D sym
+
+--- !COFF
+header:
+  Machine:         [[MACHINE]]
+  Characteristics: [  ]
+sections:
+  - Name:            .data
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, 
IMAGE_SCN_MEM_WRITE ]
+    Alignment:       4
+    SectionData:     '00000000'
+    SizeOfRawData:   4
+symbols:
+  - Name:            .data
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          4
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        0
+      Number:          2
+  - Name:            sym
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+...

>From 045ce538f74033c484ca4e06b9f0c751fd563e3f Mon Sep 17 00:00:00 2001
From: Jacek Caban <[email protected]>
Date: Mon, 8 Jun 2026 18:02:40 +0200
Subject: [PATCH 2/3] [LLD][COFF] Add support for multi-atch ARM64X object
 files

---
 lld/COFF/Driver.cpp              | 11 +++++
 lld/test/COFF/arm64x-hybridobj.s | 74 ++++++++++++++++++++++++++++++++
 2 files changed, 85 insertions(+)
 create mode 100644 lld/test/COFF/arm64x-hybridobj.s

diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 38dad4ef71050..b4d55cd175007 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -269,6 +269,17 @@ InputFile *LinkerDriver::addObjectFile(COFFLinkerContext 
&ctx,
   std::unique_ptr<COFFObjectFile> coffObj = ObjFile::createCOFFObject(ctx, mb);
   InputFile *obj = nullptr;
 
+  if (ctx.symtab.isEC()) {
+    if (std::optional<MemoryBufferRef> hybridSec =
+            coffObj->findHybridObjectSection()) {
+      InputFile *hybridObj =
+          addObjectFile(ctx, *hybridSec, archiveName, offsetInArchive, lazy);
+      // For the ARM64X target, continue processing the native file.
+      if (ctx.config.machine != ARM64X)
+        return hybridObj;
+    }
+  }
+
   if (ctx.config.fatLTOObjects) {
     Expected<MemoryBufferRef> fatLTOData =
         IRObjectFile::findBitcodeInObject(*coffObj);
diff --git a/lld/test/COFF/arm64x-hybridobj.s b/lld/test/COFF/arm64x-hybridobj.s
new file mode 100644
index 0000000000000..fe6fda3ec415d
--- /dev/null
+++ b/lld/test/COFF/arm64x-hybridobj.s
@@ -0,0 +1,74 @@
+// REQUIRES: aarch64
+// RUN: split-file %s %t.dir && cd %t.dir
+
+// RUN: llvm-mc -filetype=obj -triple=aarch64-windows sym.s -o sym-arm64.obj
+// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows sym.s -o sym-arm64ec.obj
+// RUN: llvm-objcopy --add-section=.obj.arm64ec=sym-arm64ec.obj 
--set-section-flags=.obj.arm64ec=debug \
+// RUN:              sym-arm64.obj sym.obj
+
+// RUN: llvm-mc -filetype=obj -triple=aarch64-windows ref.s -o ref-arm64.obj
+// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows ref.s -o ref-arm64ec.obj
+// RUN: llvm-objcopy --add-section=.obj.arm64ec=ref-arm64ec.obj 
--set-section-flags=.obj.arm64ec=debug \
+// RUN:              ref-arm64.obj ref.obj
+
+// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows 
%S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
+// RUN: llvm-mc -filetype=obj -triple=aarch64-windows 
%S/Inputs/loadconfig-arm64.s -o loadconfig-arm64.obj
+// RUN: llvm-objcopy --add-section=.obj.arm64ec=loadconfig-arm64ec.obj 
--set-section-flags=.obj.arm64ec=debug \
+// RUN:              loadconfig-arm64.obj loadconfig.obj
+
+// Test linking hybrid object files, verify that both native and EC exports 
are present.
+// RUN: lld-link -machine:arm64x -dll -noentry -out:out.dll sym.obj 
loadconfig.obj
+// RUN: llvm-readobj --coff-exports out.dll | FileCheck %s
+
+// RUN: lld-link -machine:arm64ec -dll -noentry -out:out-ec.dll sym.obj 
loadconfig.obj
+// RUN: lld-link -machine:arm64 -dll -noentry -out:out-native.dll sym.obj 
loadconfig.obj
+
+// Test linking symbols from an archive.
+// RUN: llvm-ar cr sym.lib sym.obj
+// RUN: lld-link -machine:arm64x -dll -noentry -out:out2.dll ref.obj sym.lib 
loadconfig.obj
+// RUN: llvm-readobj --coff-exports out2.dll | FileCheck %s
+// RUN: lld-link -machine:arm64ec -dll -noentry -out:out-ec2.dll sym.obj 
loadconfig.obj
+// RUN: lld-link -machine:arm64 -dll -noentry -out:out-native2.dll sym.obj 
loadconfig.obj
+
+// Test linking symbols from a thin archive.
+// RUN: llvm-ar cr --thin sym-thin.lib sym.obj
+// RUN: lld-link -machine:arm64x -dll -noentry -out:out3.dll ref.obj 
sym-thin.lib loadconfig.obj
+// RUN: llvm-readobj --coff-exports out3.dll | FileCheck %s
+// RUN: lld-link -machine:arm64x -dll -noentry -out:out4.dll 
-wholearchive:sym-thin.lib loadconfig.obj
+// RUN: llvm-readobj --coff-exports out4.dll | FileCheck %s
+
+// Test -start-lib/-end-lib with hybrid object files.
+// RUN: lld-link -machine:arm64x -dll -noentry -out:out5.dll ref.obj 
-start-lib sym.obj loadconfig.obj -end-lib
+// RUN: llvm-readobj --coff-exports out5.dll | FileCheck %s
+
+// CHECK:      Format: COFF-ARM64X
+// CHECK-NEXT: Arch: aarch64
+// CHECK-NEXT: AddressSize: 64bit
+// CHECK-NEXT: Export {
+// CHECK-NEXT:   Ordinal: 1
+// CHECK-NEXT:   Name: sym
+// CHECK-NEXT:   RVA: 0x4004
+// CHECK-NEXT: }
+// CHECK-NEXT: HybridObject {
+// CHECK-NEXT:   Format: COFF-ARM64EC
+// CHECK-NEXT:   Arch: aarch64
+// CHECK-NEXT:   AddressSize: 64bit
+// CHECK-NEXT:   Export {
+// CHECK-NEXT:     Ordinal: 1
+// CHECK-NEXT:     Name: sym
+// CHECK-NEXT:     RVA: 0x4000
+// CHECK-NEXT:   }
+// CHECK-NEXT: }
+
+#--- sym.s
+        .section .sym,"dr"
+        .globl sym
+sym:
+        .long 0
+
+        .section .drectve
+        .ascii "-export:sym"
+
+#--- ref.s
+        .data
+        .rva sym

>From 89d908763890ec0ad12d140214ac230e0152345a Mon Sep 17 00:00:00 2001
From: Jacek Caban <[email protected]>
Date: Wed, 1 Apr 2026 16:18:07 +0200
Subject: [PATCH 3/3] [clang][ARM64X] Support compiling both native and EC
 objects with -marm64x

When -marm64x is used during the assembly phase, construct jobs for both
native and EC targets and merge their outputs using llvm-objcopy.
---
 clang/include/clang/Driver/Action.h    |  2 +-
 clang/include/clang/Options/Options.td |  2 +-
 clang/lib/Driver/Action.cpp            |  4 +--
 clang/lib/Driver/Driver.cpp            | 37 +++++++++++++++++++-------
 clang/lib/Driver/ToolChain.cpp         |  4 ++-
 clang/lib/Driver/ToolChains/MSVC.cpp   | 36 +++++++++++++++++++++++++
 clang/lib/Driver/ToolChains/MSVC.h     | 16 +++++++++++
 clang/lib/Driver/ToolChains/MinGW.cpp  |  4 +++
 clang/lib/Driver/ToolChains/MinGW.h    |  2 ++
 clang/test/Driver/arm64x.c             |  6 +++++
 clang/test/Driver/msvc-link.c          |  4 +--
 11 files changed, 101 insertions(+), 16 deletions(-)
 create mode 100644 clang/test/Driver/arm64x.c

diff --git a/clang/include/clang/Driver/Action.h 
b/clang/include/clang/Driver/Action.h
index bbd6f03dd30da..306db1be15910 100644
--- a/clang/include/clang/Driver/Action.h
+++ b/clang/include/clang/Driver/Action.h
@@ -695,7 +695,7 @@ class ObjcopyJobAction : public JobAction {
   void anchor() override;
 
 public:
-  ObjcopyJobAction(Action *Input, types::ID Type);
+  ObjcopyJobAction(ActionList &Inputs, types::ID Type);
 
   static bool classof(const Action *A) {
     return A->getKind() == ObjcopyJobClass;
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 309bc94068a38..1ff5f9a4860ac 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -5466,7 +5466,7 @@ def municode : Joined<["-"], "municode">, Group<m_Group>;
 def mthreads : Joined<["-"], "mthreads">, Group<m_Group>;
 def marm64x : Joined<["-"], "marm64x">, Group<m_Group>,
   Visibility<[ClangOption, CLOption]>,
-  HelpText<"Link as a hybrid ARM64X image">;
+  HelpText<"Build as a hybrid ARM64X image">;
 def mguard_EQ : Joined<["-"], "mguard=">, Group<m_Group>,
   HelpText<"Enable or disable Control Flow Guard checks and guard tables 
emission">,
   Values<"none,cf,cf-nochecks">;
diff --git a/clang/lib/Driver/Action.cpp b/clang/lib/Driver/Action.cpp
index c0b84ffcc95be..b74ae925e922c 100644
--- a/clang/lib/Driver/Action.cpp
+++ b/clang/lib/Driver/Action.cpp
@@ -470,5 +470,5 @@ BinaryTranslatorJobAction::BinaryTranslatorJobAction(Action 
*Input,
 
 void ObjcopyJobAction::anchor() {}
 
-ObjcopyJobAction::ObjcopyJobAction(Action *Input, types::ID Type)
-    : JobAction(ObjcopyJobClass, Input, Type) {}
+ObjcopyJobAction::ObjcopyJobAction(ActionList &Inputs, types::ID Type)
+    : JobAction(ObjcopyJobClass, Inputs, Type) {}
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 0b04a68417bbe..9e4e18eaaffa4 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -616,10 +616,9 @@ static void setZosTargetVersion(const Driver &D, 
llvm::Triple &Target,
 ///
 /// This routine provides the logic to compute a target triple from various
 /// args passed to the driver and the default triple string.
-static llvm::Triple computeTargetTriple(const Driver &D,
-                                        StringRef TargetTriple,
+static llvm::Triple computeTargetTriple(const Driver &D, StringRef 
TargetTriple,
                                         const ArgList &Args,
-                                        StringRef DarwinArchName = "") {
+                                        StringRef ArchName = "") {
   // FIXME: Already done in Compilation *Driver::BuildCompilation
   if (const Arg *A = Args.getLastArg(options::OPT_target))
     TargetTriple = A->getValue();
@@ -635,9 +634,8 @@ static llvm::Triple computeTargetTriple(const Driver &D,
   // Handle Apple-specific options available here.
   if (Target.isOSBinFormatMachO()) {
     // If an explicit Darwin arch name is given, that trumps all.
-    if (!DarwinArchName.empty()) {
-      tools::darwin::setTripleTypeForMachOArchName(Target, DarwinArchName,
-                                                   Args);
+    if (!ArchName.empty()) {
+      tools::darwin::setTripleTypeForMachOArchName(Target, ArchName, Args);
       return llvm::Triple(Target.normalize());
     }
 
@@ -646,6 +644,9 @@ static llvm::Triple computeTargetTriple(const Driver &D,
       StringRef ArchName = A->getValue();
       tools::darwin::setTripleTypeForMachOArchName(Target, ArchName, Args);
     }
+  } else if (!ArchName.empty()) {
+    Target.setArchName(ArchName);
+    return Target;
   }
 
   // Handle pseudo-target flags '-mlittle-endian'/'-EL' and
@@ -703,6 +704,11 @@ static llvm::Triple computeTargetTriple(const Driver &D,
     D.Diag(diag::err_drv_unsupported_opt_for_target)
         << A->getAsString(Args) << Target.str();
 
+  // The `-marm64x` flag is only valid for Windows targets.
+  if (Args.hasArgNoClaim(options::OPT_marm64x) && !Target.isOSWindows())
+    D.Diag(diag::err_drv_unsupported_opt_for_target)
+        << "-marm64x" << Target.str();
+
   // Handle pseudo-target flags '-m64', '-mx32', '-m32' and '-m16'.
   Arg *A = Args.getLastArg(options::OPT_m64, options::OPT_mx32,
                            options::OPT_m32, options::OPT_m16,
@@ -4807,9 +4813,11 @@ void Driver::BuildActions(Compilation &C, DerivedArgList 
&Args,
     if (TC.requiresObjcopy(Args)) {
       Action *LastAction = Actions.back();
       // llvm-objcopy expects an unvalidated DXIL container (TY_OBJECT).
-      if (LastAction->getType() == types::TY_Object)
+      if (LastAction->getType() == types::TY_Object) {
+        ActionList ObjcopyActions({LastAction});
         Actions.push_back(
-            C.MakeAction<ObjcopyJobAction>(LastAction, types::TY_Object));
+            C.MakeAction<ObjcopyJobAction>(ObjcopyActions, types::TY_Object));
+      }
     }
 
     // Call validator when -Vd not in Args.
@@ -5401,6 +5409,16 @@ Action *Driver::ConstructPhaseAction(
     return C.MakeAction<BackendJobAction>(Input, types::TY_PP_Asm);
   }
   case phases::Assemble:
+    // When -marm64x is used, construct jobs for the EC and native targets and
+    // merge them into an archive with llvm-objcopy.
+    if (Args.hasArg(options::OPT_marm64x)) {
+      Action *Act =
+          C.MakeAction<AssembleJobAction>(std::move(Input), types::TY_Object);
+      ActionList Inputs;
+      Inputs.push_back(C.MakeAction<BindArchAction>(Act, 
BoundArch("arm64ec")));
+      Inputs.push_back(C.MakeAction<BindArchAction>(Act, 
BoundArch("aarch64")));
+      return C.MakeAction<ObjcopyJobAction>(Inputs, types::TY_Object);
+    }
     return C.MakeAction<AssembleJobAction>(std::move(Input), types::TY_Object);
   }
 
@@ -5495,7 +5513,8 @@ void Driver::BuildJobs(Compilation &C) const {
     BuildJobsForAction(C, A, &C.getDefaultToolChain(),
                        /*BA=*/{},
                        /*AtTopLevel*/ true,
-                       /*MultipleArchs*/ ArchNames.size() > 1,
+                       /*MultipleArchs*/ ArchNames.size() > 1 ||
+                           C.getArgs().hasArgNoClaim(options::OPT_marm64x),
                        /*LinkingOutput*/ LinkingOutput, CachedResults,
                        /*TargetDeviceOffloadKind*/ Action::OFK_None);
   }
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 21d2fbef59fc6..9230e4dae548f 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -801,9 +801,11 @@ Tool *ToolChain::getTool(Action::ActionClass AC) const {
   case Action::VerifyDebugInfoJobClass:
   case Action::BinaryAnalyzeJobClass:
   case Action::BinaryTranslatorJobClass:
-  case Action::ObjcopyJobClass:
     llvm_unreachable("Invalid tool kind.");
 
+  case Action::ObjcopyJobClass:
+    return nullptr;
+
   case Action::CompileJobClass:
   case Action::PrecompileJobClass:
   case Action::PreprocessJobClass:
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp 
b/clang/lib/Driver/ToolChains/MSVC.cpp
index eb81f1b4e142c..d826b81f0dd3b 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -498,6 +498,31 @@ void visualstudio::Linker::ConstructJob(Compilation &C, 
const JobAction &JA,
   C.addCommand(std::move(LinkCmd));
 }
 
+void tools::ARM64XObjcopy::ConstructJob(Compilation &C, const JobAction &JA,
+                                        const InputInfo &Output,
+                                        const InputInfoList &Inputs,
+                                        const ArgList &Args,
+                                        const char *LinkingOutput) const {
+  // Assume llvm-objcopy is only used for hybrid ARM64X object files.
+  if (Inputs.size() != 2)
+    return;
+
+  std::string ObjcopyPath = getToolChain().GetProgramPath("llvm-objcopy");
+  const char *Exec = Args.MakeArgString(ObjcopyPath);
+
+  // Embed the hybrid object in the .obj.arm64ec section.
+  ArgStringList CmdArgs;
+  CmdArgs.push_back(Args.MakeArgString("--add-section=.obj.arm64ec=" +
+                                       Twine(Inputs[0].getFilename())));
+  // Mark the .obj.arm64ec section as discardable.
+  CmdArgs.push_back("--set-section-flags=.obj.arm64ec=debug");
+  CmdArgs.push_back(Inputs[1].getFilename());
+  CmdArgs.push_back(Output.getFilename());
+
+  C.addCommand(std::make_unique<Command>(JA, *this, 
ResponseFileSupport::None(),
+                                         Exec, CmdArgs, Inputs, Output));
+}
+
 MSVCToolChain::MSVCToolChain(const Driver &D, const llvm::Triple &Triple,
                              const ArgList &Args)
     : ToolChain(D, Triple, Args), CudaInstallation(D, Triple, Args),
@@ -531,6 +556,17 @@ MSVCToolChain::MSVCToolChain(const Driver &D, const 
llvm::Triple &Triple,
   loadMultilibsFromYAML(Args, D);
 }
 
+Tool *MSVCToolChain::getTool(Action::ActionClass AC) const {
+  switch (AC) {
+  case Action::ObjcopyJobClass:
+    if (!Objcopy)
+      Objcopy.reset(new tools::ARM64XObjcopy(*this));
+    return Objcopy.get();
+  default:
+    return ToolChain::getTool(AC);
+  }
+}
+
 Tool *MSVCToolChain::buildLinker() const {
   return new tools::visualstudio::Linker(*this);
 }
diff --git a/clang/lib/Driver/ToolChains/MSVC.h 
b/clang/lib/Driver/ToolChains/MSVC.h
index 0ec73fb59b921..6878d6a195d78 100644
--- a/clang/lib/Driver/ToolChains/MSVC.h
+++ b/clang/lib/Driver/ToolChains/MSVC.h
@@ -39,6 +39,19 @@ class LLVM_LIBRARY_VISIBILITY Linker final : public Tool {
 };
 } // end namespace visualstudio
 
+class LLVM_LIBRARY_VISIBILITY ARM64XObjcopy : public Tool {
+public:
+  ARM64XObjcopy(const ToolChain &TC)
+      : Tool("ARM64XObjcopy", "llvm-objcopy", TC) {}
+
+  bool hasIntegratedCPP() const override { return false; }
+
+  void ConstructJob(Compilation &C, const JobAction &JA,
+                    const InputInfo &Output, const InputInfoList &Inputs,
+                    const llvm::opt::ArgList &TCArgs,
+                    const char *LinkingOutput) const override;
+};
+
 } // end namespace tools
 
 namespace toolchains {
@@ -137,8 +150,10 @@ class LLVM_LIBRARY_VISIBILITY MSVCToolChain : public 
ToolChain {
                                      const Twine &subfolder2 = "",
                                      const Twine &subfolder3 = "") const;
 
+  Tool *getTool(Action::ActionClass AC) const override;
   Tool *buildLinker() const override;
   Tool *buildAssembler() const override;
+
 private:
   std::optional<llvm::StringRef> WinSdkDir, WinSdkVersion, WinSysRoot;
   std::string VCToolChainPath;
@@ -146,6 +161,7 @@ class LLVM_LIBRARY_VISIBILITY MSVCToolChain : public 
ToolChain {
   LazyDetector<CudaInstallationDetector> CudaInstallation;
   LazyDetector<RocmInstallationDetector> RocmInstallation;
   LazyDetector<SYCLInstallationDetector> SYCLInstallation;
+  mutable std::unique_ptr<tools::ARM64XObjcopy> Objcopy;
 };
 
 } // end namespace toolchains
diff --git a/clang/lib/Driver/ToolChains/MinGW.cpp 
b/clang/lib/Driver/ToolChains/MinGW.cpp
index 4237a6906be3b..7fe5be92012ee 100644
--- a/clang/lib/Driver/ToolChains/MinGW.cpp
+++ b/clang/lib/Driver/ToolChains/MinGW.cpp
@@ -575,6 +575,10 @@ Tool *toolchains::MinGW::getTool(Action::ActionClass AC) 
const {
     if (!Compiler)
       Compiler.reset(new tools::gcc::Compiler(*this));
     return Compiler.get();
+  case Action::ObjcopyJobClass:
+    if (!Objcopy)
+      Objcopy.reset(new tools::ARM64XObjcopy(*this));
+    return Objcopy.get();
   default:
     return ToolChain::getTool(AC);
   }
diff --git a/clang/lib/Driver/ToolChains/MinGW.h 
b/clang/lib/Driver/ToolChains/MinGW.h
index 36ab7b6c6e295..c8b7a73af0a2d 100644
--- a/clang/lib/Driver/ToolChains/MinGW.h
+++ b/clang/lib/Driver/ToolChains/MinGW.h
@@ -11,6 +11,7 @@
 
 #include "Cuda.h"
 #include "Gnu.h"
+#include "MSVC.h"
 #include "clang/Driver/CudaInstallationDetector.h"
 #include "clang/Driver/LazyDetector.h"
 #include "clang/Driver/RocmInstallationDetector.h"
@@ -117,6 +118,7 @@ class LLVM_LIBRARY_VISIBILITY MinGW : public ToolChain {
   std::string TripleDirName;
   mutable std::unique_ptr<tools::gcc::Preprocessor> Preprocessor;
   mutable std::unique_ptr<tools::gcc::Compiler> Compiler;
+  mutable std::unique_ptr<tools::ARM64XObjcopy> Objcopy;
   void findGccLibDir(const llvm::Triple &LiteralTriple);
 
   bool NativeLLVMSupport;
diff --git a/clang/test/Driver/arm64x.c b/clang/test/Driver/arm64x.c
new file mode 100644
index 0000000000000..34b50d9133d14
--- /dev/null
+++ b/clang/test/Driver/arm64x.c
@@ -0,0 +1,6 @@
+// RUN: %clang -c -marm64x  --target=arm64ec-pc-windows-msvc -### %s 2>&1 | 
FileCheck %s
+// RUN: %clang -c -marm64x  --target=arm64ec-pc-windows-gnu -### %s 2>&1 | 
FileCheck %s
+
+// CHECK:      "-cc1" "-triple" "arm64ec-pc-windows-{{.*}}" "-emit-obj"
+// CHECK-NEXT: "-cc1" "-triple" "aarch64-pc-windows-{{.*}}" "-emit-obj"
+// CHECK-NEXT: llvm-objcopy" 
"--add-section=.obj.arm64ec={{.*}}arm64x-arm64ec-{{.*}}.o" 
"--set-section-flags=.obj.arm64ec=debug" "{{.*}}arm64x-aarch64-{{.*}}.o" 
"arm64x.o"
diff --git a/clang/test/Driver/msvc-link.c b/clang/test/Driver/msvc-link.c
index 5cb1653bf4db9..19027ff3e80ff 100644
--- a/clang/test/Driver/msvc-link.c
+++ b/clang/test/Driver/msvc-link.c
@@ -46,9 +46,9 @@
 // ARM64X: "-machine:arm64x"
 
 // RUN: not %clang --target=x86_64-linux-gnu -marm64x -### %s 2>&1 | FileCheck 
--check-prefix=HYBRID-ERR %s
-// HYBRID-ERR: error: unsupported option '-marm64x' for target 
'x86_64-linux-gnu'
+// HYBRID-ERR: error: unsupported option '-marm64x' for target 
'x86_64-unknown-linux-gnu'
 
-// RUN: %clang -c -marm64x  --target=arm64ec-pc-windows-msvc -fuse-ld=link 
-### %s 2>&1 | \
+// RUN: %clang -S -marm64x  --target=arm64ec-pc-windows-msvc -fuse-ld=link 
-### %s 2>&1 | \
 // RUN:        FileCheck --check-prefix=HYBRID-WARN %s
 // HYBRID-WARN: warning: argument unused during compilation: '-marm64x' 
[-Wunused-command-line-argument]
 

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to