1 //===- MachOUniversal.cpp - Mach-O universal binary -------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines the MachOUniversalBinary class. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "llvm/Object/MachOUniversal.h" 14 #include "llvm/Object/Archive.h" 15 #include "llvm/Object/MachO.h" 16 #include "llvm/Object/ObjectFile.h" 17 #include "llvm/Support/Casting.h" 18 #include "llvm/Support/Host.h" 19 #include "llvm/Support/MemoryBuffer.h" 20 21 using namespace llvm; 22 using namespace object; 23 24 static Error 25 malformedError(Twine Msg) { 26 std::string StringMsg = "truncated or malformed fat file (" + Msg.str() + ")"; 27 return make_error<GenericBinaryError>(std::move(StringMsg), 28 object_error::parse_failed); 29 } 30 31 template<typename T> 32 static T getUniversalBinaryStruct(const char *Ptr) { 33 T Res; 34 memcpy(&Res, Ptr, sizeof(T)); 35 // Universal binary headers have big-endian byte order. 36 if (sys::IsLittleEndianHost) 37 swapStruct(Res); 38 return Res; 39 } 40 41 MachOUniversalBinary::ObjectForArch::ObjectForArch( 42 const MachOUniversalBinary *Parent, uint32_t Index) 43 : Parent(Parent), Index(Index) { 44 // The iterators use Parent as a nullptr and an Index+1 == NumberOfObjects. 45 if (!Parent || Index >= Parent->getNumberOfObjects()) { 46 clear(); 47 } else { 48 // Parse object header. 49 StringRef ParentData = Parent->getData(); 50 if (Parent->getMagic() == MachO::FAT_MAGIC) { 51 const char *HeaderPos = ParentData.begin() + sizeof(MachO::fat_header) + 52 Index * sizeof(MachO::fat_arch); 53 Header = getUniversalBinaryStruct<MachO::fat_arch>(HeaderPos); 54 } else { // Parent->getMagic() == MachO::FAT_MAGIC_64 55 const char *HeaderPos = ParentData.begin() + sizeof(MachO::fat_header) + 56 Index * sizeof(MachO::fat_arch_64); 57 Header64 = getUniversalBinaryStruct<MachO::fat_arch_64>(HeaderPos); 58 } 59 } 60 } 61 62 Expected<std::unique_ptr<MachOObjectFile>> 63 MachOUniversalBinary::ObjectForArch::getAsObjectFile() const { 64 if (!Parent) 65 report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsObjectFile() " 66 "called when Parent is a nullptr"); 67 68 StringRef ParentData = Parent->getData(); 69 StringRef ObjectData; 70 uint32_t cputype; 71 if (Parent->getMagic() == MachO::FAT_MAGIC) { 72 ObjectData = ParentData.substr(Header.offset, Header.size); 73 cputype = Header.cputype; 74 } else { // Parent->getMagic() == MachO::FAT_MAGIC_64 75 ObjectData = ParentData.substr(Header64.offset, Header64.size); 76 cputype = Header64.cputype; 77 } 78 StringRef ObjectName = Parent->getFileName(); 79 MemoryBufferRef ObjBuffer(ObjectData, ObjectName); 80 return ObjectFile::createMachOObjectFile(ObjBuffer, cputype, Index); 81 } 82 83 Expected<std::unique_ptr<Archive>> 84 MachOUniversalBinary::ObjectForArch::getAsArchive() const { 85 if (!Parent) 86 report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsArchive() " 87 "called when Parent is a nullptr"); 88 89 StringRef ParentData = Parent->getData(); 90 StringRef ObjectData; 91 if (Parent->getMagic() == MachO::FAT_MAGIC) 92 ObjectData = ParentData.substr(Header.offset, Header.size); 93 else // Parent->getMagic() == MachO::FAT_MAGIC_64 94 ObjectData = ParentData.substr(Header64.offset, Header64.size); 95 StringRef ObjectName = Parent->getFileName(); 96 MemoryBufferRef ObjBuffer(ObjectData, ObjectName); 97 return Archive::create(ObjBuffer); 98 } 99 100 void MachOUniversalBinary::anchor() { } 101 102 Expected<std::unique_ptr<MachOUniversalBinary>> 103 MachOUniversalBinary::create(MemoryBufferRef Source) { 104 Error Err = Error::success(); 105 std::unique_ptr<MachOUniversalBinary> Ret( 106 new MachOUniversalBinary(Source, Err)); 107 if (Err) 108 return std::move(Err); 109 return std::move(Ret); 110 } 111 112 MachOUniversalBinary::MachOUniversalBinary(MemoryBufferRef Source, Error &Err) 113 : Binary(Binary::ID_MachOUniversalBinary, Source), Magic(0), 114 NumberOfObjects(0) { 115 ErrorAsOutParameter ErrAsOutParam(&Err); 116 if (Data.getBufferSize() < sizeof(MachO::fat_header)) { 117 Err = make_error<GenericBinaryError>("File too small to be a Mach-O " 118 "universal file", 119 object_error::invalid_file_type); 120 return; 121 } 122 // Check for magic value and sufficient header size. 123 StringRef Buf = getData(); 124 MachO::fat_header H = 125 getUniversalBinaryStruct<MachO::fat_header>(Buf.begin()); 126 Magic = H.magic; 127 NumberOfObjects = H.nfat_arch; 128 if (NumberOfObjects == 0) { 129 Err = malformedError("contains zero architecture types"); 130 return; 131 } 132 uint32_t MinSize = sizeof(MachO::fat_header); 133 if (Magic == MachO::FAT_MAGIC) 134 MinSize += sizeof(MachO::fat_arch) * NumberOfObjects; 135 else if (Magic == MachO::FAT_MAGIC_64) 136 MinSize += sizeof(MachO::fat_arch_64) * NumberOfObjects; 137 else { 138 Err = malformedError("bad magic number"); 139 return; 140 } 141 if (Buf.size() < MinSize) { 142 Err = malformedError("fat_arch" + 143 Twine(Magic == MachO::FAT_MAGIC ? "" : "_64") + 144 " structs would extend past the end of the file"); 145 return; 146 } 147 for (uint32_t i = 0; i < NumberOfObjects; i++) { 148 ObjectForArch A(this, i); 149 uint64_t bigSize = A.getOffset(); 150 bigSize += A.getSize(); 151 if (bigSize > Buf.size()) { 152 Err = malformedError("offset plus size of cputype (" + 153 Twine(A.getCPUType()) + ") cpusubtype (" + 154 Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + 155 ") extends past the end of the file"); 156 return; 157 } 158 159 if (A.getAlign() > MaxSectionAlignment) { 160 Err = malformedError("align (2^" + Twine(A.getAlign()) + 161 ") too large for cputype (" + Twine(A.getCPUType()) + 162 ") cpusubtype (" + 163 Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + 164 ") (maximum 2^" + Twine(MaxSectionAlignment) + ")"); 165 return; 166 } 167 if(A.getOffset() % (1ull << 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<MachOUniversalBinary::ObjectForArch> 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 for (const auto &Obj : objects()) 221 if (Obj.getArchFlagName() == ArchName) 222 return Obj; 223 return make_error<GenericBinaryError>("fat file does not " 224 "contain " + 225 ArchName, 226 object_error::arch_not_found); 227 } 228 229 Expected<std::unique_ptr<MachOObjectFile>> 230 MachOUniversalBinary::getMachOObjectForArch(StringRef ArchName) const { 231 Expected<ObjectForArch> O = getObjectForArch(ArchName); 232 if (!O) 233 return O.takeError(); 234 return O->getAsObjectFile(); 235 } 236 237 Expected<std::unique_ptr<Archive>> 238 MachOUniversalBinary::getArchiveForArch(StringRef ArchName) const { 239 Expected<ObjectForArch> O = getObjectForArch(ArchName); 240 if (!O) 241 return O.takeError(); 242 return O->getAsArchive(); 243 } 244