Ahmed Bougacha fd4f9520a6
[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@apple.com>
2024-08-20 11:37:12 -07:00

126 lines
4.1 KiB
C++

//===-- llvm/BinaryFormat/MachO.cpp - The MachO file format -----*- C++/-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/TargetParser/ARMTargetParser.h"
#include "llvm/TargetParser/Triple.h"
using namespace llvm;
static MachO::CPUSubTypeX86 getX86SubType(const Triple &T) {
assert(T.isX86());
if (T.isArch32Bit())
return MachO::CPU_SUBTYPE_I386_ALL;
assert(T.isArch64Bit());
if (T.getArchName() == "x86_64h")
return MachO::CPU_SUBTYPE_X86_64_H;
return MachO::CPU_SUBTYPE_X86_64_ALL;
}
static MachO::CPUSubTypeARM getARMSubType(const Triple &T) {
assert(T.isARM() || T.isThumb());
StringRef Arch = T.getArchName();
ARM::ArchKind AK = ARM::parseArch(Arch);
switch (AK) {
default:
return MachO::CPU_SUBTYPE_ARM_V7;
case ARM::ArchKind::ARMV4T:
return MachO::CPU_SUBTYPE_ARM_V4T;
case ARM::ArchKind::ARMV5T:
case ARM::ArchKind::ARMV5TE:
case ARM::ArchKind::ARMV5TEJ:
return MachO::CPU_SUBTYPE_ARM_V5;
case ARM::ArchKind::ARMV6:
case ARM::ArchKind::ARMV6K:
return MachO::CPU_SUBTYPE_ARM_V6;
case ARM::ArchKind::ARMV7A:
return MachO::CPU_SUBTYPE_ARM_V7;
case ARM::ArchKind::ARMV7S:
return MachO::CPU_SUBTYPE_ARM_V7S;
case ARM::ArchKind::ARMV7K:
return MachO::CPU_SUBTYPE_ARM_V7K;
case ARM::ArchKind::ARMV6M:
return MachO::CPU_SUBTYPE_ARM_V6M;
case ARM::ArchKind::ARMV7M:
return MachO::CPU_SUBTYPE_ARM_V7M;
case ARM::ArchKind::ARMV7EM:
return MachO::CPU_SUBTYPE_ARM_V7EM;
}
}
static MachO::CPUSubTypeARM64 getARM64SubType(const Triple &T) {
assert(T.isAArch64());
if (T.isArch32Bit())
return (MachO::CPUSubTypeARM64)MachO::CPU_SUBTYPE_ARM64_32_V8;
if (T.isArm64e())
return MachO::CPU_SUBTYPE_ARM64E;
return MachO::CPU_SUBTYPE_ARM64_ALL;
}
static MachO::CPUSubTypePowerPC getPowerPCSubType(const Triple &T) {
return MachO::CPU_SUBTYPE_POWERPC_ALL;
}
static Error unsupported(const char *Str, const Triple &T) {
return createStringError(std::errc::invalid_argument,
"Unsupported triple for mach-o cpu %s: %s", Str,
T.str().c_str());
}
Expected<uint32_t> MachO::getCPUType(const Triple &T) {
if (!T.isOSBinFormatMachO())
return unsupported("type", T);
if (T.isX86() && T.isArch32Bit())
return MachO::CPU_TYPE_X86;
if (T.isX86() && T.isArch64Bit())
return MachO::CPU_TYPE_X86_64;
if (T.isARM() || T.isThumb())
return MachO::CPU_TYPE_ARM;
if (T.isAArch64())
return T.isArch32Bit() ? MachO::CPU_TYPE_ARM64_32 : MachO::CPU_TYPE_ARM64;
if (T.getArch() == Triple::ppc)
return MachO::CPU_TYPE_POWERPC;
if (T.getArch() == Triple::ppc64)
return MachO::CPU_TYPE_POWERPC64;
return unsupported("type", T);
}
Expected<uint32_t> MachO::getCPUSubType(const Triple &T) {
if (!T.isOSBinFormatMachO())
return unsupported("subtype", T);
if (T.isX86())
return getX86SubType(T);
if (T.isARM() || T.isThumb())
return getARMSubType(T);
if (T.isAArch64() || T.getArch() == Triple::aarch64_32)
return getARM64SubType(T);
if (T.getArch() == Triple::ppc || T.getArch() == Triple::ppc64)
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);
}