[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