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/IRObjectFile.h" 16 #include "llvm/Object/MachO.h" 17 #include "llvm/Object/ObjectFile.h" 18 #include "llvm/Support/ErrorHandling.h" 19 #include "llvm/Support/SwapByteOrder.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<IRObjectFile>> 84 MachOUniversalBinary::ObjectForArch::getAsIRObject(LLVMContext &Ctx) const { 85 if (!Parent) 86 report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsIRObject() " 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 } 96 StringRef ObjectName = Parent->getFileName(); 97 MemoryBufferRef ObjBuffer(ObjectData, ObjectName); 98 99 return IRObjectFile::create(ObjBuffer, Ctx); 100 } 101 102 Expected<std::unique_ptr<Archive>> 103 MachOUniversalBinary::ObjectForArch::getAsArchive() const { 104 if (!Parent) 105 report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsArchive() " 106 "called when Parent is a nullptr"); 107 108 StringRef ParentData = Parent->getData(); 109 StringRef ObjectData; 110 if (Parent->getMagic() == MachO::FAT_MAGIC) 111 ObjectData = ParentData.substr(Header.offset, Header.size); 112 else // Parent->getMagic() == MachO::FAT_MAGIC_64 113 ObjectData = ParentData.substr(Header64.offset, Header64.size); 114 StringRef ObjectName = Parent->getFileName(); 115 MemoryBufferRef ObjBuffer(ObjectData, ObjectName); 116 return Archive::create(ObjBuffer); 117 } 118 119 void MachOUniversalBinary::anchor() { } 120 121 Expected<std::unique_ptr<MachOUniversalBinary>> 122 MachOUniversalBinary::create(MemoryBufferRef Source) { 123 Error Err = Error::success(); 124 std::unique_ptr<MachOUniversalBinary> Ret( 125 new MachOUniversalBinary(Source, Err)); 126 if (Err) 127 return std::move(Err); 128 return std::move(Ret); 129 } 130 131 MachOUniversalBinary::MachOUniversalBinary(MemoryBufferRef Source, Error &Err) 132 : Binary(Binary::ID_MachOUniversalBinary, Source), Magic(0), 133 NumberOfObjects(0) { 134 ErrorAsOutParameter ErrAsOutParam(Err); 135 if (Data.getBufferSize() < sizeof(MachO::fat_header)) { 136 Err = make_error<GenericBinaryError>("File too small to be a Mach-O " 137 "universal file", 138 object_error::invalid_file_type); 139 return; 140 } 141 // Check for magic value and sufficient header size. 142 StringRef Buf = getData(); 143 MachO::fat_header H = 144 getUniversalBinaryStruct<MachO::fat_header>(Buf.begin()); 145 Magic = H.magic; 146 NumberOfObjects = H.nfat_arch; 147 if (NumberOfObjects == 0) { 148 Err = malformedError("contains zero architecture types"); 149 return; 150 } 151 uint32_t MinSize = sizeof(MachO::fat_header); 152 if (Magic == MachO::FAT_MAGIC) 153 MinSize += sizeof(MachO::fat_arch) * NumberOfObjects; 154 else if (Magic == MachO::FAT_MAGIC_64) 155 MinSize += sizeof(MachO::fat_arch_64) * NumberOfObjects; 156 else { 157 Err = malformedError("bad magic number"); 158 return; 159 } 160 if (Buf.size() < MinSize) { 161 Err = malformedError("fat_arch" + 162 Twine(Magic == MachO::FAT_MAGIC ? "" : "_64") + 163 " structs would extend past the end of the file"); 164 return; 165 } 166 for (uint32_t i = 0; i < NumberOfObjects; i++) { 167 ObjectForArch A(this, i); 168 uint64_t bigSize = A.getOffset(); 169 bigSize += A.getSize(); 170 if (bigSize > Buf.size()) { 171 Err = malformedError("offset plus size of cputype (" + 172 Twine(A.getCPUType()) + ") cpusubtype (" + 173 Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + 174 ") extends past the end of the file"); 175 return; 176 } 177 178 if (A.getAlign() > MaxSectionAlignment) { 179 Err = malformedError("align (2^" + Twine(A.getAlign()) + 180 ") too large for cputype (" + Twine(A.getCPUType()) + 181 ") cpusubtype (" + 182 Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + 183 ") (maximum 2^" + Twine(MaxSectionAlignment) + ")"); 184 return; 185 } 186 if(A.getOffset() % (1ull << A.getAlign()) != 0){ 187 Err = malformedError("offset: " + Twine(A.getOffset()) + 188 " for cputype (" + Twine(A.getCPUType()) + ") cpusubtype (" + 189 Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + 190 ") not aligned on it's alignment (2^" + Twine(A.getAlign()) + ")"); 191 return; 192 } 193 if (A.getOffset() < MinSize) { 194 Err = malformedError("cputype (" + Twine(A.getCPUType()) + ") " 195 "cpusubtype (" + Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + 196 ") offset " + Twine(A.getOffset()) + " overlaps universal headers"); 197 return; 198 } 199 } 200 for (uint32_t i = 0; i < NumberOfObjects; i++) { 201 ObjectForArch A(this, i); 202 for (uint32_t j = i + 1; j < NumberOfObjects; j++) { 203 ObjectForArch B(this, j); 204 if (A.getCPUType() == B.getCPUType() && 205 (A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) == 206 (B.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK)) { 207 Err = malformedError("contains two of the same architecture (cputype " 208 "(" + Twine(A.getCPUType()) + ") cpusubtype (" + 209 Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + "))"); 210 return; 211 } 212 if ((A.getOffset() >= B.getOffset() && 213 A.getOffset() < B.getOffset() + B.getSize()) || 214 (A.getOffset() + A.getSize() > B.getOffset() && 215 A.getOffset() + A.getSize() < B.getOffset() + B.getSize()) || 216 (A.getOffset() <= B.getOffset() && 217 A.getOffset() + A.getSize() >= B.getOffset() + B.getSize())) { 218 Err = malformedError("cputype (" + Twine(A.getCPUType()) + ") " 219 "cpusubtype (" + Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + 220 ") at offset " + Twine(A.getOffset()) + " with a size of " + 221 Twine(A.getSize()) + ", overlaps cputype (" + Twine(B.getCPUType()) + 222 ") cpusubtype (" + Twine(B.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) 223 + ") at offset " + Twine(B.getOffset()) + " with a size of " 224 + Twine(B.getSize())); 225 return; 226 } 227 } 228 } 229 Err = Error::success(); 230 } 231 232 Expected<MachOUniversalBinary::ObjectForArch> 233 MachOUniversalBinary::getObjectForArch(StringRef ArchName) const { 234 if (Triple(ArchName).getArch() == Triple::ArchType::UnknownArch) 235 return make_error<GenericBinaryError>("Unknown architecture " 236 "named: " + 237 ArchName, 238 object_error::arch_not_found); 239 for (const auto &Obj : objects()) 240 if (Obj.getArchFlagName() == ArchName) 241 return Obj; 242 return make_error<GenericBinaryError>("fat file does not " 243 "contain " + 244 ArchName, 245 object_error::arch_not_found); 246 } 247 248 Expected<std::unique_ptr<MachOObjectFile>> 249 MachOUniversalBinary::getMachOObjectForArch(StringRef ArchName) const { 250 Expected<ObjectForArch> O = getObjectForArch(ArchName); 251 if (!O) 252 return O.takeError(); 253 return O->getAsObjectFile(); 254 } 255 256 Expected<std::unique_ptr<IRObjectFile>> 257 MachOUniversalBinary::getIRObjectForArch(StringRef ArchName, 258 LLVMContext &Ctx) const { 259 Expected<ObjectForArch> O = getObjectForArch(ArchName); 260 if (!O) 261 return O.takeError(); 262 return O->getAsIRObject(Ctx); 263 } 264 265 Expected<std::unique_ptr<Archive>> 266 MachOUniversalBinary::getArchiveForArch(StringRef ArchName) const { 267 Expected<ObjectForArch> O = getObjectForArch(ArchName); 268 if (!O) 269 return O.takeError(); 270 return O->getAsArchive(); 271 } 272