[llvm-branch-commits] [lld] [lld-macho] Reject incompatible arm64e object ABI markers (PR #234005)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Oct 21 10:48:04 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lld

Author: Oskar Wirga (oskarwirga)

<details>
<summary>Changes</summary>

This PR adds parity for how ld64 rejects incompatible ARM64e subversions. The CPU subtype of an arm64e object is used for a pointer authentication ABI marker. 

In a dynamic link, ld64 accepts only objects marked userland ABI version 0 whose executables are marked versionĀ 1. ld64 names any other marker as another architecture (`arm64e.old`, `arm64e.v1`, `arm64e.kernel`, `arm64e.kernel.v1` or unknown) and ignores the file with a warning, or reports an error with `-arch_errors_fatal`.

lld linked these objects. Check the marker in the same way. ld64 accepts unversioned and kernel-ABI objects in static links, so those links are not checked.

Assisted-by: Claude


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


2 Files Affected:

- (modified) lld/MachO/InputFiles.cpp (+32-12) 
- (added) lld/test/MachO/arm64e-input-abi.s (+134) 


``````````diff
diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp
index 930322e4462e6..c6f552eb5359a 100644
--- a/lld/MachO/InputFiles.cpp
+++ b/lld/MachO/InputFiles.cpp
@@ -184,11 +184,34 @@ static bool checkCompatibility(const InputFile *input) {
   return true;
 }
 
+// ld64's names for arm64e objects whose ABI marker is not userland version 0.
+static StringRef getArm64eVariantName(uint32_t cpuSubtype) {
+  if (cpuSubtype == CPU_SUBTYPE_ARM64E)
+    return "arm64e.old";
+  if (cpuSubtype == CPU_SUBTYPE_ARM64E_WITH_PTRAUTH_VERSION(1, false))
+    return "arm64e.v1";
+  if (cpuSubtype == CPU_SUBTYPE_ARM64E_WITH_PTRAUTH_VERSION(0, true))
+    return "arm64e.kernel";
+  if (cpuSubtype == CPU_SUBTYPE_ARM64E_WITH_PTRAUTH_VERSION(1, true))
+    return "arm64e.kernel.v1";
+  return "unknown";
+}
+
 template <class Header>
 static bool compatWithTargetArch(const InputFile *file, const Header *hdr) {
   uint32_t cpuType;
   std::tie(cpuType, std::ignore) = getCPUTypeFromArchitecture(config->arch());
 
+  auto reportMismatch = [&](StringRef arch) {
+    auto msg = config->errorForArchMismatch
+                   ? static_cast<void (*)(const Twine &)>(error)
+                   : warn;
+    msg(toString(file) + " has architecture " + arch +
+        " which is incompatible with target architecture " +
+        getArchitectureName(config->arch()));
+    return false;
+  };
+
   // arm64 and arm64e are ABI incompatible. arm64e.x1 has the arm64e ABI but
   // needs a newer CPU, so neither takes it.
   uint32_t subtype = hdr->cpusubtype & ~CPU_SUBTYPE_MASK;
@@ -196,18 +219,15 @@ static bool compatWithTargetArch(const InputFile *file, const Header *hdr) {
   if (hdr->cputype != cpuType ||
       (is_contained({AK_arm64, AK_arm64e}, config->arch()) &&
        (subtype == CPU_SUBTYPE_ARM64E_X1 ||
-        isArm64e != (config->arch() == AK_arm64e)))) {
-    Architecture arch =
-        getArchitectureFromCpuType(hdr->cputype, hdr->cpusubtype);
-    auto msg = config->errorForArchMismatch
-                   ? static_cast<void (*)(const Twine &)>(error)
-                   : warn;
-
-    msg(toString(file) + " has architecture " + getArchitectureName(arch) +
-        " which is incompatible with target architecture " +
-        getArchitectureName(config->arch()));
-    return false;
-  }
+        isArm64e != (config->arch() == AK_arm64e))))
+    return reportMismatch(getArchitectureName(
+        getArchitectureFromCpuType(hdr->cputype, hdr->cpusubtype)));
+
+  // Dynamic links, like ld64's, only take objects with userland ABI version 0,
+  // even though macOS executables are marked version 1.
+  if (isArm64e && hdr->filetype == MH_OBJECT && !config->staticLink &&
+      hdr->cpusubtype != CPU_SUBTYPE_ARM64E_WITH_PTRAUTH_VERSION(0, false))
+    return reportMismatch(getArm64eVariantName(hdr->cpusubtype));
 
   return checkCompatibility(file);
 }
