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"
22 using namespace object
;
25 malformedError(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
);
32 static T
getUniversalBinaryStruct(const char *Ptr
) {
34 memcpy(&Res
, Ptr
, sizeof(T
));
35 // Universal binary headers have big-endian byte order.
36 if (sys::IsLittleEndianHost
)
41 MachOUniversalBinary::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()) {
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_64
55 const char *HeaderPos
= ParentData
.begin() + sizeof(MachO::fat_header
) +
56 Index
* sizeof(MachO::fat_arch_64
);
57 Header64
= getUniversalBinaryStruct
<MachO::fat_arch_64
>(HeaderPos
);
62 Expected
<std::unique_ptr
<MachOObjectFile
>>
63 MachOUniversalBinary::ObjectForArch::getAsObjectFile() const {
65 report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsObjectFile() "
66 "called when Parent is a nullptr");
68 StringRef ParentData
= Parent
->getData();
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_64
75 ObjectData
= ParentData
.substr(Header64
.offset
, Header64
.size
);
76 cputype
= Header64
.cputype
;
78 StringRef ObjectName
= Parent
->getFileName();
79 MemoryBufferRef
ObjBuffer(ObjectData
, ObjectName
);
80 return ObjectFile::createMachOObjectFile(ObjBuffer
, cputype
, Index
);
83 Expected
<std::unique_ptr
<IRObjectFile
>>
84 MachOUniversalBinary::ObjectForArch::getAsIRObject(LLVMContext
&Ctx
) const {
86 report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsIRObject() "
87 "called when Parent is a nullptr");
89 StringRef ParentData
= Parent
->getData();
91 if (Parent
->getMagic() == MachO::FAT_MAGIC
) {
92 ObjectData
= ParentData
.substr(Header
.offset
, Header
.size
);
93 } else { // Parent->getMagic() == MachO::FAT_MAGIC_64
94 ObjectData
= ParentData
.substr(Header64
.offset
, Header64
.size
);
96 StringRef ObjectName
= Parent
->getFileName();
97 MemoryBufferRef
ObjBuffer(ObjectData
, ObjectName
);
99 return IRObjectFile::create(ObjBuffer
, Ctx
);
102 Expected
<std::unique_ptr
<Archive
>>
103 MachOUniversalBinary::ObjectForArch::getAsArchive() const {
105 report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsArchive() "
106 "called when Parent is a nullptr");
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_64
113 ObjectData
= ParentData
.substr(Header64
.offset
, Header64
.size
);
114 StringRef ObjectName
= Parent
->getFileName();
115 MemoryBufferRef
ObjBuffer(ObjectData
, ObjectName
);
116 return Archive::create(ObjBuffer
);
119 void MachOUniversalBinary::anchor() { }
121 Expected
<std::unique_ptr
<MachOUniversalBinary
>>
122 MachOUniversalBinary::create(MemoryBufferRef Source
) {
123 Error Err
= Error::success();
124 std::unique_ptr
<MachOUniversalBinary
> Ret(
125 new MachOUniversalBinary(Source
, Err
));
127 return std::move(Err
);
128 return std::move(Ret
);
131 MachOUniversalBinary::MachOUniversalBinary(MemoryBufferRef Source
, Error
&Err
)
132 : Binary(Binary::ID_MachOUniversalBinary
, Source
), Magic(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 "
138 object_error::invalid_file_type
);
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());
146 NumberOfObjects
= H
.nfat_arch
;
147 if (NumberOfObjects
== 0) {
148 Err
= malformedError("contains zero architecture types");
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
;
157 Err
= malformedError("bad magic number");
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");
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");
178 if (A
.getAlign() > MaxSectionAlignment
) {
179 Err
= malformedError("align (2^" + Twine(A
.getAlign()) +
180 ") too large for cputype (" + Twine(A
.getCPUType()) +
182 Twine(A
.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK
) +
183 ") (maximum 2^" + Twine(MaxSectionAlignment
) + ")");
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()) + ")");
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");
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
) + "))");
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()));
229 Err
= Error::success();
232 Expected
<MachOUniversalBinary::ObjectForArch
>
233 MachOUniversalBinary::getObjectForArch(StringRef ArchName
) const {
234 if (Triple(ArchName
).getArch() == Triple::ArchType::UnknownArch
)
235 return make_error
<GenericBinaryError
>("Unknown architecture "
238 object_error::arch_not_found
);
239 for (const auto &Obj
: objects())
240 if (Obj
.getArchFlagName() == ArchName
)
242 return make_error
<GenericBinaryError
>("fat file does not "
245 object_error::arch_not_found
);
248 Expected
<std::unique_ptr
<MachOObjectFile
>>
249 MachOUniversalBinary::getMachOObjectForArch(StringRef ArchName
) const {
250 Expected
<ObjectForArch
> O
= getObjectForArch(ArchName
);
252 return O
.takeError();
253 return O
->getAsObjectFile();
256 Expected
<std::unique_ptr
<IRObjectFile
>>
257 MachOUniversalBinary::getIRObjectForArch(StringRef ArchName
,
258 LLVMContext
&Ctx
) const {
259 Expected
<ObjectForArch
> O
= getObjectForArch(ArchName
);
261 return O
.takeError();
262 return O
->getAsIRObject(Ctx
);
265 Expected
<std::unique_ptr
<Archive
>>
266 MachOUniversalBinary::getArchiveForArch(StringRef ArchName
) const {
267 Expected
<ObjectForArch
> O
= getObjectForArch(ArchName
);
269 return O
.takeError();
270 return O
->getAsArchive();