MachOUniversal.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. //===- MachOUniversal.cpp - Mach-O universal binary -------------*- C++ -*-===//
  2. //
  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
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // This file defines the MachOUniversalBinary class.
  10. //
  11. //===----------------------------------------------------------------------===//
  12. #include "llvm/Object/MachOUniversal.h"
  13. #include "llvm/Object/Archive.h"
  14. #include "llvm/Object/IRObjectFile.h"
  15. #include "llvm/Object/MachO.h"
  16. #include "llvm/Object/ObjectFile.h"
  17. #include "llvm/Support/ErrorHandling.h"
  18. #include "llvm/Support/SwapByteOrder.h"
  19. #include "llvm/Support/type_traits.h"
  20. using namespace llvm;
  21. using namespace object;
  22. static Error
  23. malformedError(Twine Msg) {
  24. std::string StringMsg = "truncated or malformed fat file (" + Msg.str() + ")";
  25. return make_error<GenericBinaryError>(std::move(StringMsg),
  26. object_error::parse_failed);
  27. }
  28. template<typename T>
  29. static T getUniversalBinaryStruct(const char *Ptr) {
  30. T Res;
  31. memcpy(&Res, Ptr, sizeof(T));
  32. // Universal binary headers have big-endian byte order.
  33. if (sys::IsLittleEndianHost)
  34. swapStruct(Res);
  35. return Res;
  36. }
  37. MachOUniversalBinary::ObjectForArch::ObjectForArch(
  38. const MachOUniversalBinary *Parent, uint32_t Index)
  39. : Parent(Parent), Index(Index) {
  40. // The iterators use Parent as a nullptr and an Index+1 == NumberOfObjects.
  41. if (!Parent || Index >= Parent->getNumberOfObjects()) {
  42. clear();
  43. } else {
  44. // Parse object header.
  45. StringRef ParentData = Parent->getData();
  46. if (Parent->getMagic() == MachO::FAT_MAGIC) {
  47. const char *HeaderPos = ParentData.begin() + sizeof(MachO::fat_header) +
  48. Index * sizeof(MachO::fat_arch);
  49. Header = getUniversalBinaryStruct<MachO::fat_arch>(HeaderPos);
  50. } else { // Parent->getMagic() == MachO::FAT_MAGIC_64
  51. const char *HeaderPos = ParentData.begin() + sizeof(MachO::fat_header) +
  52. Index * sizeof(MachO::fat_arch_64);
  53. Header64 = getUniversalBinaryStruct<MachO::fat_arch_64>(HeaderPos);
  54. }
  55. }
  56. }
  57. Expected<std::unique_ptr<MachOObjectFile>>
  58. MachOUniversalBinary::ObjectForArch::getAsObjectFile() const {
  59. if (!Parent)
  60. report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsObjectFile() "
  61. "called when Parent is a nullptr");
  62. StringRef ParentData = Parent->getData();
  63. StringRef ObjectData;
  64. uint32_t cputype;
  65. if (Parent->getMagic() == MachO::FAT_MAGIC) {
  66. ObjectData = ParentData.substr(Header.offset, Header.size);
  67. cputype = Header.cputype;
  68. } else { // Parent->getMagic() == MachO::FAT_MAGIC_64
  69. ObjectData = ParentData.substr(Header64.offset, Header64.size);
  70. cputype = Header64.cputype;
  71. }
  72. StringRef ObjectName = Parent->getFileName();
  73. MemoryBufferRef ObjBuffer(ObjectData, ObjectName);
  74. return ObjectFile::createMachOObjectFile(ObjBuffer, cputype, Index);
  75. }
  76. Expected<std::unique_ptr<IRObjectFile>>
  77. MachOUniversalBinary::ObjectForArch::getAsIRObject(LLVMContext &Ctx) const {
  78. if (!Parent)
  79. report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsIRObject() "
  80. "called when Parent is a nullptr");
  81. StringRef ParentData = Parent->getData();
  82. StringRef ObjectData;
  83. if (Parent->getMagic() == MachO::FAT_MAGIC) {
  84. ObjectData = ParentData.substr(Header.offset, Header.size);
  85. } else { // Parent->getMagic() == MachO::FAT_MAGIC_64
  86. ObjectData = ParentData.substr(Header64.offset, Header64.size);
  87. }
  88. StringRef ObjectName = Parent->getFileName();
  89. MemoryBufferRef ObjBuffer(ObjectData, ObjectName);
  90. return IRObjectFile::create(ObjBuffer, Ctx);
  91. }
  92. Expected<std::unique_ptr<Archive>>
  93. MachOUniversalBinary::ObjectForArch::getAsArchive() const {
  94. if (!Parent)
  95. report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsArchive() "
  96. "called when Parent is a nullptr");
  97. StringRef ParentData = Parent->getData();
  98. StringRef ObjectData;
  99. if (Parent->getMagic() == MachO::FAT_MAGIC)
  100. ObjectData = ParentData.substr(Header.offset, Header.size);
  101. else // Parent->getMagic() == MachO::FAT_MAGIC_64
  102. ObjectData = ParentData.substr(Header64.offset, Header64.size);
  103. StringRef ObjectName = Parent->getFileName();
  104. MemoryBufferRef ObjBuffer(ObjectData, ObjectName);
  105. return Archive::create(ObjBuffer);
  106. }
  107. void MachOUniversalBinary::anchor() { }
  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. MachOUniversalBinary::MachOUniversalBinary(MemoryBufferRef Source, Error &Err)
  118. : Binary(Binary::ID_MachOUniversalBinary, Source), Magic(0),
  119. NumberOfObjects(0) {
  120. ErrorAsOutParameter ErrAsOutParam(&Err);
  121. if (Data.getBufferSize() < sizeof(MachO::fat_header)) {
  122. Err = make_error<GenericBinaryError>("File too small to be a Mach-O "
  123. "universal file",
  124. object_error::invalid_file_type);
  125. return;
  126. }
  127. // Check for magic value and sufficient header size.
  128. StringRef Buf = getData();
  129. MachO::fat_header H =
  130. getUniversalBinaryStruct<MachO::fat_header>(Buf.begin());
  131. Magic = H.magic;
  132. NumberOfObjects = H.nfat_arch;
  133. if (NumberOfObjects == 0) {
  134. Err = malformedError("contains zero architecture types");
  135. return;
  136. }
  137. uint32_t MinSize = sizeof(MachO::fat_header);
  138. if (Magic == MachO::FAT_MAGIC)
  139. MinSize += sizeof(MachO::fat_arch) * NumberOfObjects;
  140. else if (Magic == MachO::FAT_MAGIC_64)
  141. MinSize += sizeof(MachO::fat_arch_64) * NumberOfObjects;
  142. else {
  143. Err = malformedError("bad magic number");
  144. return;
  145. }
  146. if (Buf.size() < MinSize) {
  147. Err = malformedError("fat_arch" +
  148. Twine(Magic == MachO::FAT_MAGIC ? "" : "_64") +
  149. " structs would extend past the end of the file");
  150. return;
  151. }
  152. for (uint32_t i = 0; i < NumberOfObjects; i++) {
  153. ObjectForArch A(this, i);
  154. uint64_t bigSize = A.getOffset();
  155. bigSize += A.getSize();
  156. if (bigSize > Buf.size()) {
  157. Err = malformedError("offset plus size of cputype (" +
  158. Twine(A.getCPUType()) + ") cpusubtype (" +
  159. Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) +
  160. ") extends past the end of the file");
  161. return;
  162. }
  163. if (A.getAlign() > MaxSectionAlignment) {
  164. Err = malformedError("align (2^" + Twine(A.getAlign()) +
  165. ") too large for cputype (" + Twine(A.getCPUType()) +
  166. ") cpusubtype (" +
  167. Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) +
  168. ") (maximum 2^" + Twine(MaxSectionAlignment) + ")");
  169. return;
  170. }
  171. if(A.getOffset() % (1ull << A.getAlign()) != 0){
  172. Err = malformedError("offset: " + Twine(A.getOffset()) +
  173. " for cputype (" + Twine(A.getCPUType()) + ") cpusubtype (" +
  174. Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) +
  175. ") not aligned on it's alignment (2^" + Twine(A.getAlign()) + ")");
  176. return;
  177. }
  178. if (A.getOffset() < MinSize) {
  179. Err = malformedError("cputype (" + Twine(A.getCPUType()) + ") "
  180. "cpusubtype (" + Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) +
  181. ") offset " + Twine(A.getOffset()) + " overlaps universal headers");
  182. return;
  183. }
  184. }
  185. for (uint32_t i = 0; i < NumberOfObjects; i++) {
  186. ObjectForArch A(this, i);
  187. for (uint32_t j = i + 1; j < NumberOfObjects; j++) {
  188. ObjectForArch B(this, j);
  189. if (A.getCPUType() == B.getCPUType() &&
  190. (A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) ==
  191. (B.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK)) {
  192. Err = malformedError("contains two of the same architecture (cputype "
  193. "(" + Twine(A.getCPUType()) + ") cpusubtype (" +
  194. Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) + "))");
  195. return;
  196. }
  197. if ((A.getOffset() >= B.getOffset() &&
  198. A.getOffset() < B.getOffset() + B.getSize()) ||
  199. (A.getOffset() + A.getSize() > B.getOffset() &&
  200. A.getOffset() + A.getSize() < B.getOffset() + B.getSize()) ||
  201. (A.getOffset() <= B.getOffset() &&
  202. A.getOffset() + A.getSize() >= B.getOffset() + B.getSize())) {
  203. Err = malformedError("cputype (" + Twine(A.getCPUType()) + ") "
  204. "cpusubtype (" + Twine(A.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) +
  205. ") at offset " + Twine(A.getOffset()) + " with a size of " +
  206. Twine(A.getSize()) + ", overlaps cputype (" + Twine(B.getCPUType()) +
  207. ") cpusubtype (" + Twine(B.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK)
  208. + ") at offset " + Twine(B.getOffset()) + " with a size of "
  209. + Twine(B.getSize()));
  210. return;
  211. }
  212. }
  213. }
  214. Err = Error::success();
  215. }
  216. Expected<MachOUniversalBinary::ObjectForArch>
  217. MachOUniversalBinary::getObjectForArch(StringRef ArchName) const {
  218. if (Triple(ArchName).getArch() == Triple::ArchType::UnknownArch)
  219. return make_error<GenericBinaryError>("Unknown architecture "
  220. "named: " +
  221. ArchName,
  222. object_error::arch_not_found);
  223. for (const auto &Obj : objects())
  224. if (Obj.getArchFlagName() == ArchName)
  225. return Obj;
  226. return make_error<GenericBinaryError>("fat file does not "
  227. "contain " +
  228. ArchName,
  229. object_error::arch_not_found);
  230. }
  231. Expected<std::unique_ptr<MachOObjectFile>>
  232. MachOUniversalBinary::getMachOObjectForArch(StringRef ArchName) const {
  233. Expected<ObjectForArch> O = getObjectForArch(ArchName);
  234. if (!O)
  235. return O.takeError();
  236. return O->getAsObjectFile();
  237. }
  238. Expected<std::unique_ptr<IRObjectFile>>
  239. MachOUniversalBinary::getIRObjectForArch(StringRef ArchName,
  240. LLVMContext &Ctx) const {
  241. Expected<ObjectForArch> O = getObjectForArch(ArchName);
  242. if (!O)
  243. return O.takeError();
  244. return O->getAsIRObject(Ctx);
  245. }
  246. Expected<std::unique_ptr<Archive>>
  247. MachOUniversalBinary::getArchiveForArch(StringRef ArchName) const {
  248. Expected<ObjectForArch> O = getObjectForArch(ArchName);
  249. if (!O)
  250. return O.takeError();
  251. return O->getAsArchive();
  252. }