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/MachO.h"
16 #include "llvm/Object/ObjectFile.h"
17 #include "llvm/Support/Casting.h"
18 #include "llvm/Support/Host.h"
19 #include "llvm/Support/MemoryBuffer.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
<Archive
>>
84 MachOUniversalBinary::ObjectForArch::getAsArchive() const {
86 report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsArchive() "
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
);
95 StringRef ObjectName
= Parent
->getFileName();
96 MemoryBufferRef
ObjBuffer(ObjectData
, ObjectName
);
97 return Archive::create(ObjBuffer
);
100 void MachOUniversalBinary::anchor() { }
102 Expected
<std::unique_ptr
<MachOUniversalBinary
>>
103 MachOUniversalBinary::create(MemoryBufferRef Source
) {
104 Error Err
= Error::success();
105 std::unique_ptr
<MachOUniversalBinary
> Ret(
106 new MachOUniversalBinary(Source
, Err
));
108 return std::move(Err
);
109 return std::move(Ret
);
112 MachOUniversalBinary::MachOUniversalBinary(MemoryBufferRef Source
, Error
&Err
)
113 : Binary(Binary::ID_MachOUniversalBinary
, Source
), Magic(0),
115 ErrorAsOutParameter
ErrAsOutParam(&Err
);
116 if (Data
.getBufferSize() < sizeof(MachO::fat_header
)) {
117 Err
= make_error
<GenericBinaryError
>("File too small to be a Mach-O "
119 object_error::invalid_file_type
);
122 // Check for magic value and sufficient header size.
123 StringRef Buf
= getData();
124 MachO::fat_header H
=
125 getUniversalBinaryStruct
<MachO::fat_header
>(Buf
.begin());
127 NumberOfObjects
= H
.nfat_arch
;
128 if (NumberOfObjects
== 0) {
129 Err
= malformedError("contains zero architecture types");
132 uint32_t MinSize
= sizeof(MachO::fat_header
);
133 if (Magic
== MachO::FAT_MAGIC
)
134 MinSize
+= sizeof(MachO::fat_arch
) * NumberOfObjects
;
135 else if (Magic
== MachO::FAT_MAGIC_64
)
136 MinSize
+= sizeof(MachO::fat_arch_64
) * NumberOfObjects
;
138 Err
= malformedError("bad magic number");
141 if (Buf
.size() < MinSize
) {
142 Err
= malformedError("fat_arch" +
143 Twine(Magic
== MachO::FAT_MAGIC
? "" : "_64") +
144 " structs would extend past the end of the file");
147 for (uint32_t i
= 0; i
< NumberOfObjects
; i
++) {
148 ObjectForArch
A(this, i
);
149 uint64_t bigSize
= A
.getOffset();
150 bigSize
+= A
.getSize();
151 if (bigSize
> Buf
.size()) {
152 Err
= malformedError("offset plus size of cputype (" +
153 Twine(A
.getCPUType()) + ") cpusubtype (" +
154 Twine(A
.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK
) +
155 ") extends past the end of the file");
159 if (A
.getAlign() > MaxSectionAlignment
) {
160 Err
= malformedError("align (2^" + Twine(A
.getAlign()) +
161 ") too large for cputype (" + Twine(A
.getCPUType()) +
163 Twine(A
.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK
) +
164 ") (maximum 2^" + Twine(MaxSectionAlignment
) + ")");
167 if(A
.getOffset() % (1 << A
.getAlign()) != 0){
168 Err
= malformedError("offset: " + Twine(A
.getOffset()) +
169 " for cputype (" + Twine(A
.getCPUType()) + ") cpusubtype (" +
170 Twine(A
.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK
) +
171 ") not aligned on it's alignment (2^" + Twine(A
.getAlign()) + ")");
174 if (A
.getOffset() < MinSize
) {
175 Err
= malformedError("cputype (" + Twine(A
.getCPUType()) + ") "
176 "cpusubtype (" + Twine(A
.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK
) +
177 ") offset " + Twine(A
.getOffset()) + " overlaps universal headers");
181 for (uint32_t i
= 0; i
< NumberOfObjects
; i
++) {
182 ObjectForArch
A(this, i
);
183 for (uint32_t j
= i
+ 1; j
< NumberOfObjects
; j
++) {
184 ObjectForArch
B(this, j
);
185 if (A
.getCPUType() == B
.getCPUType() &&
186 (A
.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK
) ==
187 (B
.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK
)) {
188 Err
= malformedError("contains two of the same architecture (cputype "
189 "(" + Twine(A
.getCPUType()) + ") cpusubtype (" +
190 Twine(A
.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK
) + "))");
193 if ((A
.getOffset() >= B
.getOffset() &&
194 A
.getOffset() < B
.getOffset() + B
.getSize()) ||
195 (A
.getOffset() + A
.getSize() > B
.getOffset() &&
196 A
.getOffset() + A
.getSize() < B
.getOffset() + B
.getSize()) ||
197 (A
.getOffset() <= B
.getOffset() &&
198 A
.getOffset() + A
.getSize() >= B
.getOffset() + B
.getSize())) {
199 Err
= malformedError("cputype (" + Twine(A
.getCPUType()) + ") "
200 "cpusubtype (" + Twine(A
.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK
) +
201 ") at offset " + Twine(A
.getOffset()) + " with a size of " +
202 Twine(A
.getSize()) + ", overlaps cputype (" + Twine(B
.getCPUType()) +
203 ") cpusubtype (" + Twine(B
.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK
)
204 + ") at offset " + Twine(B
.getOffset()) + " with a size of "
205 + Twine(B
.getSize()));
210 Err
= Error::success();
213 Expected
<std::unique_ptr
<MachOObjectFile
>>
214 MachOUniversalBinary::getObjectForArch(StringRef ArchName
) const {
215 if (Triple(ArchName
).getArch() == Triple::ArchType::UnknownArch
)
216 return make_error
<GenericBinaryError
>("Unknown architecture "
219 object_error::arch_not_found
);
221 for (auto &Obj
: objects())
222 if (Obj
.getArchFlagName() == ArchName
)
223 return Obj
.getAsObjectFile();
224 return make_error
<GenericBinaryError
>("fat file does not "
227 object_error::arch_not_found
);