[llvm] fd4f952 - [AArch64][MachO] Add ptrauth ABI version to arm64e cpusubtype. (#104650)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 20 11:37:16 PDT 2024
Author: Ahmed Bougacha
Date: 2024-08-20T11:37:12-07:00
New Revision: fd4f9520a6a08c3dcf15622e3b887d8f3624fc42
URL: https://github.com/llvm/llvm-project/commit/fd4f9520a6a08c3dcf15622e3b887d8f3624fc42
DIFF: https://github.com/llvm/llvm-project/commit/fd4f9520a6a08c3dcf15622e3b887d8f3624fc42.diff
LOG: [AArch64][MachO] Add ptrauth ABI version to arm64e cpusubtype. (#104650)
In a mach_header, the cpusubtype is a 32-bit field, but it's split in 2
subfields:
- the low 24 bits containing the cpu subtype proper, (e.g.,
CPU_SUBTYPE_ARM64E 2)
- the high 8 bits containing a capability field used for additional
feature flags.
Notably, it's only the subtype subfield that participates in fat file
slice discrimination: the caps are ignored.
arm64e uses the caps subfield to encode a ptrauth ABI version:
- 0x80 (CPU_SUBTYPE_PTRAUTH_ABI) denotes a versioned binary
- 0x40 denotes a kernel-ABI binary
- 0x00-0x0F holds the ptrauth ABI version
This teaches the basic obj tools to decode that (or ignore it when
unneeded).
It also teaches the MachO writer to default to emitting versioned
binaries, but with a version of 0 (and without the kernel ABI flag).
Modern arm64e requires versioned binaries: a binary with 0x00 caps in
cpusubtype is now rejected by the linker and everything after. We can
live without the sophistication of specifying the version and kernel ABI
for now.
Co-authored-by: Francis Visoiu Mistrih <francisvm at apple.com>
Added:
llvm/test/tools/llvm-objdump/AArch64/Inputs/fat.macho-arm64e-kernel-ptrauth-abi
llvm/test/tools/llvm-objdump/AArch64/Inputs/fat.macho-arm64e-ptrauth-abi
llvm/test/tools/llvm-objdump/AArch64/macho-universal-arm64e-kernel-ptrauth-abi.test
llvm/test/tools/llvm-objdump/AArch64/macho-universal-arm64e-ptrauth-abi.test
llvm/test/tools/llvm-objdump/macho-ptrauth-cpusubtype.test
Modified:
llvm/include/llvm/BinaryFormat/MachO.h
llvm/lib/BinaryFormat/MachO.cpp
llvm/lib/MC/MachObjectWriter.cpp
llvm/lib/Object/MachOObjectFile.cpp
llvm/test/MC/AArch64/arm64e-subtype.s
llvm/tools/dsymutil/MachOUtils.cpp
llvm/tools/llvm-objdump/MachODump.cpp
llvm/unittests/BinaryFormat/MachOTest.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/BinaryFormat/MachO.h b/llvm/include/llvm/BinaryFormat/MachO.h
index bef70f869520b7..b37651e85a6269 100644
--- a/llvm/include/llvm/BinaryFormat/MachO.h
+++ b/llvm/include/llvm/BinaryFormat/MachO.h
@@ -1641,8 +1641,39 @@ enum CPUSubTypeARM64 {
CPU_SUBTYPE_ARM64_ALL = 0,
CPU_SUBTYPE_ARM64_V8 = 1,
CPU_SUBTYPE_ARM64E = 2,
+
+ // arm64e uses the capability bits to encode ptrauth ABI information.
+ // Bit 63 marks the binary as Versioned.
+ CPU_SUBTYPE_ARM64E_VERSIONED_PTRAUTH_ABI_MASK = 0x80000000,
+ // Bit 62 marks the binary as using a kernel ABI.
+ CPU_SUBTYPE_ARM64E_KERNEL_PTRAUTH_ABI_MASK = 0x40000000,
+ // Bits [59:56] hold the 4-bit ptrauth ABI version.
+ CPU_SUBTYPE_ARM64E_PTRAUTH_MASK = 0x0f000000,
};
+inline int CPU_SUBTYPE_ARM64E_PTRAUTH_VERSION(unsigned ST) {
+ return (ST & CPU_SUBTYPE_ARM64E_PTRAUTH_MASK) >> 24;
+}
+
+inline unsigned
+CPU_SUBTYPE_ARM64E_WITH_PTRAUTH_VERSION(unsigned PtrAuthABIVersion,
+ bool PtrAuthKernelABIVersion) {
+ assert((PtrAuthABIVersion <= 0xF) &&
+ "ptrauth abi version must fit in 4 bits");
+ return CPU_SUBTYPE_ARM64E | CPU_SUBTYPE_ARM64E_VERSIONED_PTRAUTH_ABI_MASK |
+ (PtrAuthKernelABIVersion ? CPU_SUBTYPE_ARM64E_KERNEL_PTRAUTH_ABI_MASK
+ : 0) |
+ (PtrAuthABIVersion << 24);
+}
+
+inline unsigned CPU_SUBTYPE_ARM64E_IS_VERSIONED_PTRAUTH_ABI(unsigned ST) {
+ return ST & CPU_SUBTYPE_ARM64E_VERSIONED_PTRAUTH_ABI_MASK;
+}
+
+inline unsigned CPU_SUBTYPE_ARM64E_IS_KERNEL_PTRAUTH_ABI(unsigned ST) {
+ return ST & CPU_SUBTYPE_ARM64E_KERNEL_PTRAUTH_ABI_MASK;
+}
+
enum CPUSubTypeARM64_32 { CPU_SUBTYPE_ARM64_32_V8 = 1 };
enum CPUSubTypeSPARC { CPU_SUBTYPE_SPARC_ALL = 0 };
@@ -1668,6 +1699,8 @@ enum CPUSubTypePowerPC {
Expected<uint32_t> getCPUType(const Triple &T);
Expected<uint32_t> getCPUSubType(const Triple &T);
+Expected<uint32_t> getCPUSubType(const Triple &T, unsigned PtrAuthABIVersion,
+ bool PtrAuthKernelABIVersion);
struct x86_thread_state32_t {
uint32_t eax;
diff --git a/llvm/lib/BinaryFormat/MachO.cpp b/llvm/lib/BinaryFormat/MachO.cpp
index 1b5941cf527525..f46b9d5147ff19 100644
--- a/llvm/lib/BinaryFormat/MachO.cpp
+++ b/llvm/lib/BinaryFormat/MachO.cpp
@@ -105,3 +105,21 @@ Expected<uint32_t> MachO::getCPUSubType(const Triple &T) {
return getPowerPCSubType(T);
return unsupported("subtype", T);
}
+
+Expected<uint32_t> MachO::getCPUSubType(const Triple &T,
+ unsigned PtrAuthABIVersion,
+ bool PtrAuthKernelABIVersion) {
+ Expected<uint32_t> Result = MachO::getCPUSubType(T);
+ if (!Result)
+ return Result.takeError();
+ if (*Result != MachO::CPU_SUBTYPE_ARM64E)
+ return createStringError(
+ std::errc::invalid_argument,
+ "ptrauth ABI version is only supported on arm64e.");
+ if (PtrAuthABIVersion > 0xF)
+ return createStringError(
+ std::errc::invalid_argument,
+ "The ptrauth ABI version needs to fit within 4 bits.");
+ return CPU_SUBTYPE_ARM64E_WITH_PTRAUTH_VERSION(PtrAuthABIVersion,
+ PtrAuthKernelABIVersion);
+}
diff --git a/llvm/lib/MC/MachObjectWriter.cpp b/llvm/lib/MC/MachObjectWriter.cpp
index 8596f457574a3d..b6180fbc03704d 100644
--- a/llvm/lib/MC/MachObjectWriter.cpp
+++ b/llvm/lib/MC/MachObjectWriter.cpp
@@ -191,7 +191,18 @@ void MachObjectWriter::writeHeader(MachO::HeaderFileType Type,
W.write<uint32_t>(is64Bit() ? MachO::MH_MAGIC_64 : MachO::MH_MAGIC);
W.write<uint32_t>(TargetObjectWriter->getCPUType());
- W.write<uint32_t>(TargetObjectWriter->getCPUSubtype());
+
+ uint32_t Cpusubtype = TargetObjectWriter->getCPUSubtype();
+
+ // Promote arm64e subtypes to always be ptrauth-ABI-versioned, at version 0.
+ // We never need to emit unversioned binaries.
+ // And we don't support arbitrary ABI versions (or the kernel flag) yet.
+ if (TargetObjectWriter->getCPUType() == MachO::CPU_TYPE_ARM64 &&
+ Cpusubtype == MachO::CPU_SUBTYPE_ARM64E)
+ Cpusubtype = MachO::CPU_SUBTYPE_ARM64E_WITH_PTRAUTH_VERSION(
+ /*PtrAuthABIVersion=*/0, /*PtrAuthKernelABIVersion=*/false);
+
+ W.write<uint32_t>(Cpusubtype);
W.write<uint32_t>(Type);
W.write<uint32_t>(NumLoadCommands);
diff --git a/llvm/lib/Object/MachOObjectFile.cpp b/llvm/lib/Object/MachOObjectFile.cpp
index e2d1723b1fa981..bd9b5dd13cdfe8 100644
--- a/llvm/lib/Object/MachOObjectFile.cpp
+++ b/llvm/lib/Object/MachOObjectFile.cpp
@@ -134,7 +134,7 @@ static unsigned getCPUType(const MachOObjectFile &O) {
}
static unsigned getCPUSubType(const MachOObjectFile &O) {
- return O.getHeader().cpusubtype;
+ return O.getHeader().cpusubtype & ~MachO::CPU_SUBTYPE_MASK;
}
static uint32_t
diff --git a/llvm/test/MC/AArch64/arm64e-subtype.s b/llvm/test/MC/AArch64/arm64e-subtype.s
index 44d414bb4e72f1..4196d62164ea37 100644
--- a/llvm/test/MC/AArch64/arm64e-subtype.s
+++ b/llvm/test/MC/AArch64/arm64e-subtype.s
@@ -4,8 +4,8 @@
; CHECK: 0: c0 03 5f d6 ret
; CHECK: Mach header
-; CHECK: magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
-; CHECK: MH_MAGIC_64 ARM64 E 0x00 OBJECT 3 256 0x00000000
+; CHECK: magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
+; CHECK: MH_MAGIC_64 ARM64 E PAC00 OBJECT 3 256 0x00000000
.globl _foo
_foo:
diff --git a/llvm/test/tools/llvm-objdump/AArch64/Inputs/fat.macho-arm64e-kernel-ptrauth-abi b/llvm/test/tools/llvm-objdump/AArch64/Inputs/fat.macho-arm64e-kernel-ptrauth-abi
new file mode 100644
index 00000000000000..cd53fc1f7b9a2e
Binary files /dev/null and b/llvm/test/tools/llvm-objdump/AArch64/Inputs/fat.macho-arm64e-kernel-ptrauth-abi
diff er
diff --git a/llvm/test/tools/llvm-objdump/AArch64/Inputs/fat.macho-arm64e-ptrauth-abi b/llvm/test/tools/llvm-objdump/AArch64/Inputs/fat.macho-arm64e-ptrauth-abi
new file mode 100644
index 00000000000000..e8094fe2e66fea
Binary files /dev/null and b/llvm/test/tools/llvm-objdump/AArch64/Inputs/fat.macho-arm64e-ptrauth-abi
diff er
diff --git a/llvm/test/tools/llvm-objdump/AArch64/macho-universal-arm64e-kernel-ptrauth-abi.test b/llvm/test/tools/llvm-objdump/AArch64/macho-universal-arm64e-kernel-ptrauth-abi.test
new file mode 100644
index 00000000000000..c40a5dbd845972
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/AArch64/macho-universal-arm64e-kernel-ptrauth-abi.test
@@ -0,0 +1,13 @@
+RUN: llvm-objdump %p/Inputs/fat.macho-arm64e-kernel-ptrauth-abi -m -p --universal-headers | FileCheck %s
+
+CHECK: Fat headers
+CHECK: fat_magic FAT_MAGIC
+CHECK: nfat_arch 1
+CHECK: architecture arm64e
+CHECK: cputype CPU_TYPE_ARM64
+CHECK: cpusubtype CPU_SUBTYPE_ARM64E
+CHECK: capabilities CPU_SUBTYPE_ARM64E_KERNEL_PTRAUTH_VERSION 5
+
+CHECK: Mach header
+CHECK: magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
+CHECK: MH_MAGIC_64 ARM64 E PAK05 OBJECT 0 0 0x00000000
diff --git a/llvm/test/tools/llvm-objdump/AArch64/macho-universal-arm64e-ptrauth-abi.test b/llvm/test/tools/llvm-objdump/AArch64/macho-universal-arm64e-ptrauth-abi.test
new file mode 100644
index 00000000000000..df352f52eeb107
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/AArch64/macho-universal-arm64e-ptrauth-abi.test
@@ -0,0 +1,13 @@
+RUN: llvm-objdump %p/Inputs/fat.macho-arm64e-ptrauth-abi -m -p --universal-headers | FileCheck %s
+
+CHECK: Fat headers
+CHECK: fat_magic FAT_MAGIC
+CHECK: nfat_arch 1
+CHECK: architecture arm64e
+CHECK: cputype CPU_TYPE_ARM64
+CHECK: cpusubtype CPU_SUBTYPE_ARM64E
+CHECK: capabilities CPU_SUBTYPE_ARM64E_PTRAUTH_VERSION 5
+
+CHECK: Mach header
+CHECK: magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
+CHECK: MH_MAGIC_64 ARM64 E PAC05 OBJECT 0 0 0x00000000
diff --git a/llvm/test/tools/llvm-objdump/macho-ptrauth-cpusubtype.test b/llvm/test/tools/llvm-objdump/macho-ptrauth-cpusubtype.test
new file mode 100644
index 00000000000000..1fcc9536fc5a76
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/macho-ptrauth-cpusubtype.test
@@ -0,0 +1,23 @@
+#RUN: sed -e "s,SRC_CPUSUBTYPE,0x80000002,g" %s | yaml2obj -o -| llvm-objdump --macho -p - 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=V0
+#RUN: sed -e "s,SRC_CPUSUBTYPE,0x81000002,g" %s | yaml2obj -o -| llvm-objdump --macho -p - 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=V1
+#RUN: sed -e "s,SRC_CPUSUBTYPE,0xc1000002,g" %s | yaml2obj -o -| llvm-objdump --macho -p - 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=V1K
+#RUN: sed -e "s,SRC_CPUSUBTYPE,0x00000002,g" %s | yaml2obj -o -| llvm-objdump --macho -p - 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=VNONE
+
+# CHECK: Mach header
+# CHECK: magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
+# V0: MH_MAGIC_64 ARM64 E PAC00 OBJECT 0 0 0x00000000
+# V1: MH_MAGIC_64 ARM64 E PAC01 OBJECT 0 0 0x00000000
+# V1K: MH_MAGIC_64 ARM64 E PAK01 OBJECT 0 0 0x00000000
+# VNONE: MH_MAGIC_64 ARM64 E 0x00 OBJECT 0 0 0x00000000
+
+--- !mach-o
+FileHeader:
+ magic: 0xFEEDFACF
+ cputype: 0x0100000C
+ cpusubtype: SRC_CPUSUBTYPE
+ filetype: 0x00000001
+ ncmds: 0
+ sizeofcmds: 0
+ flags: 0x00000000
+ reserved: 0x00000000
+...
diff --git a/llvm/tools/dsymutil/MachOUtils.cpp b/llvm/tools/dsymutil/MachOUtils.cpp
index d2bdcf8542b84c..be1934f0ee64a9 100644
--- a/llvm/tools/dsymutil/MachOUtils.cpp
+++ b/llvm/tools/dsymutil/MachOUtils.cpp
@@ -523,7 +523,8 @@ bool generateDsymCompanion(
SymtabStart = alignTo(SymtabStart, 0x1000);
// We gathered all the information we need, start emitting the output file.
- Writer.writeHeader(MachO::MH_DSYM, NumLoadCommands, LoadCommandSize, false);
+ Writer.writeHeader(MachO::MH_DSYM, NumLoadCommands, LoadCommandSize,
+ /*SubsectionsViaSymbols=*/false);
// Write the load commands.
assert(OutFile.tell() == HeaderSize);
diff --git a/llvm/tools/llvm-objdump/MachODump.cpp b/llvm/tools/llvm-objdump/MachODump.cpp
index 0544fc4f406bb0..b8afb560d2ae9c 100644
--- a/llvm/tools/llvm-objdump/MachODump.cpp
+++ b/llvm/tools/llvm-objdump/MachODump.cpp
@@ -2394,8 +2394,16 @@ static void printMachOUniversalHeaders(const object::MachOUniversalBinary *UB,
outs() << " cpusubtype " << (cpusubtype & ~MachO::CPU_SUBTYPE_MASK)
<< "\n";
}
- if (verbose &&
- (cpusubtype & MachO::CPU_SUBTYPE_MASK) == MachO::CPU_SUBTYPE_LIB64)
+ if (verbose && cputype == MachO::CPU_TYPE_ARM64 &&
+ MachO::CPU_SUBTYPE_ARM64E_IS_VERSIONED_PTRAUTH_ABI(cpusubtype)) {
+ outs() << " capabilities CPU_SUBTYPE_ARM64E_";
+ if (MachO::CPU_SUBTYPE_ARM64E_IS_KERNEL_PTRAUTH_ABI(cpusubtype))
+ outs() << "KERNEL_";
+ outs() << format("PTRAUTH_VERSION %d",
+ MachO::CPU_SUBTYPE_ARM64E_PTRAUTH_VERSION(cpusubtype))
+ << "\n";
+ } else if (verbose && (cpusubtype & MachO::CPU_SUBTYPE_MASK) ==
+ MachO::CPU_SUBTYPE_LIB64)
outs() << " capabilities CPU_SUBTYPE_LIB64\n";
else
outs() << " capabilities "
@@ -8368,7 +8376,17 @@ static void PrintMachHeader(uint32_t magic, uint32_t cputype,
outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK);
break;
}
- if ((cpusubtype & MachO::CPU_SUBTYPE_MASK) == MachO::CPU_SUBTYPE_LIB64) {
+
+ if (cputype == MachO::CPU_TYPE_ARM64 &&
+ MachO::CPU_SUBTYPE_ARM64E_IS_VERSIONED_PTRAUTH_ABI(cpusubtype)) {
+ const char *Format =
+ MachO::CPU_SUBTYPE_ARM64E_IS_KERNEL_PTRAUTH_ABI(cpusubtype)
+ ? " PAK%02d"
+ : " PAC%02d";
+ outs() << format(Format,
+ MachO::CPU_SUBTYPE_ARM64E_PTRAUTH_VERSION(cpusubtype));
+ } else if ((cpusubtype & MachO::CPU_SUBTYPE_MASK) ==
+ MachO::CPU_SUBTYPE_LIB64) {
outs() << " LIB64";
} else {
outs() << format(" 0x%02" PRIx32,
diff --git a/llvm/unittests/BinaryFormat/MachOTest.cpp b/llvm/unittests/BinaryFormat/MachOTest.cpp
index 78b20c28a9549a..e10cc3112664ff 100644
--- a/llvm/unittests/BinaryFormat/MachOTest.cpp
+++ b/llvm/unittests/BinaryFormat/MachOTest.cpp
@@ -125,3 +125,56 @@ TEST(MachOTest, CPUSubType) {
}
#undef CHECK_CPUSUBTYPE
}
+
+TEST(MachOTest, CPUSubTypePtrAuthABI) {
+ {
+ Expected<uint32_t> Type = MachO::getCPUSubType(
+ Triple("x86_64-apple-darwin"), /*PtrAuthABIVersion=*/5,
+ /*PtrAuthKernelABIVersion=*/false);
+ ASSERT_EQ(toString(Type.takeError()),
+ "ptrauth ABI version is only supported on arm64e.");
+ }
+ {
+ Expected<uint32_t> Type = MachO::getCPUSubType(
+ Triple("arm64e-apple-darwin"), /*PtrAuthABIVersion=*/0x10,
+ /*PtrAuthKernelABIVersion=*/false);
+ ASSERT_EQ(toString(Type.takeError()),
+ "The ptrauth ABI version needs to fit within 4 bits.");
+ }
+ {
+ uint32_t Type = cantFail(MachO::getCPUSubType(
+ Triple("arm64e-apple-darwin"),
+ /*PtrAuthABIVersion=*/5, /*PtrAuthKernelABIVersion=*/false));
+ ASSERT_EQ(Type, 0x85000002U);
+ }
+ {
+ uint32_t Type = cantFail(MachO::getCPUSubType(
+ Triple("arm64e-apple-darwin"),
+ /*PtrAuthABIVersion=*/5, /*PtrAuthKernelABIVersion=*/true));
+ ASSERT_EQ(Type, 0xC5000002U);
+ }
+ {
+ uint32_t Type = cantFail(MachO::getCPUSubType(
+ Triple("arm64e-apple-darwin"),
+ /*PtrAuthABIVersion=*/0xF, /*PtrAuthKernelABIVersion=*/false));
+ ASSERT_EQ(Type, 0x8F000002U);
+ }
+ {
+ uint32_t Type = cantFail(MachO::getCPUSubType(
+ Triple("arm64e-apple-darwin"),
+ /*PtrAuthABIVersion=*/0xF, /*PtrAuthKernelABIVersion=*/true));
+ ASSERT_EQ(Type, 0xCF000002U);
+ }
+ {
+ uint32_t Type = cantFail(MachO::getCPUSubType(
+ Triple("arm64e-apple-darwin"),
+ /*PtrAuthABIVersion=*/0, /*PtrAuthKernelABIVersion=*/false));
+ ASSERT_EQ(Type, 0x80000002U);
+ }
+ {
+ uint32_t Type = cantFail(MachO::getCPUSubType(
+ Triple("arm64e-apple-darwin"),
+ /*PtrAuthABIVersion=*/0, /*PtrAuthKernelABIVersion=*/true));
+ ASSERT_EQ(Type, 0xC0000002U);
+ }
+}
More information about the llvm-commits
mailing list