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