1 //===- MachOUniversal.cpp - Mach-O universal binary -------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file defines the MachOUniversalBinary class. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "llvm/Object/MachOUniversal.h" 15 #include "llvm/Object/Archive.h" 16 #include "llvm/Object/MachO.h" 17 #include "llvm/Object/ObjectFile.h" 18 #include "llvm/Support/Casting.h" 19 #include "llvm/Support/Host.h" 20 #include "llvm/Support/MemoryBuffer.h" 21 22 using namespace llvm; 23 using namespace object; 24 25 static Error 26 malformedError(Twine Msg) { 27 std::string StringMsg = "truncated or malformed fat file (" + Msg.str() + ")"; 28 return make_error<GenericBinaryError>(std::move(StringMsg), 29 object_error::parse_failed); 30 } 31 32 template<typename T> 33 static T getUniversalBinaryStruct(const char *Ptr) { 34 T Res; 35 memcpy(&Res, Ptr, sizeof(T)); 36 // Universal binary headers have big-endian byte order. 37 if (sys::IsLittleEndianHost) 38 swapStruct(Res); 39 return Res; 40 } 41 42 MachOUniversalBinary::ObjectForArch::ObjectForArch( 43 const MachOUniversalBinary *Parent, uint32_t Index) 44 : Parent(Parent), Index(Index) { 45 // The iterators use Parent as a nullptr and an Index+1 == NumberOfObjects. 46 if (!Parent || Index >= Parent->getNumberOfObjects()) { 47 clear(); 48 } else { 49 // Parse object header. 50 StringRef ParentData = Parent->getData(); 51 if (Parent->getMagic() == MachO::FAT_MAGIC) { 52 const char *HeaderPos = ParentData.begin() + sizeof(MachO::fat_header) + 53 Index * sizeof(MachO::fat_arch); 54 Header = getUniversalBinaryStruct<MachO::fat_arch>(HeaderPos); 55 } else { // Parent->getMagic() == MachO::FAT_MAGIC_64 56 const char *HeaderPos = ParentData.begin() + sizeof(MachO::fat_header) + 57 Index * sizeof(MachO::fat_arch_64); 58 Header64 = getUniversalBinaryStruct<MachO::fat_arch_64>(HeaderPos); 59 } 60 } 61 } 62 63 Expected<std::unique_ptr<MachOObjectFile>> 64 MachOUniversalBinary::ObjectForArch::getAsObjectFile() const { 65 if (!Parent) 66 report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsObjectFile() " 67 "called when Parent is a nullptr"); 68 69 StringRef ParentData = Parent->getData(); 70 StringRef ObjectData; 71 uint32_t cputype; 72 if (Parent->getMagic() == MachO::FAT_MAGIC) { 73 ObjectData = ParentData.substr(Header.offset, Header.size); 74 cputype = Header.cputype; 75 } else { // Parent->getMagic() == MachO::FAT_MAGIC_64 76 ObjectData = ParentData.substr(Header64.offset, Header64.size); 77 cputype = Header64.cputype; 78 } 79 StringRef ObjectName = Parent->getFileName(); 80 MemoryBufferRef ObjBuffer(ObjectData, ObjectName); 81 return ObjectFile::createMachOObjectFile(ObjBuffer, cputype, Index); 82 } 83 84 Expected<std::unique_ptr<Archive>> 85 MachOUniversalBinary::ObjectForArch::getAsArchive() const { 86 if (!Parent) 87 report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsArchive() " 88 "called when Parent is a nullptr"); 89 90 StringRef ParentData = Parent->getData(); 91 StringRef ObjectData; 92 if (Parent->getMagic() == MachO::FAT_MAGIC) 93 ObjectData = ParentData.substr(Header.offset, Header.size); 94 else // Parent->getMagic() == MachO::FAT_MAGIC_64 95 ObjectData = ParentData.substr(Header64.offset, Header64.size); 96 StringRef ObjectName = Parent->getFileName(); 97 MemoryBufferRef ObjBuffer(ObjectData, ObjectName); 98 return Archive::create(ObjBuffer); 99 } 100 101 void MachOUniversalBinary::anchor() { } 102 103 Expected<std::unique_ptr<MachOUniversalBinary>> 104 MachOUniversalBinary::create(MemoryBufferRef Source) { 105 Error Err = Error::success(); 106 std::unique_ptr<MachOUniversalBinary> Ret( 107 new MachOUniversalBinary(Source, Err)); 108 if (Err) 109 return std::move(Err); 110 return std::move(Ret); 111 } 112 113 MachOUniversalBinary::MachOUniversalBinary(MemoryBufferRef Source, Error &Err) 114 : Binary(Binary::ID_MachOUniversalBinary, Source), Magic(0), 115 NumberOfObjects(0) { 116 ErrorAsOutParameter ErrAsOutParam(&Err); 117 if (Data.getBufferSize() < sizeof(MachO::fat_header)) { 118 Err = make_error<GenericBinaryError>("File too small to be a Mach-O " 119 "universal file", 120 object_error::invalid_file_type); 121 return; 122 } 123 // Check for magic value and sufficient header size. 124 StringRef Buf = getData(); 125 MachO::fat_header H = 126 getUniversalBinaryStruct<MachO::fat_header>(Buf.begin()); 127 Magic = H.magic; 128 NumberOfObjects = H.nfat_arch; 129 if (NumberOfObjects == 0) { 130 Err = malformedError("contains zero architecture types"); 131 return; 132 } 133 uint32_t MinSize = sizeof(MachO::fat_header); 134 if (Magic == MachO::FAT_MAGIC) 135 MinSize += sizeof(MachO::fat_arch) * NumberOfObjects; 136 else if (Magic == MachO::FAT_MAGIC_64) 137 MinSize += sizeof(MachO::fat_arch_64) * NumberOfObjects; 138 else { 139 Err = malformedError("bad magic number"); 140 return; 141 } 142 if (Buf.size() < MinSize) { 143 Err = malformedError("fat_arch" + 144 Twine(Magic == MachO::FAT_MAGIC ? "" : "_64") + 145 " structs would extend past the end of the file"); 146 return; 147 } 148 for (uint32_t i = 0; i < NumberOfObjects; i++) { 149 ObjectForArch A(this, i); 150 uint64_t bigSize = A.getOffset(); 151 bigSize += A.getSize(); 152 if (bigSize > Buf.size()) { 153 Err = malformedError("offset plus size of cputype (" + 154 Twine(A.getCPUType()) + ") cpusubtype (" + 155 Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + 156 ") extends past the end of the file"); 157 return; 158 } 159 #define MAXSECTALIGN 15 /* 2**15 or 0x8000 */ 160 if (A.getAlign() > MAXSECTALIGN) { 161 Err = malformedError("align (2^" + Twine(A.getAlign()) + ") too large " 162 "for cputype (" + Twine(A.getCPUType()) + ") cpusubtype (" + 163 Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + 164 ") (maximum 2^" + Twine(MAXSECTALIGN) + ")"); 165 return; 166 } 167 if(A.getOffset() % (1 << A.getAlign()) != 0){ 168 Err = malformedError("offset: " + Twine(A.getOffset()) + 169 " for cputype (" + Twine(A.getCPUType()) + ") cpusubtype (" + 170 Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + 171 ") not aligned on it's alignment (2^" + Twine(A.getAlign()) + ")"); 172 return; 173 } 174 if (A.getOffset() < MinSize) { 175 Err = malformedError("cputype (" + Twine(A.getCPUType()) + ") " 176 "cpusubtype (" + Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + 177 ") offset " + Twine(A.getOffset()) + " overlaps universal headers"); 178 return; 179 } 180 } 181 for (uint32_t i = 0; i < NumberOfObjects; i++) { 182 ObjectForArch A(this, i); 183 for (uint32_t j = i + 1; j < NumberOfObjects; j++) { 184 ObjectForArch B(this, j); 185 if (A.getCPUType() == B.getCPUType() && 186 (A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) == 187 (B.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK)) { 188 Err = malformedError("contains two of the same architecture (cputype " 189 "(" + Twine(A.getCPUType()) + ") cpusubtype (" + 190 Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + "))"); 191 return; 192 } 193 if ((A.getOffset() >= B.getOffset() && 194 A.getOffset() < B.getOffset() + B.getSize()) || 195 (A.getOffset() + A.getSize() > B.getOffset() && 196 A.getOffset() + A.getSize() < B.getOffset() + B.getSize()) || 197 (A.getOffset() <= B.getOffset() && 198 A.getOffset() + A.getSize() >= B.getOffset() + B.getSize())) { 199 Err = malformedError("cputype (" + Twine(A.getCPUType()) + ") " 200 "cpusubtype (" + Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + 201 ") at offset " + Twine(A.getOffset()) + " with a size of " + 202 Twine(A.getSize()) + ", overlaps cputype (" + Twine(B.getCPUType()) + 203 ") cpusubtype (" + Twine(B.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) 204 + ") at offset " + Twine(B.getOffset()) + " with a size of " 205 + Twine(B.getSize())); 206 return; 207 } 208 } 209 } 210 Err = Error::success(); 211 } 212 213 Expected<std::unique_ptr<MachOObjectFile>> 214 MachOUniversalBinary::getObjectForArch(StringRef ArchName) const { 215 if (Triple(ArchName).getArch() == Triple::ArchType::UnknownArch) 216 return make_error<GenericBinaryError>("Unknown architecture " 217 "named: " + 218 ArchName, 219 object_error::arch_not_found); 220 221 for (auto &Obj : objects()) 222 if (Obj.getArchFlagName() == ArchName) 223 return Obj.getAsObjectFile(); 224 return make_error<GenericBinaryError>("fat file does not " 225 "contain " + 226 ArchName, 227 object_error::arch_not_found); 228 } 229