diff --git a/lld/test/MachO/arm64e-input-abi.s b/lld/test/MachO/arm64e-input-abi.s
new file mode 100644
index 0000000000000..f76062583e549
--- /dev/null
+++ b/lld/test/MachO/arm64e-input-abi.s
@@ -0,0 +1,134 @@
+# REQUIRES: aarch64
+# RUN: rm -rf %t; split-file %s %t
+# RUN: llvm-mc -filetype=obj -triple=arm64e-apple-darwin %t/main.s -o %t/main.o
+# RUN: yaml2obj -DSUBTYPE=0x80000002 %t/helper.yaml -o %t/helper.o
+# RUN: yaml2obj -DSUBTYPE=0x2 %t/helper.yaml -o %t/old.o
+# RUN: yaml2obj -DSUBTYPE=0x81000002 %t/helper.yaml -o %t/v1.o
+# RUN: yaml2obj -DSUBTYPE=0xC0000002 %t/helper.yaml -o %t/kernel.o
+# RUN: yaml2obj -DSUBTYPE=0xC1000002 %t/helper.yaml -o %t/kernel-v1.o
+# RUN: yaml2obj -DSUBTYPE=0x82000002 %t/helper.yaml -o %t/v2.o
+# RUN: llvm-ar rcs %t/old.a %t/old.o
+# RUN: llvm-ar rcs %t/v1.a %t/v1.o
+# RUN: llvm-ar rcs %t/kernel.a %t/kernel.o
+
+## Objects must be marked userland ABI version 0, even on platforms whose
+## executables are marked version 1.
+# RUN: %no-arg-lld -arch arm64e -platform_version ios 17.0 17.0 %t/main.o %t/helper.o -o %t/ios
+# RUN: %no-arg-lld -arch arm64e -platform_version macos 14.0 14.0 %t/main.o %t/helper.o -o %t/mac
+# RUN: %no-arg-lld -arch arm64e -platform_version mac-catalyst 17.0 17.0 %t/main.o %t/helper.o -o %t/catalyst
+
+## Like ld64, treat any other marker as another architecture, in both thin
+## objects and archive members.
+# RUN: not %lld -arch arm64e -arch_errors_fatal %t/main.o %t/old.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=OLD
+# RUN: not %lld -arch arm64e -arch_errors_fatal %t/main.o %t/old.a -o /dev/null 2>&1 | FileCheck %s --check-prefix=OLD
+# RUN: not %lld -arch arm64e -arch_errors_fatal %t/main.o %t/v1.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=V1
+# RUN: not %lld -arch arm64e -arch_errors_fatal %t/main.o %t/v1.a -o /dev/null 2>&1 | FileCheck %s --check-prefix=V1
+# RUN: not %lld -arch arm64e -arch_errors_fatal %t/main.o %t/kernel.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=KERNEL
+# RUN: not %lld -arch arm64e -arch_errors_fatal %t/main.o %t/kernel.a -o /dev/null 2>&1 | FileCheck %s --check-prefix=KERNEL
+# RUN: not %lld -arch arm64e -arch_errors_fatal %t/main.o %t/kernel-v1.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=KERNEL-V1
+# RUN: not %lld -arch arm64e -arch_errors_fatal %t/main.o %t/v2.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=UNKNOWN
+# RUN: not %no-arg-lld -arch arm64e -arch_errors_fatal -platform_version mac-catalyst 17.0 17.0 %t/main.o %t/v1.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=V1
+
+# OLD: error: {{.*}}old.{{.*}} has architecture arm64e.old which is incompatible with target architecture arm64e
+# V1: error: {{.*}}v1.{{.*}} has architecture arm64e.v1 which is incompatible with target architecture arm64e
+# KERNEL: error: {{.*}}kernel.{{.*}} has architecture arm64e.kernel which is incompatible with target architecture arm64e
+# KERNEL-V1: error: {{.*}}kernel-v1.o has architecture arm64e.kernel.v1 which is incompatible with target architecture arm64e
+# UNKNOWN: error: {{.*}}v2.o has architecture unknown which is incompatible with target architecture arm64e
+
+## Without -arch_errors_fatal, the object is ignored with a warning.
+# RUN: not %no-arg-lld -arch arm64e -platform_version ios 17.0 17.0 %t/main.o %t/v1.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=WARN
+# WARN: warning: {{.*}}v1.o has architecture arm64e.v1 which is incompatible with target architecture arm64e
+# WARN: error: undefined symbol: _helper
+
+## ld64 accepts some other markers in static links, such as kernels.
+# RUN: %no-arg-lld -arch arm64e -platform_version ios 17.0 17.0 -static -e _main %t/main.o %t/kernel.o -o %t/static
+
+#--- main.s
+.globl _main
+.p2align 2
+_main:
+  bl _helper
+  ret
+
+#--- helper.yaml
+## An object defining _helper; SUBTYPE varies only its arm64e ABI marker.
+--- !mach-o
+FileHeader:
+  magic:           0xFEEDFACF
+  cputype:         0x100000C
+  cpusubtype:      [[SUBTYPE]]
+  filetype:        0x1
+  ncmds:           3
+  sizeofcmds:      256
+  flags:           0x0
+  reserved:        0x0
+LoadCommands:
+  - cmd:             LC_SEGMENT_64
+    cmdsize:         152
+    segname:         ''
+    vmaddr:          0
+    vmsize:          4
+    fileoff:         288
+    filesize:        4
+    maxprot:         7
+    initprot:        7
+    nsects:          1
+    flags:           0
+    Sections:
+      - sectname:        __text
+        segname:         __TEXT
+        addr:            0x0
+        size:            4
+        offset:          0x120
+        align:           2
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x80000400
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+        content:         C0035FD6
+  - cmd:             LC_SYMTAB
+    cmdsize:         24
+    symoff:          296
+    nsyms:           2
+    stroff:          328
+    strsize:         16
+  - cmd:             LC_DYSYMTAB
+    cmdsize:         80
+    ilocalsym:       0
+    nlocalsym:       1
+    iextdefsym:      1
+    nextdefsym:      1
+    iundefsym:       2
+    nundefsym:       0
+    tocoff:          0
+    ntoc:            0
+    modtaboff:       0
+    nmodtab:         0
+    extrefsymoff:    0
+    nextrefsyms:     0
+    indirectsymoff:  0
+    nindirectsyms:   0
+    extreloff:       0
+    nextrel:         0
+    locreloff:       0
+    nlocrel:         0
+LinkEditData:
+  NameList:
+    - n_strx:          9
+      n_type:          0xE
+      n_sect:          1
+      n_desc:          0
+      n_value:         0
+    - n_strx:          1
+      n_type:          0xF
+      n_sect:          1
+      n_desc:          0
+      n_value:         0
+  StringTable:
+    - ''
+    - _helper
+    - ltmp0
+    - ''
+...

``````````

</details>


https://github.com/llvm/llvm-project/pull/234005


More information about the llvm-branch-commits mailing list