272 lines · cpp
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-exception6//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 21using namespace llvm;22using namespace object;23 24static Error25malformedError(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 31template<typename T>32static 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 41MachOUniversalBinary::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_6455 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 62Expected<std::unique_ptr<MachOObjectFile>>63MachOUniversalBinary::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_6475 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 83Expected<std::unique_ptr<IRObjectFile>>84MachOUniversalBinary::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_6494 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 102Expected<std::unique_ptr<Archive>>103MachOUniversalBinary::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_64113 ObjectData = ParentData.substr(Header64.offset, Header64.size);114 StringRef ObjectName = Parent->getFileName();115 MemoryBufferRef ObjBuffer(ObjectData, ObjectName);116 return Archive::create(ObjBuffer);117}118 119void MachOUniversalBinary::anchor() { }120 121Expected<std::unique_ptr<MachOUniversalBinary>>122MachOUniversalBinary::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 131MachOUniversalBinary::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 232Expected<MachOUniversalBinary::ObjectForArch>233MachOUniversalBinary::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 248Expected<std::unique_ptr<MachOObjectFile>>249MachOUniversalBinary::getMachOObjectForArch(StringRef ArchName) const {250 Expected<ObjectForArch> O = getObjectForArch(ArchName);251 if (!O)252 return O.takeError();253 return O->getAsObjectFile();254}255 256Expected<std::unique_ptr<IRObjectFile>>257MachOUniversalBinary::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 265Expected<std::unique_ptr<Archive>>266MachOUniversalBinary::getArchiveForArch(StringRef ArchName) const {267 Expected<ObjectForArch> O = getObjectForArch(ArchName);268 if (!O)269 return O.takeError();270 return O->getAsArchive();271}272