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