// 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 #include #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( ByteSwap(static_cast::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 MachOArchitectures GetFatMachOArchitectures(File& file, const uint32_t nfat_arch, const bool swap) { MachOArchitectures result = static_cast(0); std::vector 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(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( file, header.fat.nfat_arch, swap) : GetFatMachOArchitectures( file, header.fat.nfat_arch, swap); } default: { return MachOArchitectures::kInvalidFormat; } } } } // namespace base