blob: 8d90e819e29afd4e3f29904e8a789f41d9b3101c [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/mac/mach_o.h"
#include <mach/machine.h>
#include <vector>
#include "base/check.h"
#include "base/check_op.h"
#include "base/containers/span.h"
#include "base/files/file.h"
#include "base/sys_byteorder.h"
namespace base {
namespace {
// ByteSwap is only implemented for unsigned types, and cpu_type_t is a signed
// type.
cpu_type_t ByteSwapCPUType(const cpu_type_t cputype) {
return static_cast<cpu_type_t>(
ByteSwap(static_cast<std::make_unsigned<cpu_type_t>::type>(cputype)));
}
MachOArchitectures CPUTypeToBit(const cpu_type_t cputype) {
switch (cputype) {
case CPU_TYPE_X86_64:
return MachOArchitectures::kX86_64;
case CPU_TYPE_ARM64:
return MachOArchitectures::kARM64;
default:
return MachOArchitectures::kUnknownArchitecture;
}
}
template <typename FatArchType>
MachOArchitectures GetFatMachOArchitectures(File& file,
const uint32_t nfat_arch,
const bool swap) {
MachOArchitectures result = static_cast<MachOArchitectures>(0);
std::vector<FatArchType> archs(nfat_arch);
if (!file.ReadAtCurrentPosAndCheck(
as_writable_bytes(make_span(archs.data(), archs.size())))) {
return MachOArchitectures::kInvalidFormat;
}
for (const FatArchType& arch : archs) {
result |= CPUTypeToBit(swap ? ByteSwapCPUType(arch.cputype) : arch.cputype);
}
DCHECK_NE(result, static_cast<MachOArchitectures>(0));
return result;
}
} // namespace
MachOArchitectures GetMachOArchitectures(const FilePath& path) {
File file(path, File::FLAG_OPEN | File::FLAG_READ);
if (!file.IsValid()) {
return MachOArchitectures::kFileError;
}
union {
uint32_t magic;
// In a 64-bit file, the header will be a mach_header_64 instead of a
// mach_header, but the only difference between the two is that the latter
// ends with a 4-byte padding field. This function does not consider
// anything beyond the header, so mach_header will suffice in place of
// mach_header_64.
mach_header mach;
fat_header fat;
} header;
if (!file.ReadAtCurrentPosAndCheck(
as_writable_bytes(make_span(&header.magic, 1)))) {
return MachOArchitectures::kInvalidFormat;
}
switch (header.magic) {
case MH_MAGIC:
case MH_MAGIC_64:
case MH_CIGAM:
case MH_CIGAM_64: {
const bool is_64 =
header.magic == MH_MAGIC_64 || header.magic == MH_CIGAM_64;
const bool swap = header.magic == MH_CIGAM || header.magic == MH_CIGAM_64;
if (!file.ReadAtCurrentPosAndCheck(
as_writable_bytes(make_span(&header.mach, 1))
.subspan(sizeof(header.magic)))) {
return MachOArchitectures::kInvalidFormat;
}
if (swap) {
header.mach.magic = ByteSwap(header.mach.magic);
header.mach.cputype = ByteSwapCPUType(header.mach.cputype);
}
DCHECK(header.mach.magic == MH_MAGIC || header.mach.magic == MH_MAGIC_64);
DCHECK_EQ(header.mach.magic == MH_MAGIC_64, is_64);
return CPUTypeToBit(header.mach.cputype);
}
case FAT_MAGIC:
case FAT_MAGIC_64:
case FAT_CIGAM:
case FAT_CIGAM_64: {
const bool is_64 =
header.magic == FAT_MAGIC_64 || header.magic == FAT_CIGAM_64;
const bool swap =
header.magic == FAT_CIGAM || header.magic == FAT_CIGAM_64;
if (!file.ReadAtCurrentPosAndCheck(
as_writable_bytes(make_span(&header.fat, 1))
.subspan(sizeof(header.magic)))) {
return MachOArchitectures::kInvalidFormat;
}
if (swap) {
header.fat.magic = ByteSwap(header.fat.magic);
header.fat.nfat_arch = ByteSwap(header.fat.nfat_arch);
}
DCHECK(header.fat.magic == FAT_MAGIC || header.fat.magic == FAT_MAGIC_64);
DCHECK_EQ(header.fat.magic == FAT_MAGIC_64, is_64);
if (header.fat.nfat_arch == 0) {
return MachOArchitectures::kInvalidFormat;
}
return is_64 ? GetFatMachOArchitectures<fat_arch_64>(
file, header.fat.nfat_arch, swap)
: GetFatMachOArchitectures<fat_arch>(
file, header.fat.nfat_arch, swap);
}
default: {
return MachOArchitectures::kInvalidFormat;
}
}
}
} // namespace base