1e6388e62SAlexey Samsonov //===- MachOUniversal.cpp - Mach-O universal binary -------------*- C++ -*-===//
2e6388e62SAlexey Samsonov //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e6388e62SAlexey Samsonov //
7e6388e62SAlexey Samsonov //===----------------------------------------------------------------------===//
8e6388e62SAlexey Samsonov //
9e6388e62SAlexey Samsonov // This file defines the MachOUniversalBinary class.
10e6388e62SAlexey Samsonov //
11e6388e62SAlexey Samsonov //===----------------------------------------------------------------------===//
12e6388e62SAlexey Samsonov 
13e6388e62SAlexey Samsonov #include "llvm/Object/MachOUniversal.h"
14d9903888SChandler Carruth #include "llvm/Object/Archive.h"
15c6f7ac00SAdrien Guinet #include "llvm/Object/IRObjectFile.h"
16e6388e62SAlexey Samsonov #include "llvm/Object/MachO.h"
17e6388e62SAlexey Samsonov #include "llvm/Object/ObjectFile.h"
18*e72c195fSserge-sans-paille #include "llvm/Support/ErrorHandling.h"
19*e72c195fSserge-sans-paille #include "llvm/Support/SwapByteOrder.h"
20*e72c195fSserge-sans-paille #include "llvm/Support/type_traits.h"
21e6388e62SAlexey Samsonov 
22e6388e62SAlexey Samsonov using namespace llvm;
23e6388e62SAlexey Samsonov using namespace object;
24e6388e62SAlexey Samsonov 
251051909dSKevin Enderby static Error
malformedError(Twine Msg)261051909dSKevin Enderby malformedError(Twine Msg) {
271051909dSKevin Enderby   std::string StringMsg = "truncated or malformed fat file (" + Msg.str() + ")";
281051909dSKevin Enderby   return make_error<GenericBinaryError>(std::move(StringMsg),
291051909dSKevin Enderby                                         object_error::parse_failed);
301051909dSKevin Enderby }
311051909dSKevin Enderby 
32e6388e62SAlexey Samsonov template<typename T>
getUniversalBinaryStruct(const char * Ptr)33e6388e62SAlexey Samsonov static T getUniversalBinaryStruct(const char *Ptr) {
34e6388e62SAlexey Samsonov   T Res;
35e6388e62SAlexey Samsonov   memcpy(&Res, Ptr, sizeof(T));
36e6388e62SAlexey Samsonov   // Universal binary headers have big-endian byte order.
37e6388e62SAlexey Samsonov   if (sys::IsLittleEndianHost)
388e783eb7SChris Bieneman     swapStruct(Res);
39e6388e62SAlexey Samsonov   return Res;
40e6388e62SAlexey Samsonov }
41e6388e62SAlexey Samsonov 
ObjectForArch(const MachOUniversalBinary * Parent,uint32_t Index)42e6388e62SAlexey Samsonov MachOUniversalBinary::ObjectForArch::ObjectForArch(
43e6388e62SAlexey Samsonov     const MachOUniversalBinary *Parent, uint32_t Index)
44e6388e62SAlexey Samsonov     : Parent(Parent), Index(Index) {
454ffec859SKevin Enderby   // The iterators use Parent as a nullptr and an Index+1 == NumberOfObjects.
464b8fc281SKevin Enderby   if (!Parent || Index >= Parent->getNumberOfObjects()) {
47e6388e62SAlexey Samsonov     clear();
48e6388e62SAlexey Samsonov   } else {
49e6388e62SAlexey Samsonov     // Parse object header.
50e6388e62SAlexey Samsonov     StringRef ParentData = Parent->getData();
51eb6d110cSKevin Enderby     if (Parent->getMagic() == MachO::FAT_MAGIC) {
528bdfafd5SCharles Davis       const char *HeaderPos = ParentData.begin() + sizeof(MachO::fat_header) +
538bdfafd5SCharles Davis                               Index * sizeof(MachO::fat_arch);
548bdfafd5SCharles Davis       Header = getUniversalBinaryStruct<MachO::fat_arch>(HeaderPos);
55eb6d110cSKevin Enderby     } else { // Parent->getMagic() == MachO::FAT_MAGIC_64
56eb6d110cSKevin Enderby       const char *HeaderPos = ParentData.begin() + sizeof(MachO::fat_header) +
57eb6d110cSKevin Enderby                               Index * sizeof(MachO::fat_arch_64);
58eb6d110cSKevin Enderby       Header64 = getUniversalBinaryStruct<MachO::fat_arch_64>(HeaderPos);
59eb6d110cSKevin Enderby     }
60e6388e62SAlexey Samsonov   }
61e6388e62SAlexey Samsonov }
62e6388e62SAlexey Samsonov 
639acb1099SKevin Enderby Expected<std::unique_ptr<MachOObjectFile>>
getAsObjectFile() const644f7932b2SRafael Espindola MachOUniversalBinary::ObjectForArch::getAsObjectFile() const {
656eb30ee4SFrederic Riss   if (!Parent)
6642398051SKevin Enderby     report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsObjectFile() "
6742398051SKevin Enderby                        "called when Parent is a nullptr");
686eb30ee4SFrederic Riss 
69e6388e62SAlexey Samsonov   StringRef ParentData = Parent->getData();
70eb6d110cSKevin Enderby   StringRef ObjectData;
7179d6c63fSKevin Enderby   uint32_t cputype;
7279d6c63fSKevin Enderby   if (Parent->getMagic() == MachO::FAT_MAGIC) {
73eb6d110cSKevin Enderby     ObjectData = ParentData.substr(Header.offset, Header.size);
7479d6c63fSKevin Enderby     cputype = Header.cputype;
7579d6c63fSKevin Enderby   } else { // Parent->getMagic() == MachO::FAT_MAGIC_64
76eb6d110cSKevin Enderby     ObjectData = ParentData.substr(Header64.offset, Header64.size);
7779d6c63fSKevin Enderby     cputype = Header64.cputype;
7879d6c63fSKevin Enderby   }
79113ba3e6SRafael Espindola   StringRef ObjectName = Parent->getFileName();
8048af1c2aSRafael Espindola   MemoryBufferRef ObjBuffer(ObjectData, ObjectName);
8179d6c63fSKevin Enderby   return ObjectFile::createMachOObjectFile(ObjBuffer, cputype, Index);
82e6388e62SAlexey Samsonov }
83e6388e62SAlexey Samsonov 
84c6f7ac00SAdrien Guinet Expected<std::unique_ptr<IRObjectFile>>
getAsIRObject(LLVMContext & Ctx) const85c6f7ac00SAdrien Guinet MachOUniversalBinary::ObjectForArch::getAsIRObject(LLVMContext &Ctx) const {
86c6f7ac00SAdrien Guinet   if (!Parent)
87c6f7ac00SAdrien Guinet     report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsIRObject() "
88c6f7ac00SAdrien Guinet                        "called when Parent is a nullptr");
89c6f7ac00SAdrien Guinet 
90c6f7ac00SAdrien Guinet   StringRef ParentData = Parent->getData();
91c6f7ac00SAdrien Guinet   StringRef ObjectData;
92c6f7ac00SAdrien Guinet   if (Parent->getMagic() == MachO::FAT_MAGIC) {
93c6f7ac00SAdrien Guinet     ObjectData = ParentData.substr(Header.offset, Header.size);
94c6f7ac00SAdrien Guinet   } else { // Parent->getMagic() == MachO::FAT_MAGIC_64
95c6f7ac00SAdrien Guinet     ObjectData = ParentData.substr(Header64.offset, Header64.size);
96c6f7ac00SAdrien Guinet   }
97c6f7ac00SAdrien Guinet   StringRef ObjectName = Parent->getFileName();
98c6f7ac00SAdrien Guinet   MemoryBufferRef ObjBuffer(ObjectData, ObjectName);
99c6f7ac00SAdrien Guinet 
100c6f7ac00SAdrien Guinet   return IRObjectFile::create(ObjBuffer, Ctx);
101c6f7ac00SAdrien Guinet }
102c6f7ac00SAdrien Guinet 
10342398051SKevin Enderby Expected<std::unique_ptr<Archive>>
getAsArchive() const1040bfe828fSRafael Espindola MachOUniversalBinary::ObjectForArch::getAsArchive() const {
1050bfe828fSRafael Espindola   if (!Parent)
10642398051SKevin Enderby     report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsArchive() "
10742398051SKevin Enderby                        "called when Parent is a nullptr");
1080bfe828fSRafael Espindola 
109e858a653SKevin Enderby   StringRef ParentData = Parent->getData();
110eb6d110cSKevin Enderby   StringRef ObjectData;
111eb6d110cSKevin Enderby   if (Parent->getMagic() == MachO::FAT_MAGIC)
112eb6d110cSKevin Enderby     ObjectData = ParentData.substr(Header.offset, Header.size);
113eb6d110cSKevin Enderby   else // Parent->getMagic() == MachO::FAT_MAGIC_64
114eb6d110cSKevin Enderby     ObjectData = ParentData.substr(Header64.offset, Header64.size);
115113ba3e6SRafael Espindola   StringRef ObjectName = Parent->getFileName();
11648af1c2aSRafael Espindola   MemoryBufferRef ObjBuffer(ObjectData, ObjectName);
117c60a321cSKevin Enderby   return Archive::create(ObjBuffer);
118e858a653SKevin Enderby }
119e858a653SKevin Enderby 
anchor()120e6388e62SAlexey Samsonov void MachOUniversalBinary::anchor() { }
121e6388e62SAlexey Samsonov 
1221051909dSKevin Enderby Expected<std::unique_ptr<MachOUniversalBinary>>
create(MemoryBufferRef Source)12348af1c2aSRafael Espindola MachOUniversalBinary::create(MemoryBufferRef Source) {
12441af4309SMehdi Amini   Error Err = Error::success();
12556440fd8SAhmed Charles   std::unique_ptr<MachOUniversalBinary> Ret(
1261051909dSKevin Enderby       new MachOUniversalBinary(Source, Err));
1271051909dSKevin Enderby   if (Err)
128c55cf4afSBill Wendling     return std::move(Err);
129c55cf4afSBill Wendling   return std::move(Ret);
130692410efSRafael Espindola }
131692410efSRafael Espindola 
MachOUniversalBinary(MemoryBufferRef Source,Error & Err)1321051909dSKevin Enderby MachOUniversalBinary::MachOUniversalBinary(MemoryBufferRef Source, Error &Err)
133eb6d110cSKevin Enderby     : Binary(Binary::ID_MachOUniversalBinary, Source), Magic(0),
134eb6d110cSKevin Enderby       NumberOfObjects(0) {
1355e51a2e3SLang Hames   ErrorAsOutParameter ErrAsOutParam(&Err);
13648af1c2aSRafael Espindola   if (Data.getBufferSize() < sizeof(MachO::fat_header)) {
1371051909dSKevin Enderby     Err = make_error<GenericBinaryError>("File too small to be a Mach-O "
1381051909dSKevin Enderby                                          "universal file",
1391051909dSKevin Enderby                                          object_error::invalid_file_type);
140e6388e62SAlexey Samsonov     return;
141e6388e62SAlexey Samsonov   }
142e6388e62SAlexey Samsonov   // Check for magic value and sufficient header size.
143e6388e62SAlexey Samsonov   StringRef Buf = getData();
1441ead14b1SVedant Kumar   MachO::fat_header H =
1451ead14b1SVedant Kumar       getUniversalBinaryStruct<MachO::fat_header>(Buf.begin());
146eb6d110cSKevin Enderby   Magic = H.magic;
1478bdfafd5SCharles Davis   NumberOfObjects = H.nfat_arch;
1484ffec859SKevin Enderby   if (NumberOfObjects == 0) {
1494ffec859SKevin Enderby     Err = malformedError("contains zero architecture types");
1504ffec859SKevin Enderby     return;
1514ffec859SKevin Enderby   }
152eb6d110cSKevin Enderby   uint32_t MinSize = sizeof(MachO::fat_header);
153eb6d110cSKevin Enderby   if (Magic == MachO::FAT_MAGIC)
154eb6d110cSKevin Enderby     MinSize += sizeof(MachO::fat_arch) * NumberOfObjects;
155eb6d110cSKevin Enderby   else if (Magic == MachO::FAT_MAGIC_64)
156eb6d110cSKevin Enderby     MinSize += sizeof(MachO::fat_arch_64) * NumberOfObjects;
157eb6d110cSKevin Enderby   else {
1581051909dSKevin Enderby     Err = malformedError("bad magic number");
159eb6d110cSKevin Enderby     return;
160eb6d110cSKevin Enderby   }
161eb6d110cSKevin Enderby   if (Buf.size() < MinSize) {
1621051909dSKevin Enderby     Err = malformedError("fat_arch" +
1631051909dSKevin Enderby                          Twine(Magic == MachO::FAT_MAGIC ? "" : "_64") +
1641051909dSKevin Enderby                          " structs would extend past the end of the file");
165e6388e62SAlexey Samsonov     return;
166e6388e62SAlexey Samsonov   }
1674ffec859SKevin Enderby   for (uint32_t i = 0; i < NumberOfObjects; i++) {
1684ffec859SKevin Enderby     ObjectForArch A(this, i);
1694ffec859SKevin Enderby     uint64_t bigSize = A.getOffset();
1704ffec859SKevin Enderby     bigSize += A.getSize();
1714ffec859SKevin Enderby     if (bigSize > Buf.size()) {
1724ffec859SKevin Enderby       Err = malformedError("offset plus size of cputype (" +
1734ffec859SKevin Enderby         Twine(A.getCPUType()) + ") cpusubtype (" +
1744ffec859SKevin Enderby         Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) +
1754ffec859SKevin Enderby         ") extends past the end of the file");
1764ffec859SKevin Enderby       return;
1774ffec859SKevin Enderby     }
178a67f6f17SShoaib Meenai 
179a67f6f17SShoaib Meenai     if (A.getAlign() > MaxSectionAlignment) {
180a67f6f17SShoaib Meenai       Err = malformedError("align (2^" + Twine(A.getAlign()) +
181a67f6f17SShoaib Meenai                            ") too large for cputype (" + Twine(A.getCPUType()) +
182a67f6f17SShoaib Meenai                            ") cpusubtype (" +
1834ffec859SKevin Enderby                            Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) +
184a67f6f17SShoaib Meenai                            ") (maximum 2^" + Twine(MaxSectionAlignment) + ")");
1854ffec859SKevin Enderby       return;
1864ffec859SKevin Enderby     }
187da40d4e4SSimon Pilgrim     if(A.getOffset() % (1ull << A.getAlign()) != 0){
1884ffec859SKevin Enderby       Err = malformedError("offset: " + Twine(A.getOffset()) +
1894ffec859SKevin Enderby         " for cputype (" + Twine(A.getCPUType()) + ") cpusubtype (" +
1904ffec859SKevin Enderby         Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) +
1914ffec859SKevin Enderby         ") not aligned on it's alignment (2^" + Twine(A.getAlign()) + ")");
1924ffec859SKevin Enderby       return;
1934ffec859SKevin Enderby     }
1944ffec859SKevin Enderby     if (A.getOffset() < MinSize) {
1954ffec859SKevin Enderby       Err =  malformedError("cputype (" + Twine(A.getCPUType()) + ") "
1964ffec859SKevin Enderby         "cpusubtype (" + Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) +
1974ffec859SKevin Enderby         ") offset " + Twine(A.getOffset()) + " overlaps universal headers");
1984ffec859SKevin Enderby       return;
1994ffec859SKevin Enderby     }
2004ffec859SKevin Enderby   }
2014ffec859SKevin Enderby   for (uint32_t i = 0; i < NumberOfObjects; i++) {
2024ffec859SKevin Enderby     ObjectForArch A(this, i);
2034ffec859SKevin Enderby     for (uint32_t j = i + 1; j < NumberOfObjects; j++) {
2044ffec859SKevin Enderby       ObjectForArch B(this, j);
2054ffec859SKevin Enderby       if (A.getCPUType() == B.getCPUType() &&
2064ffec859SKevin Enderby           (A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) ==
2074ffec859SKevin Enderby           (B.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK)) {
2084ffec859SKevin Enderby         Err = malformedError("contains two of the same architecture (cputype "
2094ffec859SKevin Enderby           "(" + Twine(A.getCPUType()) + ") cpusubtype (" +
2104ffec859SKevin Enderby           Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + "))");
2114ffec859SKevin Enderby         return;
2124ffec859SKevin Enderby       }
2134ffec859SKevin Enderby       if ((A.getOffset() >= B.getOffset() &&
2144ffec859SKevin Enderby            A.getOffset() < B.getOffset() + B.getSize()) ||
2154ffec859SKevin Enderby           (A.getOffset() + A.getSize() > B.getOffset() &&
2164ffec859SKevin Enderby            A.getOffset() + A.getSize() < B.getOffset() + B.getSize()) ||
2174ffec859SKevin Enderby           (A.getOffset() <= B.getOffset() &&
2184ffec859SKevin Enderby            A.getOffset() + A.getSize() >= B.getOffset() + B.getSize())) {
2194ffec859SKevin Enderby         Err =  malformedError("cputype (" + Twine(A.getCPUType()) + ") "
2204ffec859SKevin Enderby           "cpusubtype (" + Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) +
2214ffec859SKevin Enderby           ") at offset " + Twine(A.getOffset()) + " with a size of " +
2224ffec859SKevin Enderby           Twine(A.getSize()) + ", overlaps cputype (" + Twine(B.getCPUType()) +
2234ffec859SKevin Enderby           ") cpusubtype (" + Twine(B.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK)
2244ffec859SKevin Enderby           + ") at offset " + Twine(B.getOffset()) + " with a size of "
2254ffec859SKevin Enderby           + Twine(B.getSize()));
2264ffec859SKevin Enderby         return;
2274ffec859SKevin Enderby       }
2284ffec859SKevin Enderby     }
2294ffec859SKevin Enderby   }
2301051909dSKevin Enderby   Err = Error::success();
231e6388e62SAlexey Samsonov }
232e6388e62SAlexey Samsonov 
2334fd11c1eSAlexander Shaposhnikov Expected<MachOUniversalBinary::ObjectForArch>
getObjectForArch(StringRef ArchName) const234ebc162a7SFrederic Riss MachOUniversalBinary::getObjectForArch(StringRef ArchName) const {
235ebc162a7SFrederic Riss   if (Triple(ArchName).getArch() == Triple::ArchType::UnknownArch)
2361ead14b1SVedant Kumar     return make_error<GenericBinaryError>("Unknown architecture "
2371ead14b1SVedant Kumar                                           "named: " +
2381ead14b1SVedant Kumar                                               ArchName,
23942398051SKevin Enderby                                           object_error::arch_not_found);
2404fd11c1eSAlexander Shaposhnikov   for (const auto &Obj : objects())
24159343a94SKevin Enderby     if (Obj.getArchFlagName() == ArchName)
2424fd11c1eSAlexander Shaposhnikov       return Obj;
2431ead14b1SVedant Kumar   return make_error<GenericBinaryError>("fat file does not "
2441ead14b1SVedant Kumar                                         "contain " +
2451ead14b1SVedant Kumar                                             ArchName,
24642398051SKevin Enderby                                         object_error::arch_not_found);
247e6388e62SAlexey Samsonov }
2484fd11c1eSAlexander Shaposhnikov 
2494fd11c1eSAlexander Shaposhnikov Expected<std::unique_ptr<MachOObjectFile>>
getMachOObjectForArch(StringRef ArchName) const2504fd11c1eSAlexander Shaposhnikov MachOUniversalBinary::getMachOObjectForArch(StringRef ArchName) const {
2514fd11c1eSAlexander Shaposhnikov   Expected<ObjectForArch> O = getObjectForArch(ArchName);
2524fd11c1eSAlexander Shaposhnikov   if (!O)
2534fd11c1eSAlexander Shaposhnikov     return O.takeError();
2544fd11c1eSAlexander Shaposhnikov   return O->getAsObjectFile();
2554fd11c1eSAlexander Shaposhnikov }
2564fd11c1eSAlexander Shaposhnikov 
257c6f7ac00SAdrien Guinet Expected<std::unique_ptr<IRObjectFile>>
getIRObjectForArch(StringRef ArchName,LLVMContext & Ctx) const258c6f7ac00SAdrien Guinet MachOUniversalBinary::getIRObjectForArch(StringRef ArchName,
259c6f7ac00SAdrien Guinet                                          LLVMContext &Ctx) const {
260c6f7ac00SAdrien Guinet   Expected<ObjectForArch> O = getObjectForArch(ArchName);
261c6f7ac00SAdrien Guinet   if (!O)
262c6f7ac00SAdrien Guinet     return O.takeError();
263c6f7ac00SAdrien Guinet   return O->getAsIRObject(Ctx);
264c6f7ac00SAdrien Guinet }
265c6f7ac00SAdrien Guinet 
2664fd11c1eSAlexander Shaposhnikov Expected<std::unique_ptr<Archive>>
getArchiveForArch(StringRef ArchName) const2674fd11c1eSAlexander Shaposhnikov MachOUniversalBinary::getArchiveForArch(StringRef ArchName) const {
2684fd11c1eSAlexander Shaposhnikov   Expected<ObjectForArch> O = getObjectForArch(ArchName);
2694fd11c1eSAlexander Shaposhnikov   if (!O)
2704fd11c1eSAlexander Shaposhnikov     return O.takeError();
2714fd11c1eSAlexander Shaposhnikov   return O->getAsArchive();
2724fd11c1eSAlexander Shaposhnikov }
273