MachOUniversal.cpp revision 314564
1259698Sdim//===- MachOUniversal.cpp - Mach-O universal binary -------------*- C++ -*-===// 2259698Sdim// 3259698Sdim// The LLVM Compiler Infrastructure 4259698Sdim// 5259698Sdim// This file is distributed under the University of Illinois Open Source 6259698Sdim// License. See LICENSE.TXT for details. 7259698Sdim// 8259698Sdim//===----------------------------------------------------------------------===// 9259698Sdim// 10259698Sdim// This file defines the MachOUniversalBinary class. 11259698Sdim// 12259698Sdim//===----------------------------------------------------------------------===// 13259698Sdim 14259698Sdim#include "llvm/Object/MachOUniversal.h" 15280031Sdim#include "llvm/Object/Archive.h" 16259698Sdim#include "llvm/Object/MachO.h" 17259698Sdim#include "llvm/Object/ObjectFile.h" 18259698Sdim#include "llvm/Support/Casting.h" 19259698Sdim#include "llvm/Support/Host.h" 20259698Sdim#include "llvm/Support/MemoryBuffer.h" 21259698Sdim 22259698Sdimusing namespace llvm; 23259698Sdimusing namespace object; 24259698Sdim 25309124Sdimstatic Error 26309124SdimmalformedError(Twine Msg) { 27309124Sdim std::string StringMsg = "truncated or malformed fat file (" + Msg.str() + ")"; 28309124Sdim return make_error<GenericBinaryError>(std::move(StringMsg), 29309124Sdim object_error::parse_failed); 30259698Sdim} 31259698Sdim 32259698Sdimtemplate<typename T> 33259698Sdimstatic T getUniversalBinaryStruct(const char *Ptr) { 34259698Sdim T Res; 35259698Sdim memcpy(&Res, Ptr, sizeof(T)); 36259698Sdim // Universal binary headers have big-endian byte order. 37259698Sdim if (sys::IsLittleEndianHost) 38309124Sdim swapStruct(Res); 39259698Sdim return Res; 40259698Sdim} 41259698Sdim 42259698SdimMachOUniversalBinary::ObjectForArch::ObjectForArch( 43259698Sdim const MachOUniversalBinary *Parent, uint32_t Index) 44259698Sdim : Parent(Parent), Index(Index) { 45314564Sdim // The iterators use Parent as a nullptr and an Index+1 == NumberOfObjects. 46276479Sdim if (!Parent || Index >= Parent->getNumberOfObjects()) { 47259698Sdim clear(); 48259698Sdim } else { 49259698Sdim // Parse object header. 50259698Sdim StringRef ParentData = Parent->getData(); 51309124Sdim if (Parent->getMagic() == MachO::FAT_MAGIC) { 52309124Sdim const char *HeaderPos = ParentData.begin() + sizeof(MachO::fat_header) + 53309124Sdim Index * sizeof(MachO::fat_arch); 54309124Sdim Header = getUniversalBinaryStruct<MachO::fat_arch>(HeaderPos); 55309124Sdim } else { // Parent->getMagic() == MachO::FAT_MAGIC_64 56309124Sdim const char *HeaderPos = ParentData.begin() + sizeof(MachO::fat_header) + 57309124Sdim Index * sizeof(MachO::fat_arch_64); 58309124Sdim Header64 = getUniversalBinaryStruct<MachO::fat_arch_64>(HeaderPos); 59259698Sdim } 60259698Sdim } 61259698Sdim} 62259698Sdim 63309124SdimExpected<std::unique_ptr<MachOObjectFile>> 64276479SdimMachOUniversalBinary::ObjectForArch::getAsObjectFile() const { 65296417Sdim if (!Parent) 66309124Sdim report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsObjectFile() " 67309124Sdim "called when Parent is a nullptr"); 68296417Sdim 69296417Sdim StringRef ParentData = Parent->getData(); 70309124Sdim StringRef ObjectData; 71314564Sdim uint32_t cputype; 72314564Sdim if (Parent->getMagic() == MachO::FAT_MAGIC) { 73309124Sdim ObjectData = ParentData.substr(Header.offset, Header.size); 74314564Sdim cputype = Header.cputype; 75314564Sdim } else { // Parent->getMagic() == MachO::FAT_MAGIC_64 76309124Sdim ObjectData = ParentData.substr(Header64.offset, Header64.size); 77314564Sdim cputype = Header64.cputype; 78314564Sdim } 79296417Sdim StringRef ObjectName = Parent->getFileName(); 80296417Sdim MemoryBufferRef ObjBuffer(ObjectData, ObjectName); 81314564Sdim return ObjectFile::createMachOObjectFile(ObjBuffer, cputype, Index); 82259698Sdim} 83259698Sdim 84309124SdimExpected<std::unique_ptr<Archive>> 85280031SdimMachOUniversalBinary::ObjectForArch::getAsArchive() const { 86280031Sdim if (!Parent) 87309124Sdim report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsArchive() " 88309124Sdim "called when Parent is a nullptr"); 89280031Sdim 90280031Sdim StringRef ParentData = Parent->getData(); 91309124Sdim StringRef ObjectData; 92309124Sdim if (Parent->getMagic() == MachO::FAT_MAGIC) 93309124Sdim ObjectData = ParentData.substr(Header.offset, Header.size); 94309124Sdim else // Parent->getMagic() == MachO::FAT_MAGIC_64 95309124Sdim ObjectData = ParentData.substr(Header64.offset, Header64.size); 96280031Sdim StringRef ObjectName = Parent->getFileName(); 97280031Sdim MemoryBufferRef ObjBuffer(ObjectData, ObjectName); 98280031Sdim return Archive::create(ObjBuffer); 99276479Sdim} 100276479Sdim 101259698Sdimvoid MachOUniversalBinary::anchor() { } 102259698Sdim 103309124SdimExpected<std::unique_ptr<MachOUniversalBinary>> 104280031SdimMachOUniversalBinary::create(MemoryBufferRef Source) { 105314564Sdim Error Err = Error::success(); 106276479Sdim std::unique_ptr<MachOUniversalBinary> Ret( 107309124Sdim new MachOUniversalBinary(Source, Err)); 108309124Sdim if (Err) 109309124Sdim return std::move(Err); 110280031Sdim return std::move(Ret); 111276479Sdim} 112276479Sdim 113309124SdimMachOUniversalBinary::MachOUniversalBinary(MemoryBufferRef Source, Error &Err) 114309124Sdim : Binary(Binary::ID_MachOUniversalBinary, Source), Magic(0), 115309124Sdim NumberOfObjects(0) { 116314564Sdim ErrorAsOutParameter ErrAsOutParam(&Err); 117280031Sdim if (Data.getBufferSize() < sizeof(MachO::fat_header)) { 118309124Sdim Err = make_error<GenericBinaryError>("File too small to be a Mach-O " 119309124Sdim "universal file", 120309124Sdim object_error::invalid_file_type); 121259698Sdim return; 122259698Sdim } 123259698Sdim // Check for magic value and sufficient header size. 124259698Sdim StringRef Buf = getData(); 125309124Sdim MachO::fat_header H = 126309124Sdim getUniversalBinaryStruct<MachO::fat_header>(Buf.begin()); 127309124Sdim Magic = H.magic; 128259698Sdim NumberOfObjects = H.nfat_arch; 129314564Sdim if (NumberOfObjects == 0) { 130314564Sdim Err = malformedError("contains zero architecture types"); 131314564Sdim return; 132314564Sdim } 133309124Sdim uint32_t MinSize = sizeof(MachO::fat_header); 134309124Sdim if (Magic == MachO::FAT_MAGIC) 135309124Sdim MinSize += sizeof(MachO::fat_arch) * NumberOfObjects; 136309124Sdim else if (Magic == MachO::FAT_MAGIC_64) 137309124Sdim MinSize += sizeof(MachO::fat_arch_64) * NumberOfObjects; 138309124Sdim else { 139309124Sdim Err = malformedError("bad magic number"); 140259698Sdim return; 141259698Sdim } 142309124Sdim if (Buf.size() < MinSize) { 143309124Sdim Err = malformedError("fat_arch" + 144309124Sdim Twine(Magic == MachO::FAT_MAGIC ? "" : "_64") + 145309124Sdim " structs would extend past the end of the file"); 146309124Sdim return; 147309124Sdim } 148314564Sdim for (uint32_t i = 0; i < NumberOfObjects; i++) { 149314564Sdim ObjectForArch A(this, i); 150314564Sdim uint64_t bigSize = A.getOffset(); 151314564Sdim bigSize += A.getSize(); 152314564Sdim if (bigSize > Buf.size()) { 153314564Sdim Err = malformedError("offset plus size of cputype (" + 154314564Sdim Twine(A.getCPUType()) + ") cpusubtype (" + 155314564Sdim Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + 156314564Sdim ") extends past the end of the file"); 157314564Sdim return; 158314564Sdim } 159314564Sdim#define MAXSECTALIGN 15 /* 2**15 or 0x8000 */ 160314564Sdim if (A.getAlign() > MAXSECTALIGN) { 161314564Sdim Err = malformedError("align (2^" + Twine(A.getAlign()) + ") too large " 162314564Sdim "for cputype (" + Twine(A.getCPUType()) + ") cpusubtype (" + 163314564Sdim Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + 164314564Sdim ") (maximum 2^" + Twine(MAXSECTALIGN) + ")"); 165314564Sdim return; 166314564Sdim } 167314564Sdim if(A.getOffset() % (1 << 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 213309124SdimExpected<std::unique_ptr<MachOObjectFile>> 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); 220288943Sdim 221314564Sdim for (auto &Obj : objects()) 222314564Sdim if (Obj.getArchFlagName() == ArchName) 223314564Sdim return Obj.getAsObjectFile(); 224309124Sdim return make_error<GenericBinaryError>("fat file does not " 225309124Sdim "contain " + 226309124Sdim ArchName, 227309124Sdim object_error::arch_not_found); 228259698Sdim} 229