1 //===- MachOUniversal.cpp - Mach-O universal binary -------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // This file defines the MachOUniversalBinary class.
11 //===----------------------------------------------------------------------===//
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 #include "llvm/Support/type_traits.h"
23 using namespace object
;
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
);
33 static T
getUniversalBinaryStruct(const char *Ptr
) {
35 memcpy(&Res
, Ptr
, sizeof(T
));
36 // Universal binary headers have big-endian byte order.
37 if (sys::IsLittleEndianHost
)
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()) {
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
);
63 Expected
<std::unique_ptr
<MachOObjectFile
>>
64 MachOUniversalBinary::ObjectForArch::getAsObjectFile() const {
66 report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsObjectFile() "
67 "called when Parent is a nullptr");
69 StringRef ParentData
= Parent
->getData();
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
;
79 StringRef ObjectName
= Parent
->getFileName();
80 MemoryBufferRef
ObjBuffer(ObjectData
, ObjectName
);
81 return ObjectFile::createMachOObjectFile(ObjBuffer
, cputype
, Index
);
84 Expected
<std::unique_ptr
<IRObjectFile
>>
85 MachOUniversalBinary::ObjectForArch::getAsIRObject(LLVMContext
&Ctx
) const {
87 report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsIRObject() "
88 "called when Parent is a nullptr");
90 StringRef ParentData
= Parent
->getData();
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
);
97 StringRef ObjectName
= Parent
->getFileName();
98 MemoryBufferRef
ObjBuffer(ObjectData
, ObjectName
);
100 return IRObjectFile::create(ObjBuffer
, Ctx
);
103 Expected
<std::unique_ptr
<Archive
>>
104 MachOUniversalBinary::ObjectForArch::getAsArchive() const {
106 report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsArchive() "
107 "called when Parent is a nullptr");
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
);
120 void MachOUniversalBinary::anchor() { }
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
));
128 return std::move(Err
);
129 return std::move(Ret
);
132 MachOUniversalBinary::MachOUniversalBinary(MemoryBufferRef Source
, Error
&Err
)
133 : Binary(Binary::ID_MachOUniversalBinary
, Source
), Magic(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 "
139 object_error::invalid_file_type
);
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());
147 NumberOfObjects
= H
.nfat_arch
;
148 if (NumberOfObjects
== 0) {
149 Err
= malformedError("contains zero architecture types");
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
;
158 Err
= malformedError("bad magic number");
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");
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");
179 if (A
.getAlign() > MaxSectionAlignment
) {
180 Err
= malformedError("align (2^" + Twine(A
.getAlign()) +
181 ") too large for cputype (" + Twine(A
.getCPUType()) +
183 Twine(A
.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK
) +
184 ") (maximum 2^" + Twine(MaxSectionAlignment
) + ")");
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()) + ")");
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");
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
) + "))");
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()));
230 Err
= Error::success();
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 "
239 object_error::arch_not_found
);
240 for (const auto &Obj
: objects())
241 if (Obj
.getArchFlagName() == ArchName
)
243 return make_error
<GenericBinaryError
>("fat file does not "
246 object_error::arch_not_found
);
249 Expected
<std::unique_ptr
<MachOObjectFile
>>
250 MachOUniversalBinary::getMachOObjectForArch(StringRef ArchName
) const {
251 Expected
<ObjectForArch
> O
= getObjectForArch(ArchName
);
253 return O
.takeError();
254 return O
->getAsObjectFile();
257 Expected
<std::unique_ptr
<IRObjectFile
>>
258 MachOUniversalBinary::getIRObjectForArch(StringRef ArchName
,
259 LLVMContext
&Ctx
) const {
260 Expected
<ObjectForArch
> O
= getObjectForArch(ArchName
);
262 return O
.takeError();
263 return O
->getAsIRObject(Ctx
);
266 Expected
<std::unique_ptr
<Archive
>>
267 MachOUniversalBinary::getArchiveForArch(StringRef ArchName
) const {
268 Expected
<ObjectForArch
> O
= getObjectForArch(ArchName
);
270 return O
.takeError();
271 return O
->getAsArchive();