1259698Sdim//===- MachOUniversal.cpp - Mach-O universal binary -------------*- C++ -*-===// 2259698Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6259698Sdim// 7259698Sdim//===----------------------------------------------------------------------===// 8259698Sdim// 9259698Sdim// This file defines the MachOUniversalBinary class. 10259698Sdim// 11259698Sdim//===----------------------------------------------------------------------===// 12259698Sdim 13259698Sdim#include "llvm/Object/MachOUniversal.h" 14280031Sdim#include "llvm/Object/Archive.h" 15259698Sdim#include "llvm/Object/MachO.h" 16259698Sdim#include "llvm/Object/ObjectFile.h" 17259698Sdim#include "llvm/Support/Casting.h" 18259698Sdim#include "llvm/Support/Host.h" 19259698Sdim#include "llvm/Support/MemoryBuffer.h" 20259698Sdim 21259698Sdimusing namespace llvm; 22259698Sdimusing namespace object; 23259698Sdim 24309124Sdimstatic Error 25309124SdimmalformedError(Twine Msg) { 26309124Sdim std::string StringMsg = "truncated or malformed fat file (" + Msg.str() + ")"; 27309124Sdim return make_error<GenericBinaryError>(std::move(StringMsg), 28309124Sdim object_error::parse_failed); 29259698Sdim} 30259698Sdim 31259698Sdimtemplate<typename T> 32259698Sdimstatic T getUniversalBinaryStruct(const char *Ptr) { 33259698Sdim T Res; 34259698Sdim memcpy(&Res, Ptr, sizeof(T)); 35259698Sdim // Universal binary headers have big-endian byte order. 36259698Sdim if (sys::IsLittleEndianHost) 37309124Sdim swapStruct(Res); 38259698Sdim return Res; 39259698Sdim} 40259698Sdim 41259698SdimMachOUniversalBinary::ObjectForArch::ObjectForArch( 42259698Sdim const MachOUniversalBinary *Parent, uint32_t Index) 43259698Sdim : Parent(Parent), Index(Index) { 44314564Sdim // The iterators use Parent as a nullptr and an Index+1 == NumberOfObjects. 45276479Sdim if (!Parent || Index >= Parent->getNumberOfObjects()) { 46259698Sdim clear(); 47259698Sdim } else { 48259698Sdim // Parse object header. 49259698Sdim StringRef ParentData = Parent->getData(); 50309124Sdim if (Parent->getMagic() == MachO::FAT_MAGIC) { 51309124Sdim const char *HeaderPos = ParentData.begin() + sizeof(MachO::fat_header) + 52309124Sdim Index * sizeof(MachO::fat_arch); 53309124Sdim Header = getUniversalBinaryStruct<MachO::fat_arch>(HeaderPos); 54309124Sdim } else { // Parent->getMagic() == MachO::FAT_MAGIC_64 55309124Sdim const char *HeaderPos = ParentData.begin() + sizeof(MachO::fat_header) + 56309124Sdim Index * sizeof(MachO::fat_arch_64); 57309124Sdim Header64 = getUniversalBinaryStruct<MachO::fat_arch_64>(HeaderPos); 58259698Sdim } 59259698Sdim } 60259698Sdim} 61259698Sdim 62309124SdimExpected<std::unique_ptr<MachOObjectFile>> 63276479SdimMachOUniversalBinary::ObjectForArch::getAsObjectFile() const { 64296417Sdim if (!Parent) 65309124Sdim report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsObjectFile() " 66309124Sdim "called when Parent is a nullptr"); 67296417Sdim 68296417Sdim StringRef ParentData = Parent->getData(); 69309124Sdim StringRef ObjectData; 70314564Sdim uint32_t cputype; 71314564Sdim if (Parent->getMagic() == MachO::FAT_MAGIC) { 72309124Sdim ObjectData = ParentData.substr(Header.offset, Header.size); 73314564Sdim cputype = Header.cputype; 74314564Sdim } else { // Parent->getMagic() == MachO::FAT_MAGIC_64 75309124Sdim ObjectData = ParentData.substr(Header64.offset, Header64.size); 76314564Sdim cputype = Header64.cputype; 77314564Sdim } 78296417Sdim StringRef ObjectName = Parent->getFileName(); 79296417Sdim MemoryBufferRef ObjBuffer(ObjectData, ObjectName); 80314564Sdim return ObjectFile::createMachOObjectFile(ObjBuffer, cputype, Index); 81259698Sdim} 82259698Sdim 83309124SdimExpected<std::unique_ptr<Archive>> 84280031SdimMachOUniversalBinary::ObjectForArch::getAsArchive() const { 85280031Sdim if (!Parent) 86309124Sdim report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsArchive() " 87309124Sdim "called when Parent is a nullptr"); 88280031Sdim 89280031Sdim StringRef ParentData = Parent->getData(); 90309124Sdim StringRef ObjectData; 91309124Sdim if (Parent->getMagic() == MachO::FAT_MAGIC) 92309124Sdim ObjectData = ParentData.substr(Header.offset, Header.size); 93309124Sdim else // Parent->getMagic() == MachO::FAT_MAGIC_64 94309124Sdim ObjectData = ParentData.substr(Header64.offset, Header64.size); 95280031Sdim StringRef ObjectName = Parent->getFileName(); 96280031Sdim MemoryBufferRef ObjBuffer(ObjectData, ObjectName); 97280031Sdim return Archive::create(ObjBuffer); 98276479Sdim} 99276479Sdim 100259698Sdimvoid MachOUniversalBinary::anchor() { } 101259698Sdim 102309124SdimExpected<std::unique_ptr<MachOUniversalBinary>> 103280031SdimMachOUniversalBinary::create(MemoryBufferRef Source) { 104314564Sdim Error Err = Error::success(); 105276479Sdim std::unique_ptr<MachOUniversalBinary> Ret( 106309124Sdim new MachOUniversalBinary(Source, Err)); 107309124Sdim if (Err) 108309124Sdim return std::move(Err); 109280031Sdim return std::move(Ret); 110276479Sdim} 111276479Sdim 112309124SdimMachOUniversalBinary::MachOUniversalBinary(MemoryBufferRef Source, Error &Err) 113309124Sdim : Binary(Binary::ID_MachOUniversalBinary, Source), Magic(0), 114309124Sdim NumberOfObjects(0) { 115314564Sdim ErrorAsOutParameter ErrAsOutParam(&Err); 116280031Sdim if (Data.getBufferSize() < sizeof(MachO::fat_header)) { 117309124Sdim Err = make_error<GenericBinaryError>("File too small to be a Mach-O " 118309124Sdim "universal file", 119309124Sdim object_error::invalid_file_type); 120259698Sdim return; 121259698Sdim } 122259698Sdim // Check for magic value and sufficient header size. 123259698Sdim StringRef Buf = getData(); 124309124Sdim MachO::fat_header H = 125309124Sdim getUniversalBinaryStruct<MachO::fat_header>(Buf.begin()); 126309124Sdim Magic = H.magic; 127259698Sdim NumberOfObjects = H.nfat_arch; 128314564Sdim if (NumberOfObjects == 0) { 129314564Sdim Err = malformedError("contains zero architecture types"); 130314564Sdim return; 131314564Sdim } 132309124Sdim uint32_t MinSize = sizeof(MachO::fat_header); 133309124Sdim if (Magic == MachO::FAT_MAGIC) 134309124Sdim MinSize += sizeof(MachO::fat_arch) * NumberOfObjects; 135309124Sdim else if (Magic == MachO::FAT_MAGIC_64) 136309124Sdim MinSize += sizeof(MachO::fat_arch_64) * NumberOfObjects; 137309124Sdim else { 138309124Sdim Err = malformedError("bad magic number"); 139259698Sdim return; 140259698Sdim } 141309124Sdim if (Buf.size() < MinSize) { 142309124Sdim Err = malformedError("fat_arch" + 143309124Sdim Twine(Magic == MachO::FAT_MAGIC ? "" : "_64") + 144309124Sdim " structs would extend past the end of the file"); 145309124Sdim return; 146309124Sdim } 147314564Sdim for (uint32_t i = 0; i < NumberOfObjects; i++) { 148314564Sdim ObjectForArch A(this, i); 149314564Sdim uint64_t bigSize = A.getOffset(); 150314564Sdim bigSize += A.getSize(); 151314564Sdim if (bigSize > Buf.size()) { 152314564Sdim Err = malformedError("offset plus size of cputype (" + 153314564Sdim Twine(A.getCPUType()) + ") cpusubtype (" + 154314564Sdim Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + 155314564Sdim ") extends past the end of the file"); 156314564Sdim return; 157314564Sdim } 158360784Sdim 159360784Sdim if (A.getAlign() > MaxSectionAlignment) { 160360784Sdim Err = malformedError("align (2^" + Twine(A.getAlign()) + 161360784Sdim ") too large for cputype (" + Twine(A.getCPUType()) + 162360784Sdim ") cpusubtype (" + 163360784Sdim Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + 164360784Sdim ") (maximum 2^" + Twine(MaxSectionAlignment) + ")"); 165314564Sdim return; 166314564Sdim } 167360784Sdim if(A.getOffset() % (1ull << A.getAlign()) != 0){ 168314564Sdim Err = malformedError("offset: " + Twine(A.getOffset()) + 169314564Sdim " for cputype (" + Twine(A.getCPUType()) + ") cpusubtype (" + 170314564Sdim Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + 171314564Sdim ") not aligned on it's alignment (2^" + Twine(A.getAlign()) + ")"); 172314564Sdim return; 173314564Sdim } 174314564Sdim if (A.getOffset() < MinSize) { 175314564Sdim Err = malformedError("cputype (" + Twine(A.getCPUType()) + ") " 176314564Sdim "cpusubtype (" + Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + 177314564Sdim ") offset " + Twine(A.getOffset()) + " overlaps universal headers"); 178314564Sdim return; 179314564Sdim } 180314564Sdim } 181314564Sdim for (uint32_t i = 0; i < NumberOfObjects; i++) { 182314564Sdim ObjectForArch A(this, i); 183314564Sdim for (uint32_t j = i + 1; j < NumberOfObjects; j++) { 184314564Sdim ObjectForArch B(this, j); 185314564Sdim if (A.getCPUType() == B.getCPUType() && 186314564Sdim (A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) == 187314564Sdim (B.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK)) { 188314564Sdim Err = malformedError("contains two of the same architecture (cputype " 189314564Sdim "(" + Twine(A.getCPUType()) + ") cpusubtype (" + 190314564Sdim Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + "))"); 191314564Sdim return; 192314564Sdim } 193314564Sdim if ((A.getOffset() >= B.getOffset() && 194314564Sdim A.getOffset() < B.getOffset() + B.getSize()) || 195314564Sdim (A.getOffset() + A.getSize() > B.getOffset() && 196314564Sdim A.getOffset() + A.getSize() < B.getOffset() + B.getSize()) || 197314564Sdim (A.getOffset() <= B.getOffset() && 198314564Sdim A.getOffset() + A.getSize() >= B.getOffset() + B.getSize())) { 199314564Sdim Err = malformedError("cputype (" + Twine(A.getCPUType()) + ") " 200314564Sdim "cpusubtype (" + Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + 201314564Sdim ") at offset " + Twine(A.getOffset()) + " with a size of " + 202314564Sdim Twine(A.getSize()) + ", overlaps cputype (" + Twine(B.getCPUType()) + 203314564Sdim ") cpusubtype (" + Twine(B.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) 204314564Sdim + ") at offset " + Twine(B.getOffset()) + " with a size of " 205314564Sdim + Twine(B.getSize())); 206314564Sdim return; 207314564Sdim } 208314564Sdim } 209314564Sdim } 210309124Sdim Err = Error::success(); 211259698Sdim} 212259698Sdim 213360784SdimExpected<MachOUniversalBinary::ObjectForArch> 214288943SdimMachOUniversalBinary::getObjectForArch(StringRef ArchName) const { 215288943Sdim if (Triple(ArchName).getArch() == Triple::ArchType::UnknownArch) 216309124Sdim return make_error<GenericBinaryError>("Unknown architecture " 217309124Sdim "named: " + 218309124Sdim ArchName, 219309124Sdim object_error::arch_not_found); 220360784Sdim for (const auto &Obj : objects()) 221314564Sdim if (Obj.getArchFlagName() == ArchName) 222360784Sdim return Obj; 223309124Sdim return make_error<GenericBinaryError>("fat file does not " 224309124Sdim "contain " + 225309124Sdim ArchName, 226309124Sdim object_error::arch_not_found); 227259698Sdim} 228360784Sdim 229360784SdimExpected<std::unique_ptr<MachOObjectFile>> 230360784SdimMachOUniversalBinary::getMachOObjectForArch(StringRef ArchName) const { 231360784Sdim Expected<ObjectForArch> O = getObjectForArch(ArchName); 232360784Sdim if (!O) 233360784Sdim return O.takeError(); 234360784Sdim return O->getAsObjectFile(); 235360784Sdim} 236360784Sdim 237360784SdimExpected<std::unique_ptr<Archive>> 238360784SdimMachOUniversalBinary::getArchiveForArch(StringRef ArchName) const { 239360784Sdim Expected<ObjectForArch> O = getObjectForArch(ArchName); 240360784Sdim if (!O) 241360784Sdim return O.takeError(); 242360784Sdim return O->getAsArchive(); 243360784Sdim} 244