1 //===- MachOUniversal.cpp - Mach-O universal binary -------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file defines the MachOUniversalBinary class.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "llvm/Object/MachOUniversal.h"
15 #include "llvm/Object/Archive.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   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       if (ParentData.size() < Header.offset + Header.size) {
55         clear();
56       }
57     } else { // Parent->getMagic() == MachO::FAT_MAGIC_64
58       const char *HeaderPos = ParentData.begin() + sizeof(MachO::fat_header) +
59                               Index * sizeof(MachO::fat_arch_64);
60       Header64 = getUniversalBinaryStruct<MachO::fat_arch_64>(HeaderPos);
61       if (ParentData.size() < Header64.offset + Header64.size) {
62         clear();
63       }
64     }
65   }
66 }
67 
68 Expected<std::unique_ptr<MachOObjectFile>>
69 MachOUniversalBinary::ObjectForArch::getAsObjectFile() const {
70   if (!Parent)
71     report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsObjectFile() "
72                        "called when Parent is a nullptr");
73 
74   StringRef ParentData = Parent->getData();
75   StringRef ObjectData;
76   uint32_t cputype;
77   if (Parent->getMagic() == MachO::FAT_MAGIC) {
78     ObjectData = ParentData.substr(Header.offset, Header.size);
79     cputype = Header.cputype;
80   } else { // Parent->getMagic() == MachO::FAT_MAGIC_64
81     ObjectData = ParentData.substr(Header64.offset, Header64.size);
82     cputype = Header64.cputype;
83   }
84   StringRef ObjectName = Parent->getFileName();
85   MemoryBufferRef ObjBuffer(ObjectData, ObjectName);
86   return ObjectFile::createMachOObjectFile(ObjBuffer, cputype, Index);
87 }
88 
89 Expected<std::unique_ptr<Archive>>
90 MachOUniversalBinary::ObjectForArch::getAsArchive() const {
91   if (!Parent)
92     report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsArchive() "
93                        "called when Parent is a nullptr");
94 
95   StringRef ParentData = Parent->getData();
96   StringRef ObjectData;
97   if (Parent->getMagic() == MachO::FAT_MAGIC)
98     ObjectData = ParentData.substr(Header.offset, Header.size);
99   else // Parent->getMagic() == MachO::FAT_MAGIC_64
100     ObjectData = ParentData.substr(Header64.offset, Header64.size);
101   StringRef ObjectName = Parent->getFileName();
102   MemoryBufferRef ObjBuffer(ObjectData, ObjectName);
103   return Archive::create(ObjBuffer);
104 }
105 
106 void MachOUniversalBinary::anchor() { }
107 
108 Expected<std::unique_ptr<MachOUniversalBinary>>
109 MachOUniversalBinary::create(MemoryBufferRef Source) {
110   Error Err = Error::success();
111   std::unique_ptr<MachOUniversalBinary> Ret(
112       new MachOUniversalBinary(Source, Err));
113   if (Err)
114     return std::move(Err);
115   return std::move(Ret);
116 }
117 
118 MachOUniversalBinary::MachOUniversalBinary(MemoryBufferRef Source, Error &Err)
119     : Binary(Binary::ID_MachOUniversalBinary, Source), Magic(0),
120       NumberOfObjects(0) {
121   ErrorAsOutParameter ErrAsOutParam(&Err);
122   if (Data.getBufferSize() < sizeof(MachO::fat_header)) {
123     Err = make_error<GenericBinaryError>("File too small to be a Mach-O "
124                                          "universal file",
125                                          object_error::invalid_file_type);
126     return;
127   }
128   // Check for magic value and sufficient header size.
129   StringRef Buf = getData();
130   MachO::fat_header H =
131       getUniversalBinaryStruct<MachO::fat_header>(Buf.begin());
132   Magic = H.magic;
133   NumberOfObjects = H.nfat_arch;
134   uint32_t MinSize = sizeof(MachO::fat_header);
135   if (Magic == MachO::FAT_MAGIC)
136     MinSize += sizeof(MachO::fat_arch) * NumberOfObjects;
137   else if (Magic == MachO::FAT_MAGIC_64)
138     MinSize += sizeof(MachO::fat_arch_64) * NumberOfObjects;
139   else {
140     Err = malformedError("bad magic number");
141     return;
142   }
143   if (Buf.size() < MinSize) {
144     Err = malformedError("fat_arch" +
145                          Twine(Magic == MachO::FAT_MAGIC ? "" : "_64") +
146                          " structs would extend past the end of the file");
147     return;
148   }
149   Err = Error::success();
150 }
151 
152 Expected<std::unique_ptr<MachOObjectFile>>
153 MachOUniversalBinary::getObjectForArch(StringRef ArchName) const {
154   if (Triple(ArchName).getArch() == Triple::ArchType::UnknownArch)
155     return make_error<GenericBinaryError>("Unknown architecture "
156                                           "named: " +
157                                               ArchName,
158                                           object_error::arch_not_found);
159 
160   for (auto &Obj : objects())
161     if (Obj.getArchTypeName() == ArchName)
162       return Obj.getAsObjectFile();
163   return make_error<GenericBinaryError>("fat file does not "
164                                         "contain " +
165                                             ArchName,
166                                         object_error::arch_not_found);
167 }
168